• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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