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