~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/wireless/pmsr.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* SPDX-License-Identifier: GPL-2.0 */
  2 /*
  3  * Copyright (C) 2018 - 2021, 2023 - 2024 Intel Corporation
  4  */
  5 #include <net/cfg80211.h>
  6 #include "core.h"
  7 #include "nl80211.h"
  8 #include "rdev-ops.h"
  9 
 10 static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
 11                           struct nlattr *ftmreq,
 12                           struct cfg80211_pmsr_request_peer *out,
 13                           struct genl_info *info)
 14 {
 15         const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa;
 16         struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1];
 17         u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */
 18 
 19         /* validate existing data */
 20         if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) {
 21                 NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth");
 22                 return -EINVAL;
 23         }
 24 
 25         /* no validation needed - was already done via nested policy */
 26         nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq,
 27                                     NULL, NULL);
 28 
 29         if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE])
 30                 preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]);
 31 
 32         /* set up values - struct is 0-initialized */
 33         out->ftm.requested = true;
 34 
 35         switch (out->chandef.chan->band) {
 36         case NL80211_BAND_60GHZ:
 37                 /* optional */
 38                 break;
 39         default:
 40                 if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) {
 41                         NL_SET_ERR_MSG(info->extack,
 42                                        "FTM: must specify preamble");
 43                         return -EINVAL;
 44                 }
 45         }
 46 
 47         if (!(capa->ftm.preambles & BIT(preamble))) {
 48                 NL_SET_ERR_MSG_ATTR(info->extack,
 49                                     tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
 50                                     "FTM: invalid preamble");
 51                 return -EINVAL;
 52         }
 53 
 54         out->ftm.preamble = preamble;
 55 
 56         out->ftm.burst_period = 0;
 57         if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD])
 58                 out->ftm.burst_period =
 59                         nla_get_u16(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]);
 60 
 61         out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP];
 62         if (out->ftm.asap && !capa->ftm.asap) {
 63                 NL_SET_ERR_MSG_ATTR(info->extack,
 64                                     tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP],
 65                                     "FTM: ASAP mode not supported");
 66                 return -EINVAL;
 67         }
 68 
 69         if (!out->ftm.asap && !capa->ftm.non_asap) {
 70                 NL_SET_ERR_MSG(info->extack,
 71                                "FTM: non-ASAP mode not supported");
 72                 return -EINVAL;
 73         }
 74 
 75         out->ftm.num_bursts_exp = 0;
 76         if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP])
 77                 out->ftm.num_bursts_exp =
 78                         nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]);
 79 
 80         if (capa->ftm.max_bursts_exponent >= 0 &&
 81             out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) {
 82                 NL_SET_ERR_MSG_ATTR(info->extack,
 83                                     tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP],
 84                                     "FTM: max NUM_BURSTS_EXP must be set lower than the device limit");
 85                 return -EINVAL;
 86         }
 87 
 88         out->ftm.burst_duration = 15;
 89         if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION])
 90                 out->ftm.burst_duration =
 91                         nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]);
 92 
 93         out->ftm.ftms_per_burst = 0;
 94         if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST])
 95                 out->ftm.ftms_per_burst =
 96                         nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]);
 97 
 98         if (capa->ftm.max_ftms_per_burst &&
 99             (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst ||
100              out->ftm.ftms_per_burst == 0)) {
101                 NL_SET_ERR_MSG_ATTR(info->extack,
102                                     tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
103                                     "FTM: FTMs per burst must be set lower than the device limit but non-zero");
104                 return -EINVAL;
105         }
106 
107         out->ftm.ftmr_retries = 3;
108         if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES])
109                 out->ftm.ftmr_retries =
110                         nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]);
111 
112         out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI];
113         if (out->ftm.request_lci && !capa->ftm.request_lci) {
114                 NL_SET_ERR_MSG_ATTR(info->extack,
115                                     tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
116                                     "FTM: LCI request not supported");
117         }
118 
119         out->ftm.request_civicloc =
120                 !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC];
121         if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) {
122                 NL_SET_ERR_MSG_ATTR(info->extack,
123                                     tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
124                             "FTM: civic location request not supported");
125         }
126 
127         out->ftm.trigger_based =
128                 !!tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED];
129         if (out->ftm.trigger_based && !capa->ftm.trigger_based) {
130                 NL_SET_ERR_MSG_ATTR(info->extack,
131                                     tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED],
132                                     "FTM: trigger based ranging is not supported");
133                 return -EINVAL;
134         }
135 
136         out->ftm.non_trigger_based =
137                 !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED];
138         if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) {
139                 NL_SET_ERR_MSG_ATTR(info->extack,
140                                     tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED],
141                                     "FTM: trigger based ranging is not supported");
142                 return -EINVAL;
143         }
144 
145         if (out->ftm.trigger_based && out->ftm.non_trigger_based) {
146                 NL_SET_ERR_MSG(info->extack,
147                                "FTM: can't set both trigger based and non trigger based");
148                 return -EINVAL;
149         }
150 
151         if (out->ftm.ftms_per_burst > 31 && !out->ftm.non_trigger_based &&
152             !out->ftm.trigger_based) {
153                 NL_SET_ERR_MSG_ATTR(info->extack,
154                                     tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
155                                     "FTM: FTMs per burst must be set lower than 31");
156                 return -ERANGE;
157         }
158 
159         if ((out->ftm.trigger_based || out->ftm.non_trigger_based) &&
160             out->ftm.preamble != NL80211_PREAMBLE_HE) {
161                 NL_SET_ERR_MSG_ATTR(info->extack,
162                                     tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
163                                     "FTM: non EDCA based ranging must use HE preamble");
164                 return -EINVAL;
165         }
166 
167         out->ftm.lmr_feedback =
168                 !!tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK];
169         if (!out->ftm.trigger_based && !out->ftm.non_trigger_based &&
170             out->ftm.lmr_feedback) {
171                 NL_SET_ERR_MSG_ATTR(info->extack,
172                                     tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK],
173                                     "FTM: LMR feedback set for EDCA based ranging");
174                 return -EINVAL;
175         }
176 
177         if (tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]) {
178                 if (!out->ftm.non_trigger_based && !out->ftm.trigger_based) {
179                         NL_SET_ERR_MSG_ATTR(info->extack,
180                                             tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR],
181                                             "FTM: BSS color set for EDCA based ranging");
182                         return -EINVAL;
183                 }
184 
185                 out->ftm.bss_color =
186                         nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]);
187         }
188 
189         return 0;
190 }
191 
192 static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
193                            struct nlattr *peer,
194                            struct cfg80211_pmsr_request_peer *out,
195                            struct genl_info *info)
196 {
197         struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
198         struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
199         struct nlattr *treq;
200         int err, rem;
201 
202         /* no validation needed - was already done via nested policy */
203         nla_parse_nested_deprecated(tb, NL80211_PMSR_PEER_ATTR_MAX, peer,
204                                     NULL, NULL);
205 
206         if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] ||
207             !tb[NL80211_PMSR_PEER_ATTR_CHAN] ||
208             !tb[NL80211_PMSR_PEER_ATTR_REQ]) {
209                 NL_SET_ERR_MSG_ATTR(info->extack, peer,
210                                     "insufficient peer data");
211                 return -EINVAL;
212         }
213 
214         memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
215 
216         /* reuse info->attrs */
217         memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
218         err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX,
219                                           tb[NL80211_PMSR_PEER_ATTR_CHAN],
220                                           NULL, info->extack);
221         if (err)
222                 return err;
223 
224         err = nl80211_parse_chandef(rdev, info, &out->chandef);
225         if (err)
226                 return err;
227 
228         /* no validation needed - was already done via nested policy */
229         nla_parse_nested_deprecated(req, NL80211_PMSR_REQ_ATTR_MAX,
230                                     tb[NL80211_PMSR_PEER_ATTR_REQ], NULL,
231                                     NULL);
232 
233         if (!req[NL80211_PMSR_REQ_ATTR_DATA]) {
234                 NL_SET_ERR_MSG_ATTR(info->extack,
235                                     tb[NL80211_PMSR_PEER_ATTR_REQ],
236                                     "missing request type/data");
237                 return -EINVAL;
238         }
239 
240         if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF])
241                 out->report_ap_tsf = true;
242 
243         if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) {
244                 NL_SET_ERR_MSG_ATTR(info->extack,
245                                     req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF],
246                                     "reporting AP TSF is not supported");
247                 return -EINVAL;
248         }
249 
250         nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
251                 switch (nla_type(treq)) {
252                 case NL80211_PMSR_TYPE_FTM:
253                         err = pmsr_parse_ftm(rdev, treq, out, info);
254                         break;
255                 default:
256                         NL_SET_ERR_MSG_ATTR(info->extack, treq,
257                                             "unsupported measurement type");
258                         err = -EINVAL;
259                 }
260         }
261 
262         if (err)
263                 return err;
264 
265         return 0;
266 }
267 
268 int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
269 {
270         struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS];
271         struct cfg80211_registered_device *rdev = info->user_ptr[0];
272         struct wireless_dev *wdev = info->user_ptr[1];
273         struct cfg80211_pmsr_request *req;
274         struct nlattr *peers, *peer;
275         int count, rem, err, idx;
276 
277         if (!rdev->wiphy.pmsr_capa)
278                 return -EOPNOTSUPP;
279 
280         if (!reqattr)
281                 return -EINVAL;
282 
283         peers = nla_find(nla_data(reqattr), nla_len(reqattr),
284                          NL80211_PMSR_ATTR_PEERS);
285         if (!peers)
286                 return -EINVAL;
287 
288         count = 0;
289         nla_for_each_nested(peer, peers, rem) {
290                 count++;
291 
292                 if (count > rdev->wiphy.pmsr_capa->max_peers) {
293                         NL_SET_ERR_MSG_ATTR(info->extack, peer,
294                                             "Too many peers used");
295                         return -EINVAL;
296                 }
297         }
298 
299         req = kzalloc(struct_size(req, peers, count), GFP_KERNEL);
300         if (!req)
301                 return -ENOMEM;
302         req->n_peers = count;
303 
304         if (info->attrs[NL80211_ATTR_TIMEOUT])
305                 req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
306 
307         if (info->attrs[NL80211_ATTR_MAC]) {
308                 if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) {
309                         NL_SET_ERR_MSG_ATTR(info->extack,
310                                             info->attrs[NL80211_ATTR_MAC],
311                                             "device cannot randomize MAC address");
312                         err = -EINVAL;
313                         goto out_err;
314                 }
315 
316                 err = nl80211_parse_random_mac(info->attrs, req->mac_addr,
317                                                req->mac_addr_mask);
318                 if (err)
319                         goto out_err;
320         } else {
321                 memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN);
322                 eth_broadcast_addr(req->mac_addr_mask);
323         }
324 
325         idx = 0;
326         nla_for_each_nested(peer, peers, rem) {
327                 /* NB: this reuses info->attrs, but we no longer need it */
328                 err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info);
329                 if (err)
330                         goto out_err;
331                 idx++;
332         }
333         req->cookie = cfg80211_assign_cookie(rdev);
334         req->nl_portid = info->snd_portid;
335 
336         err = rdev_start_pmsr(rdev, wdev, req);
337         if (err)
338                 goto out_err;
339 
340         list_add_tail(&req->list, &wdev->pmsr_list);
341 
342         nl_set_extack_cookie_u64(info->extack, req->cookie);
343         return 0;
344 out_err:
345         kfree(req);
346         return err;
347 }
348 
349 void cfg80211_pmsr_complete(struct wireless_dev *wdev,
350                             struct cfg80211_pmsr_request *req,
351                             gfp_t gfp)
352 {
353         struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
354         struct cfg80211_pmsr_request *tmp, *prev, *to_free = NULL;
355         struct sk_buff *msg;
356         void *hdr;
357 
358         trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie);
359 
360         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
361         if (!msg)
362                 goto free_request;
363 
364         hdr = nl80211hdr_put(msg, 0, 0, 0,
365                              NL80211_CMD_PEER_MEASUREMENT_COMPLETE);
366         if (!hdr)
367                 goto free_msg;
368 
369         if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
370             nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
371                               NL80211_ATTR_PAD))
372                 goto free_msg;
373 
374         if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
375                               NL80211_ATTR_PAD))
376                 goto free_msg;
377 
378         genlmsg_end(msg, hdr);
379         genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
380         goto free_request;
381 free_msg:
382         nlmsg_free(msg);
383 free_request:
384         spin_lock_bh(&wdev->pmsr_lock);
385         /*
386          * cfg80211_pmsr_process_abort() may have already moved this request
387          * to the free list, and will free it later. In this case, don't free
388          * it here.
389          */
390         list_for_each_entry_safe(tmp, prev, &wdev->pmsr_list, list) {
391                 if (tmp == req) {
392                         list_del(&req->list);
393                         to_free = req;
394                         break;
395                 }
396         }
397         spin_unlock_bh(&wdev->pmsr_lock);
398         kfree(to_free);
399 }
400 EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete);
401 
402 static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
403                                      struct cfg80211_pmsr_result *res)
404 {
405         if (res->status == NL80211_PMSR_STATUS_FAILURE) {
406                 if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
407                                 res->ftm.failure_reason))
408                         goto error;
409 
410                 if (res->ftm.failure_reason ==
411                         NL80211_PMSR_FTM_FAILURE_PEER_BUSY &&
412                     res->ftm.busy_retry_time &&
413                     nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
414                                 res->ftm.busy_retry_time))
415                         goto error;
416 
417                 return 0;
418         }
419 
420 #define PUT(tp, attr, val)                                              \
421         do {                                                            \
422                 if (nla_put_##tp(msg,                                   \
423                                  NL80211_PMSR_FTM_RESP_ATTR_##attr,     \
424                                  res->ftm.val))                         \
425                         goto error;                                     \
426         } while (0)
427 
428 #define PUTOPT(tp, attr, val)                                           \
429         do {                                                            \
430                 if (res->ftm.val##_valid)                               \
431                         PUT(tp, attr, val);                             \
432         } while (0)
433 
434 #define PUT_U64(attr, val)                                              \
435         do {                                                            \
436                 if (nla_put_u64_64bit(msg,                              \
437                                       NL80211_PMSR_FTM_RESP_ATTR_##attr,\
438                                       res->ftm.val,                     \
439                                       NL80211_PMSR_FTM_RESP_ATTR_PAD))  \
440                         goto error;                                     \
441         } while (0)
442 
443 #define PUTOPT_U64(attr, val)                                           \
444         do {                                                            \
445                 if (res->ftm.val##_valid)                               \
446                         PUT_U64(attr, val);                             \
447         } while (0)
448 
449         if (res->ftm.burst_index >= 0)
450                 PUT(u32, BURST_INDEX, burst_index);
451         PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts);
452         PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes);
453         PUT(u8, NUM_BURSTS_EXP, num_bursts_exp);
454         PUT(u8, BURST_DURATION, burst_duration);
455         PUT(u8, FTMS_PER_BURST, ftms_per_burst);
456         PUTOPT(s32, RSSI_AVG, rssi_avg);
457         PUTOPT(s32, RSSI_SPREAD, rssi_spread);
458         if (res->ftm.tx_rate_valid &&
459             !nl80211_put_sta_rate(msg, &res->ftm.tx_rate,
460                                   NL80211_PMSR_FTM_RESP_ATTR_TX_RATE))
461                 goto error;
462         if (res->ftm.rx_rate_valid &&
463             !nl80211_put_sta_rate(msg, &res->ftm.rx_rate,
464                                   NL80211_PMSR_FTM_RESP_ATTR_RX_RATE))
465                 goto error;
466         PUTOPT_U64(RTT_AVG, rtt_avg);
467         PUTOPT_U64(RTT_VARIANCE, rtt_variance);
468         PUTOPT_U64(RTT_SPREAD, rtt_spread);
469         PUTOPT_U64(DIST_AVG, dist_avg);
470         PUTOPT_U64(DIST_VARIANCE, dist_variance);
471         PUTOPT_U64(DIST_SPREAD, dist_spread);
472         if (res->ftm.lci && res->ftm.lci_len &&
473             nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI,
474                     res->ftm.lci_len, res->ftm.lci))
475                 goto error;
476         if (res->ftm.civicloc && res->ftm.civicloc_len &&
477             nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
478                     res->ftm.civicloc_len, res->ftm.civicloc))
479                 goto error;
480 #undef PUT
481 #undef PUTOPT
482 #undef PUT_U64
483 #undef PUTOPT_U64
484 
485         return 0;
486 error:
487         return -ENOSPC;
488 }
489 
490 static int nl80211_pmsr_send_result(struct sk_buff *msg,
491                                     struct cfg80211_pmsr_result *res)
492 {
493         struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata;
494 
495         pmsr = nla_nest_start_noflag(msg, NL80211_ATTR_PEER_MEASUREMENTS);
496         if (!pmsr)
497                 goto error;
498 
499         peers = nla_nest_start_noflag(msg, NL80211_PMSR_ATTR_PEERS);
500         if (!peers)
501                 goto error;
502 
503         peer = nla_nest_start_noflag(msg, 1);
504         if (!peer)
505                 goto error;
506 
507         if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr))
508                 goto error;
509 
510         resp = nla_nest_start_noflag(msg, NL80211_PMSR_PEER_ATTR_RESP);
511         if (!resp)
512                 goto error;
513 
514         if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) ||
515             nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME,
516                               res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
517                 goto error;
518 
519         if (res->ap_tsf_valid &&
520             nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF,
521                               res->ap_tsf, NL80211_PMSR_RESP_ATTR_PAD))
522                 goto error;
523 
524         if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL))
525                 goto error;
526 
527         data = nla_nest_start_noflag(msg, NL80211_PMSR_RESP_ATTR_DATA);
528         if (!data)
529                 goto error;
530 
531         typedata = nla_nest_start_noflag(msg, res->type);
532         if (!typedata)
533                 goto error;
534 
535         switch (res->type) {
536         case NL80211_PMSR_TYPE_FTM:
537                 if (nl80211_pmsr_send_ftm_res(msg, res))
538                         goto error;
539                 break;
540         default:
541                 WARN_ON(1);
542         }
543 
544         nla_nest_end(msg, typedata);
545         nla_nest_end(msg, data);
546         nla_nest_end(msg, resp);
547         nla_nest_end(msg, peer);
548         nla_nest_end(msg, peers);
549         nla_nest_end(msg, pmsr);
550 
551         return 0;
552 error:
553         return -ENOSPC;
554 }
555 
556 void cfg80211_pmsr_report(struct wireless_dev *wdev,
557                           struct cfg80211_pmsr_request *req,
558                           struct cfg80211_pmsr_result *result,
559                           gfp_t gfp)
560 {
561         struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
562         struct sk_buff *msg;
563         void *hdr;
564         int err;
565 
566         trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie,
567                                    result->addr);
568 
569         /*
570          * Currently, only variable items are LCI and civic location,
571          * both of which are reasonably short so we don't need to
572          * worry about them here for the allocation.
573          */
574         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
575         if (!msg)
576                 return;
577 
578         hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT);
579         if (!hdr)
580                 goto free;
581 
582         if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
583             nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
584                               NL80211_ATTR_PAD))
585                 goto free;
586 
587         if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
588                               NL80211_ATTR_PAD))
589                 goto free;
590 
591         err = nl80211_pmsr_send_result(msg, result);
592         if (err) {
593                 pr_err_ratelimited("peer measurement result: message didn't fit!");
594                 goto free;
595         }
596 
597         genlmsg_end(msg, hdr);
598         genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
599         return;
600 free:
601         nlmsg_free(msg);
602 }
603 EXPORT_SYMBOL_GPL(cfg80211_pmsr_report);
604 
605 static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
606 {
607         struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
608         struct cfg80211_pmsr_request *req, *tmp;
609         LIST_HEAD(free_list);
610 
611         lockdep_assert_wiphy(wdev->wiphy);
612 
613         spin_lock_bh(&wdev->pmsr_lock);
614         list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) {
615                 if (req->nl_portid)
616                         continue;
617                 list_move_tail(&req->list, &free_list);
618         }
619         spin_unlock_bh(&wdev->pmsr_lock);
620 
621         list_for_each_entry_safe(req, tmp, &free_list, list) {
622                 rdev_abort_pmsr(rdev, wdev, req);
623 
624                 kfree(req);
625         }
626 }
627 
628 void cfg80211_pmsr_free_wk(struct work_struct *work)
629 {
630         struct wireless_dev *wdev = container_of(work, struct wireless_dev,
631                                                  pmsr_free_wk);
632 
633         wiphy_lock(wdev->wiphy);
634         cfg80211_pmsr_process_abort(wdev);
635         wiphy_unlock(wdev->wiphy);
636 }
637 
638 void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
639 {
640         struct cfg80211_pmsr_request *req;
641         bool found = false;
642 
643         spin_lock_bh(&wdev->pmsr_lock);
644         list_for_each_entry(req, &wdev->pmsr_list, list) {
645                 found = true;
646                 req->nl_portid = 0;
647         }
648         spin_unlock_bh(&wdev->pmsr_lock);
649 
650         if (found)
651                 cfg80211_pmsr_process_abort(wdev);
652 
653         WARN_ON(!list_empty(&wdev->pmsr_list));
654 }
655 
656 void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
657 {
658         struct cfg80211_pmsr_request *req;
659 
660         spin_lock_bh(&wdev->pmsr_lock);
661         list_for_each_entry(req, &wdev->pmsr_list, list) {
662                 if (req->nl_portid == portid) {
663                         req->nl_portid = 0;
664                         schedule_work(&wdev->pmsr_free_wk);
665                 }
666         }
667         spin_unlock_bh(&wdev->pmsr_lock);
668 }
669 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php