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