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