1 /******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
9 * Copyright (C) 2018 - 2020 Intel Corporation
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * The full GNU General Public License is included in this distribution
21 * in the file called COPYING.
22 *
23 * Contact Information:
24 * Intel Linux Wireless <linuxwifi@intel.com>
25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
26 *
27 * BSD LICENSE
28 *
29 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
30 * Copyright (C) 2018 - 2020 Intel Corporation
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 *
37 * * Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * * Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in
41 * the documentation and/or other materials provided with the
42 * distribution.
43 * * Neither the name Intel Corporation nor the names of its
44 * contributors may be used to endorse or promote products derived
45 * from this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 *
59 *****************************************************************************/
60 #include <net/cfg80211.h>
61 #include <linux/etherdevice.h>
62 #include "mvm.h"
63 #include "constants.h"
64
65 struct iwl_mvm_pasn_sta {
66 struct list_head list;
67 struct iwl_mvm_int_sta int_sta;
68 u8 addr[ETH_ALEN];
69 };
70
71 struct iwl_mvm_pasn_hltk_data {
72 u8 *addr;
73 u8 cipher;
74 u8 *hltk;
75 };
76
iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def * chandef,u8 * bw,u8 * ctrl_ch_position)77 static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
78 u8 *bw, u8 *ctrl_ch_position)
79 {
80 switch (chandef->width) {
81 case NL80211_CHAN_WIDTH_20_NOHT:
82 *bw = IWL_TOF_BW_20_LEGACY;
83 break;
84 case NL80211_CHAN_WIDTH_20:
85 *bw = IWL_TOF_BW_20_HT;
86 break;
87 case NL80211_CHAN_WIDTH_40:
88 *bw = IWL_TOF_BW_40;
89 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
90 break;
91 case NL80211_CHAN_WIDTH_80:
92 *bw = IWL_TOF_BW_80;
93 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
94 break;
95 default:
96 return -ENOTSUPP;
97 }
98
99 return 0;
100 }
101
iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def * chandef,u8 * format_bw,u8 * ctrl_ch_position)102 static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
103 u8 *format_bw,
104 u8 *ctrl_ch_position)
105 {
106 switch (chandef->width) {
107 case NL80211_CHAN_WIDTH_20_NOHT:
108 *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
109 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
110 break;
111 case NL80211_CHAN_WIDTH_20:
112 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
113 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
114 break;
115 case NL80211_CHAN_WIDTH_40:
116 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
117 *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
118 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
119 break;
120 case NL80211_CHAN_WIDTH_80:
121 *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
122 *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
123 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
124 break;
125 default:
126 return -ENOTSUPP;
127 }
128
129 return 0;
130 }
131
132 static int
iwl_mvm_ftm_responder_cmd(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_chan_def * chandef)133 iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
134 struct ieee80211_vif *vif,
135 struct cfg80211_chan_def *chandef)
136 {
137 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
138 /*
139 * The command structure is the same for versions 6 and 7, (only the
140 * field interpretation is different), so the same struct can be use
141 * for all cases.
142 */
143 struct iwl_tof_responder_config_cmd cmd = {
144 .channel_num = chandef->chan->hw_value,
145 .cmd_valid_fields =
146 cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
147 IWL_TOF_RESPONDER_CMD_VALID_BSSID |
148 IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
149 .sta_id = mvmvif->bcast_sta.sta_id,
150 };
151 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
152 TOF_RESPONDER_CONFIG_CMD, 6);
153 int err;
154
155 lockdep_assert_held(&mvm->mutex);
156
157 if (cmd_ver == 7)
158 err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
159 &cmd.ctrl_ch_position);
160 else
161 err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
162 &cmd.ctrl_ch_position);
163
164 if (err) {
165 IWL_ERR(mvm, "Failed to set responder bandwidth\n");
166 return err;
167 }
168
169 memcpy(cmd.bssid, vif->addr, ETH_ALEN);
170
171 return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD,
172 LOCATION_GROUP, 0),
173 0, sizeof(cmd), &cmd);
174 }
175
176 static int
iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_ftm_responder_params * params)177 iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm,
178 struct ieee80211_vif *vif,
179 struct ieee80211_ftm_responder_params *params)
180 {
181 struct iwl_tof_responder_dyn_config_cmd_v2 cmd = {
182 .lci_len = cpu_to_le32(params->lci_len + 2),
183 .civic_len = cpu_to_le32(params->civicloc_len + 2),
184 };
185 u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0};
186 struct iwl_host_cmd hcmd = {
187 .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
188 LOCATION_GROUP, 0),
189 .data[0] = &cmd,
190 .len[0] = sizeof(cmd),
191 .data[1] = &data,
192 /* .len[1] set later */
193 /* may not be able to DMA from stack */
194 .dataflags[1] = IWL_HCMD_DFL_DUP,
195 };
196 u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
197 u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
198 u8 *pos = data;
199
200 lockdep_assert_held(&mvm->mutex);
201
202 if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
203 IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
204 params->lci_len, params->civicloc_len);
205 return -ENOBUFS;
206 }
207
208 pos[0] = WLAN_EID_MEASURE_REPORT;
209 pos[1] = params->lci_len;
210 memcpy(pos + 2, params->lci, params->lci_len);
211
212 pos += aligned_lci_len;
213 pos[0] = WLAN_EID_MEASURE_REPORT;
214 pos[1] = params->civicloc_len;
215 memcpy(pos + 2, params->civicloc, params->civicloc_len);
216
217 hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
218
219 return iwl_mvm_send_cmd(mvm, &hcmd);
220 }
221
222 static int
iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_ftm_responder_params * params,struct iwl_mvm_pasn_hltk_data * hltk_data)223 iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm,
224 struct ieee80211_vif *vif,
225 struct ieee80211_ftm_responder_params *params,
226 struct iwl_mvm_pasn_hltk_data *hltk_data)
227 {
228 struct iwl_tof_responder_dyn_config_cmd cmd;
229 struct iwl_host_cmd hcmd = {
230 .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
231 LOCATION_GROUP, 0),
232 .data[0] = &cmd,
233 .len[0] = sizeof(cmd),
234 /* may not be able to DMA from stack */
235 .dataflags[0] = IWL_HCMD_DFL_DUP,
236 };
237
238 lockdep_assert_held(&mvm->mutex);
239
240 cmd.valid_flags = 0;
241
242 if (params) {
243 if (params->lci_len + 2 > sizeof(cmd.lci_buf) ||
244 params->civicloc_len + 2 > sizeof(cmd.civic_buf)) {
245 IWL_ERR(mvm,
246 "LCI/civic data too big (lci=%zd, civic=%zd)\n",
247 params->lci_len, params->civicloc_len);
248 return -ENOBUFS;
249 }
250
251 cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT;
252 cmd.lci_buf[1] = params->lci_len;
253 memcpy(cmd.lci_buf + 2, params->lci, params->lci_len);
254 cmd.lci_len = params->lci_len + 2;
255
256 cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT;
257 cmd.civic_buf[1] = params->civicloc_len;
258 memcpy(cmd.civic_buf + 2, params->civicloc,
259 params->civicloc_len);
260 cmd.civic_len = params->civicloc_len + 2;
261
262 cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI |
263 IWL_RESPONDER_DYN_CFG_VALID_CIVIC;
264 }
265
266 if (hltk_data) {
267 if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) {
268 IWL_ERR(mvm, "invalid cipher: %u\n",
269 hltk_data->cipher);
270 return -EINVAL;
271 }
272
273 cmd.cipher = hltk_data->cipher;
274 memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr));
275 memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf));
276 cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA;
277 }
278
279 return iwl_mvm_send_cmd(mvm, &hcmd);
280 }
281
282 static int
iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_ftm_responder_params * params)283 iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
284 struct ieee80211_vif *vif,
285 struct ieee80211_ftm_responder_params *params)
286 {
287 int ret;
288 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
289 TOF_RESPONDER_DYN_CONFIG_CMD, 2);
290
291 switch (cmd_ver) {
292 case 2:
293 ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif,
294 params);
295 break;
296 case 3:
297 ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif,
298 params, NULL);
299 break;
300 default:
301 IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n",
302 cmd_ver);
303 ret = -ENOTSUPP;
304 }
305
306 return ret;
307 }
308
iwl_mvm_resp_del_pasn_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_mvm_pasn_sta * sta)309 static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
310 struct ieee80211_vif *vif,
311 struct iwl_mvm_pasn_sta *sta)
312 {
313 list_del(&sta->list);
314 iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
315 iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
316 kfree(sta);
317 }
318
iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u8 * addr,u32 cipher,u8 * tk,u32 tk_len,u8 * hltk,u32 hltk_len)319 int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
320 struct ieee80211_vif *vif,
321 u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
322 u8 *hltk, u32 hltk_len)
323 {
324 int ret;
325 struct iwl_mvm_pasn_sta *sta = NULL;
326 struct iwl_mvm_pasn_hltk_data hltk_data = {
327 .addr = addr,
328 .hltk = hltk,
329 };
330 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
331 TOF_RESPONDER_DYN_CONFIG_CMD, 2);
332
333 lockdep_assert_held(&mvm->mutex);
334
335 if (cmd_ver < 3) {
336 IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
337 return -ENOTSUPP;
338 }
339
340 hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
341 if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
342 IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
343 return -EINVAL;
344 }
345
346 if (tk && tk_len) {
347 sta = kzalloc(sizeof(*sta), GFP_KERNEL);
348 if (!sta)
349 return -ENOBUFS;
350
351 ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
352 cipher, tk, tk_len);
353 if (ret) {
354 kfree(sta);
355 return ret;
356 }
357
358 memcpy(sta->addr, addr, ETH_ALEN);
359 list_add_tail(&sta->list, &mvm->resp_pasn_list);
360 }
361
362 ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, &hltk_data);
363 if (ret && sta)
364 iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
365
366 return ret;
367 }
368
iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u8 * addr)369 int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
370 struct ieee80211_vif *vif, u8 *addr)
371 {
372 struct iwl_mvm_pasn_sta *sta, *prev;
373
374 lockdep_assert_held(&mvm->mutex);
375
376 list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
377 if (!memcmp(sta->addr, addr, ETH_ALEN)) {
378 iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
379 return 0;
380 }
381 }
382
383 IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
384 return -EINVAL;
385 }
386
iwl_mvm_ftm_start_responder(struct iwl_mvm * mvm,struct ieee80211_vif * vif)387 int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
388 {
389 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
390 struct ieee80211_ftm_responder_params *params;
391 struct ieee80211_chanctx_conf ctx, *pctx;
392 u16 *phy_ctxt_id;
393 struct iwl_mvm_phy_ctxt *phy_ctxt;
394 int ret;
395
396 params = vif->bss_conf.ftmr_params;
397
398 lockdep_assert_held(&mvm->mutex);
399
400 if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder))
401 return -EINVAL;
402
403 if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
404 !mvmvif->ap_ibss_active) {
405 IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
406 return -EIO;
407 }
408
409 rcu_read_lock();
410 pctx = rcu_dereference(vif->chanctx_conf);
411 /* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care
412 * about changes in the ctx after releasing the lock because the driver
413 * is still protected by the mutex. */
414 ctx = *pctx;
415 phy_ctxt_id = (u16 *)pctx->drv_priv;
416 rcu_read_unlock();
417
418 phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
419 ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def,
420 ctx.rx_chains_static,
421 ctx.rx_chains_dynamic);
422 if (ret)
423 return ret;
424
425 ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def);
426 if (ret)
427 return ret;
428
429 if (params)
430 ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
431
432 return ret;
433 }
434
iwl_mvm_ftm_responder_clear(struct iwl_mvm * mvm,struct ieee80211_vif * vif)435 void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
436 struct ieee80211_vif *vif)
437 {
438 struct iwl_mvm_pasn_sta *sta, *prev;
439
440 lockdep_assert_held(&mvm->mutex);
441
442 list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
443 iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
444 }
445
iwl_mvm_ftm_restart_responder(struct iwl_mvm * mvm,struct ieee80211_vif * vif)446 void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
447 struct ieee80211_vif *vif)
448 {
449 if (!vif->bss_conf.ftm_responder)
450 return;
451
452 iwl_mvm_ftm_responder_clear(mvm, vif);
453 iwl_mvm_ftm_start_responder(mvm, vif);
454 }
455
iwl_mvm_ftm_responder_stats(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)456 void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
457 struct iwl_rx_cmd_buffer *rxb)
458 {
459 struct iwl_rx_packet *pkt = rxb_addr(rxb);
460 struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
461 struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
462 u32 flags = le32_to_cpu(resp->flags);
463
464 if (resp->success_ftm == resp->ftm_per_burst)
465 stats->success_num++;
466 else if (resp->success_ftm >= 2)
467 stats->partial_num++;
468 else
469 stats->failed_num++;
470
471 if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
472 (flags & FTM_RESP_STAT_ASAP_RESP))
473 stats->asap_num++;
474
475 if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
476 stats->non_asap_num++;
477
478 stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
479
480 if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
481 stats->unknown_triggers_num++;
482
483 if (flags & FTM_RESP_STAT_DUP)
484 stats->reschedule_requests_num++;
485
486 if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
487 stats->out_of_window_triggers_num++;
488 }
489