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