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) 2012 - 2014 Intel Corporation. All rights reserved.
9  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
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  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
23  * USA
24  *
25  * The full GNU General Public License is included in this distribution
26  * in the file called COPYING.
27  *
28  * Contact Information:
29  *  Intel Linux Wireless <linuxwifi@intel.com>
30  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
31  *
32  * BSD LICENSE
33  *
34  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
35  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  *
42  *  * Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  *  * Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in
46  *    the documentation and/or other materials provided with the
47  *    distribution.
48  *  * Neither the name Intel Corporation nor the names of its
49  *    contributors may be used to endorse or promote products derived
50  *    from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
58  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
59  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
60  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
62  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63  *
64  *****************************************************************************/
65 #include "mvm.h"
66 #include "fw-api-tof.h"
67 #include "debugfs.h"
68 
69 static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
70 				 struct ieee80211_vif *vif,
71 				 enum iwl_dbgfs_pm_mask param, int val)
72 {
73 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
74 	struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
75 
76 	dbgfs_pm->mask |= param;
77 
78 	switch (param) {
79 	case MVM_DEBUGFS_PM_KEEP_ALIVE: {
80 		int dtimper = vif->bss_conf.dtim_period ?: 1;
81 		int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
82 
83 		IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
84 		if (val * MSEC_PER_SEC < 3 * dtimper_msec)
85 			IWL_WARN(mvm,
86 				 "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
87 				 val * MSEC_PER_SEC, 3 * dtimper_msec);
88 		dbgfs_pm->keep_alive_seconds = val;
89 		break;
90 	}
91 	case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
92 		IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
93 				val ? "enabled" : "disabled");
94 		dbgfs_pm->skip_over_dtim = val;
95 		break;
96 	case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
97 		IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
98 		dbgfs_pm->skip_dtim_periods = val;
99 		break;
100 	case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
101 		IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
102 		dbgfs_pm->rx_data_timeout = val;
103 		break;
104 	case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
105 		IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
106 		dbgfs_pm->tx_data_timeout = val;
107 		break;
108 	case MVM_DEBUGFS_PM_LPRX_ENA:
109 		IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
110 		dbgfs_pm->lprx_ena = val;
111 		break;
112 	case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
113 		IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
114 		dbgfs_pm->lprx_rssi_threshold = val;
115 		break;
116 	case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
117 		IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
118 		dbgfs_pm->snooze_ena = val;
119 		break;
120 	case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
121 		IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
122 		dbgfs_pm->uapsd_misbehaving = val;
123 		break;
124 	case MVM_DEBUGFS_PM_USE_PS_POLL:
125 		IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val);
126 		dbgfs_pm->use_ps_poll = val;
127 		break;
128 	}
129 }
130 
131 static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
132 					 size_t count, loff_t *ppos)
133 {
134 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
135 	struct iwl_mvm *mvm = mvmvif->mvm;
136 	enum iwl_dbgfs_pm_mask param;
137 	int val, ret;
138 
139 	if (!strncmp("keep_alive=", buf, 11)) {
140 		if (sscanf(buf + 11, "%d", &val) != 1)
141 			return -EINVAL;
142 		param = MVM_DEBUGFS_PM_KEEP_ALIVE;
143 	} else if (!strncmp("skip_over_dtim=", buf, 15)) {
144 		if (sscanf(buf + 15, "%d", &val) != 1)
145 			return -EINVAL;
146 		param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
147 	} else if (!strncmp("skip_dtim_periods=", buf, 18)) {
148 		if (sscanf(buf + 18, "%d", &val) != 1)
149 			return -EINVAL;
150 		param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
151 	} else if (!strncmp("rx_data_timeout=", buf, 16)) {
152 		if (sscanf(buf + 16, "%d", &val) != 1)
153 			return -EINVAL;
154 		param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
155 	} else if (!strncmp("tx_data_timeout=", buf, 16)) {
156 		if (sscanf(buf + 16, "%d", &val) != 1)
157 			return -EINVAL;
158 		param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
159 	} else if (!strncmp("lprx=", buf, 5)) {
160 		if (sscanf(buf + 5, "%d", &val) != 1)
161 			return -EINVAL;
162 		param = MVM_DEBUGFS_PM_LPRX_ENA;
163 	} else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
164 		if (sscanf(buf + 20, "%d", &val) != 1)
165 			return -EINVAL;
166 		if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
167 		    POWER_LPRX_RSSI_THRESHOLD_MIN)
168 			return -EINVAL;
169 		param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
170 	} else if (!strncmp("snooze_enable=", buf, 14)) {
171 		if (sscanf(buf + 14, "%d", &val) != 1)
172 			return -EINVAL;
173 		param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
174 	} else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
175 		if (sscanf(buf + 18, "%d", &val) != 1)
176 			return -EINVAL;
177 		param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
178 	} else if (!strncmp("use_ps_poll=", buf, 12)) {
179 		if (sscanf(buf + 12, "%d", &val) != 1)
180 			return -EINVAL;
181 		param = MVM_DEBUGFS_PM_USE_PS_POLL;
182 	} else {
183 		return -EINVAL;
184 	}
185 
186 	mutex_lock(&mvm->mutex);
187 	iwl_dbgfs_update_pm(mvm, vif, param, val);
188 	ret = iwl_mvm_power_update_mac(mvm);
189 	mutex_unlock(&mvm->mutex);
190 
191 	return ret ?: count;
192 }
193 
194 static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file,
195 					 char __user *user_buf,
196 					 size_t count, loff_t *ppos)
197 {
198 	struct ieee80211_vif *vif = file->private_data;
199 	char buf[64];
200 	int bufsz = sizeof(buf);
201 	int pos;
202 
203 	pos = scnprintf(buf, bufsz, "bss limit = %d\n",
204 			vif->bss_conf.txpower);
205 
206 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
207 }
208 
209 static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
210 					char __user *user_buf,
211 					size_t count, loff_t *ppos)
212 {
213 	struct ieee80211_vif *vif = file->private_data;
214 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
215 	struct iwl_mvm *mvm = mvmvif->mvm;
216 	char buf[512];
217 	int bufsz = sizeof(buf);
218 	int pos;
219 
220 	pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
221 
222 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
223 }
224 
225 static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
226 					 char __user *user_buf,
227 					 size_t count, loff_t *ppos)
228 {
229 	struct ieee80211_vif *vif = file->private_data;
230 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
231 	struct iwl_mvm *mvm = mvmvif->mvm;
232 	u8 ap_sta_id;
233 	struct ieee80211_chanctx_conf *chanctx_conf;
234 	char buf[512];
235 	int bufsz = sizeof(buf);
236 	int pos = 0;
237 	int i;
238 
239 	mutex_lock(&mvm->mutex);
240 
241 	ap_sta_id = mvmvif->ap_sta_id;
242 
243 	switch (ieee80211_vif_type_p2p(vif)) {
244 	case NL80211_IFTYPE_ADHOC:
245 		pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
246 		break;
247 	case NL80211_IFTYPE_STATION:
248 		pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
249 		break;
250 	case NL80211_IFTYPE_AP:
251 		pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
252 		break;
253 	case NL80211_IFTYPE_P2P_CLIENT:
254 		pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
255 		break;
256 	case NL80211_IFTYPE_P2P_GO:
257 		pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
258 		break;
259 	case NL80211_IFTYPE_P2P_DEVICE:
260 		pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
261 		break;
262 	default:
263 		break;
264 	}
265 
266 	pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
267 			 mvmvif->id, mvmvif->color);
268 	pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
269 			 vif->bss_conf.bssid);
270 	pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
271 	for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++)
272 		pos += scnprintf(buf+pos, bufsz-pos,
273 				 "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
274 				 i, mvmvif->queue_params[i].txop,
275 				 mvmvif->queue_params[i].cw_min,
276 				 mvmvif->queue_params[i].cw_max,
277 				 mvmvif->queue_params[i].aifs,
278 				 mvmvif->queue_params[i].uapsd);
279 
280 	if (vif->type == NL80211_IFTYPE_STATION &&
281 	    ap_sta_id != IWL_MVM_STATION_COUNT) {
282 		struct ieee80211_sta *sta;
283 
284 		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
285 						lockdep_is_held(&mvm->mutex));
286 		if (!IS_ERR_OR_NULL(sta)) {
287 			struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
288 
289 			pos += scnprintf(buf+pos, bufsz-pos,
290 					 "ap_sta_id %d - reduced Tx power %d\n",
291 					 ap_sta_id,
292 					 mvm_sta->bt_reduced_txpower);
293 		}
294 	}
295 
296 	rcu_read_lock();
297 	chanctx_conf = rcu_dereference(vif->chanctx_conf);
298 	if (chanctx_conf)
299 		pos += scnprintf(buf+pos, bufsz-pos,
300 				 "idle rx chains %d, active rx chains: %d\n",
301 				 chanctx_conf->rx_chains_static,
302 				 chanctx_conf->rx_chains_dynamic);
303 	rcu_read_unlock();
304 
305 	mutex_unlock(&mvm->mutex);
306 
307 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
308 }
309 
310 static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
311 				enum iwl_dbgfs_bf_mask param, int value)
312 {
313 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
314 	struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
315 
316 	dbgfs_bf->mask |= param;
317 
318 	switch (param) {
319 	case MVM_DEBUGFS_BF_ENERGY_DELTA:
320 		dbgfs_bf->bf_energy_delta = value;
321 		break;
322 	case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
323 		dbgfs_bf->bf_roaming_energy_delta = value;
324 		break;
325 	case MVM_DEBUGFS_BF_ROAMING_STATE:
326 		dbgfs_bf->bf_roaming_state = value;
327 		break;
328 	case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
329 		dbgfs_bf->bf_temp_threshold = value;
330 		break;
331 	case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
332 		dbgfs_bf->bf_temp_fast_filter = value;
333 		break;
334 	case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
335 		dbgfs_bf->bf_temp_slow_filter = value;
336 		break;
337 	case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
338 		dbgfs_bf->bf_enable_beacon_filter = value;
339 		break;
340 	case MVM_DEBUGFS_BF_DEBUG_FLAG:
341 		dbgfs_bf->bf_debug_flag = value;
342 		break;
343 	case MVM_DEBUGFS_BF_ESCAPE_TIMER:
344 		dbgfs_bf->bf_escape_timer = value;
345 		break;
346 	case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
347 		dbgfs_bf->ba_enable_beacon_abort = value;
348 		break;
349 	case MVM_DEBUGFS_BA_ESCAPE_TIMER:
350 		dbgfs_bf->ba_escape_timer = value;
351 		break;
352 	}
353 }
354 
355 static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
356 					 size_t count, loff_t *ppos)
357 {
358 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
359 	struct iwl_mvm *mvm = mvmvif->mvm;
360 	enum iwl_dbgfs_bf_mask param;
361 	int value, ret = 0;
362 
363 	if (!strncmp("bf_energy_delta=", buf, 16)) {
364 		if (sscanf(buf+16, "%d", &value) != 1)
365 			return -EINVAL;
366 		if (value < IWL_BF_ENERGY_DELTA_MIN ||
367 		    value > IWL_BF_ENERGY_DELTA_MAX)
368 			return -EINVAL;
369 		param = MVM_DEBUGFS_BF_ENERGY_DELTA;
370 	} else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
371 		if (sscanf(buf+24, "%d", &value) != 1)
372 			return -EINVAL;
373 		if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
374 		    value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
375 			return -EINVAL;
376 		param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
377 	} else if (!strncmp("bf_roaming_state=", buf, 17)) {
378 		if (sscanf(buf+17, "%d", &value) != 1)
379 			return -EINVAL;
380 		if (value < IWL_BF_ROAMING_STATE_MIN ||
381 		    value > IWL_BF_ROAMING_STATE_MAX)
382 			return -EINVAL;
383 		param = MVM_DEBUGFS_BF_ROAMING_STATE;
384 	} else if (!strncmp("bf_temp_threshold=", buf, 18)) {
385 		if (sscanf(buf+18, "%d", &value) != 1)
386 			return -EINVAL;
387 		if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
388 		    value > IWL_BF_TEMP_THRESHOLD_MAX)
389 			return -EINVAL;
390 		param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
391 	} else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
392 		if (sscanf(buf+20, "%d", &value) != 1)
393 			return -EINVAL;
394 		if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
395 		    value > IWL_BF_TEMP_FAST_FILTER_MAX)
396 			return -EINVAL;
397 		param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
398 	} else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
399 		if (sscanf(buf+20, "%d", &value) != 1)
400 			return -EINVAL;
401 		if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
402 		    value > IWL_BF_TEMP_SLOW_FILTER_MAX)
403 			return -EINVAL;
404 		param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
405 	} else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
406 		if (sscanf(buf+24, "%d", &value) != 1)
407 			return -EINVAL;
408 		if (value < 0 || value > 1)
409 			return -EINVAL;
410 		param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
411 	} else if (!strncmp("bf_debug_flag=", buf, 14)) {
412 		if (sscanf(buf+14, "%d", &value) != 1)
413 			return -EINVAL;
414 		if (value < 0 || value > 1)
415 			return -EINVAL;
416 		param = MVM_DEBUGFS_BF_DEBUG_FLAG;
417 	} else if (!strncmp("bf_escape_timer=", buf, 16)) {
418 		if (sscanf(buf+16, "%d", &value) != 1)
419 			return -EINVAL;
420 		if (value < IWL_BF_ESCAPE_TIMER_MIN ||
421 		    value > IWL_BF_ESCAPE_TIMER_MAX)
422 			return -EINVAL;
423 		param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
424 	} else if (!strncmp("ba_escape_timer=", buf, 16)) {
425 		if (sscanf(buf+16, "%d", &value) != 1)
426 			return -EINVAL;
427 		if (value < IWL_BA_ESCAPE_TIMER_MIN ||
428 		    value > IWL_BA_ESCAPE_TIMER_MAX)
429 			return -EINVAL;
430 		param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
431 	} else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
432 		if (sscanf(buf+23, "%d", &value) != 1)
433 			return -EINVAL;
434 		if (value < 0 || value > 1)
435 			return -EINVAL;
436 		param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
437 	} else {
438 		return -EINVAL;
439 	}
440 
441 	mutex_lock(&mvm->mutex);
442 	iwl_dbgfs_update_bf(vif, param, value);
443 	if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
444 		ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
445 	else
446 		ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
447 	mutex_unlock(&mvm->mutex);
448 
449 	return ret ?: count;
450 }
451 
452 static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
453 					char __user *user_buf,
454 					size_t count, loff_t *ppos)
455 {
456 	struct ieee80211_vif *vif = file->private_data;
457 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
458 	char buf[256];
459 	int pos = 0;
460 	const size_t bufsz = sizeof(buf);
461 	struct iwl_beacon_filter_cmd cmd = {
462 		IWL_BF_CMD_CONFIG_DEFAULTS,
463 		.bf_enable_beacon_filter =
464 			cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
465 		.ba_enable_beacon_abort =
466 			cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
467 	};
468 
469 	iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
470 	if (mvmvif->bf_data.bf_enabled)
471 		cmd.bf_enable_beacon_filter = cpu_to_le32(1);
472 	else
473 		cmd.bf_enable_beacon_filter = 0;
474 
475 	pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
476 			 le32_to_cpu(cmd.bf_energy_delta));
477 	pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
478 			 le32_to_cpu(cmd.bf_roaming_energy_delta));
479 	pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
480 			 le32_to_cpu(cmd.bf_roaming_state));
481 	pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
482 			 le32_to_cpu(cmd.bf_temp_threshold));
483 	pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
484 			 le32_to_cpu(cmd.bf_temp_fast_filter));
485 	pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
486 			 le32_to_cpu(cmd.bf_temp_slow_filter));
487 	pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
488 			 le32_to_cpu(cmd.bf_enable_beacon_filter));
489 	pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
490 			 le32_to_cpu(cmd.bf_debug_flag));
491 	pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
492 			 le32_to_cpu(cmd.bf_escape_timer));
493 	pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
494 			 le32_to_cpu(cmd.ba_escape_timer));
495 	pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
496 			 le32_to_cpu(cmd.ba_enable_beacon_abort));
497 
498 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
499 }
500 
501 static inline char *iwl_dbgfs_is_match(char *name, char *buf)
502 {
503 	int len = strlen(name);
504 
505 	return !strncmp(name, buf, len) ? buf + len : NULL;
506 }
507 
508 static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif,
509 					  char *buf,
510 					  size_t count, loff_t *ppos)
511 {
512 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
513 	struct iwl_mvm *mvm = mvmvif->mvm;
514 	u32 value;
515 	int ret = -EINVAL;
516 	char *data;
517 
518 	mutex_lock(&mvm->mutex);
519 
520 	data = iwl_dbgfs_is_match("tof_disabled=", buf);
521 	if (data) {
522 		ret = kstrtou32(data, 10, &value);
523 		if (ret == 0)
524 			mvm->tof_data.tof_cfg.tof_disabled = value;
525 		goto out;
526 	}
527 
528 	data = iwl_dbgfs_is_match("one_sided_disabled=", buf);
529 	if (data) {
530 		ret = kstrtou32(data, 10, &value);
531 		if (ret == 0)
532 			mvm->tof_data.tof_cfg.one_sided_disabled = value;
533 		goto out;
534 	}
535 
536 	data = iwl_dbgfs_is_match("is_debug_mode=", buf);
537 	if (data) {
538 		ret = kstrtou32(data, 10, &value);
539 		if (ret == 0)
540 			mvm->tof_data.tof_cfg.is_debug_mode = value;
541 		goto out;
542 	}
543 
544 	data = iwl_dbgfs_is_match("is_buf=", buf);
545 	if (data) {
546 		ret = kstrtou32(data, 10, &value);
547 		if (ret == 0)
548 			mvm->tof_data.tof_cfg.is_buf_required = value;
549 		goto out;
550 	}
551 
552 	data = iwl_dbgfs_is_match("send_tof_cfg=", buf);
553 	if (data) {
554 		ret = kstrtou32(data, 10, &value);
555 		if (ret == 0 && value) {
556 			ret = iwl_mvm_tof_config_cmd(mvm);
557 			goto out;
558 		}
559 	}
560 
561 out:
562 	mutex_unlock(&mvm->mutex);
563 
564 	return ret ?: count;
565 }
566 
567 static ssize_t iwl_dbgfs_tof_enable_read(struct file *file,
568 					 char __user *user_buf,
569 					 size_t count, loff_t *ppos)
570 {
571 	struct ieee80211_vif *vif = file->private_data;
572 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
573 	struct iwl_mvm *mvm = mvmvif->mvm;
574 	char buf[256];
575 	int pos = 0;
576 	const size_t bufsz = sizeof(buf);
577 	struct iwl_tof_config_cmd *cmd;
578 
579 	cmd = &mvm->tof_data.tof_cfg;
580 
581 	mutex_lock(&mvm->mutex);
582 
583 	pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n",
584 			 cmd->tof_disabled);
585 	pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n",
586 			 cmd->one_sided_disabled);
587 	pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n",
588 			 cmd->is_debug_mode);
589 	pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n",
590 			 cmd->is_buf_required);
591 
592 	mutex_unlock(&mvm->mutex);
593 
594 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
595 }
596 
597 static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif,
598 						    char *buf,
599 						    size_t count, loff_t *ppos)
600 {
601 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
602 	struct iwl_mvm *mvm = mvmvif->mvm;
603 	u32 value;
604 	int ret = 0;
605 	char *data;
606 
607 	mutex_lock(&mvm->mutex);
608 
609 	data = iwl_dbgfs_is_match("burst_period=", buf);
610 	if (data) {
611 		ret = kstrtou32(data, 10, &value);
612 		if (!ret)
613 			mvm->tof_data.responder_cfg.burst_period =
614 							cpu_to_le16(value);
615 		goto out;
616 	}
617 
618 	data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
619 	if (data) {
620 		ret = kstrtou32(data, 10, &value);
621 		if (ret == 0)
622 			mvm->tof_data.responder_cfg.min_delta_ftm = value;
623 		goto out;
624 	}
625 
626 	data = iwl_dbgfs_is_match("burst_duration=", buf);
627 	if (data) {
628 		ret = kstrtou32(data, 10, &value);
629 		if (ret == 0)
630 			mvm->tof_data.responder_cfg.burst_duration = value;
631 		goto out;
632 	}
633 
634 	data = iwl_dbgfs_is_match("num_of_burst_exp=", buf);
635 	if (data) {
636 		ret = kstrtou32(data, 10, &value);
637 		if (ret == 0)
638 			mvm->tof_data.responder_cfg.num_of_burst_exp = value;
639 		goto out;
640 	}
641 
642 	data = iwl_dbgfs_is_match("abort_responder=", buf);
643 	if (data) {
644 		ret = kstrtou32(data, 10, &value);
645 		if (ret == 0)
646 			mvm->tof_data.responder_cfg.abort_responder = value;
647 		goto out;
648 	}
649 
650 	data = iwl_dbgfs_is_match("get_ch_est=", buf);
651 	if (data) {
652 		ret = kstrtou32(data, 10, &value);
653 		if (ret == 0)
654 			mvm->tof_data.responder_cfg.get_ch_est = value;
655 		goto out;
656 	}
657 
658 	data = iwl_dbgfs_is_match("recv_sta_req_params=", buf);
659 	if (data) {
660 		ret = kstrtou32(data, 10, &value);
661 		if (ret == 0)
662 			mvm->tof_data.responder_cfg.recv_sta_req_params = value;
663 		goto out;
664 	}
665 
666 	data = iwl_dbgfs_is_match("channel_num=", buf);
667 	if (data) {
668 		ret = kstrtou32(data, 10, &value);
669 		if (ret == 0)
670 			mvm->tof_data.responder_cfg.channel_num = value;
671 		goto out;
672 	}
673 
674 	data = iwl_dbgfs_is_match("bandwidth=", buf);
675 	if (data) {
676 		ret = kstrtou32(data, 10, &value);
677 		if (ret == 0)
678 			mvm->tof_data.responder_cfg.bandwidth = value;
679 		goto out;
680 	}
681 
682 	data = iwl_dbgfs_is_match("rate=", buf);
683 	if (data) {
684 		ret = kstrtou32(data, 10, &value);
685 		if (ret == 0)
686 			mvm->tof_data.responder_cfg.rate = value;
687 		goto out;
688 	}
689 
690 	data = iwl_dbgfs_is_match("bssid=", buf);
691 	if (data) {
692 		u8 *mac = mvm->tof_data.responder_cfg.bssid;
693 
694 		if (!mac_pton(data, mac)) {
695 			ret = -EINVAL;
696 			goto out;
697 		}
698 	}
699 
700 	data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf);
701 	if (data) {
702 		ret = kstrtou32(data, 10, &value);
703 		if (ret == 0)
704 			mvm->tof_data.responder_cfg.tsf_timer_offset_msecs =
705 							cpu_to_le16(value);
706 		goto out;
707 	}
708 
709 	data = iwl_dbgfs_is_match("toa_offset=", buf);
710 	if (data) {
711 		ret = kstrtou32(data, 10, &value);
712 		if (ret == 0)
713 			mvm->tof_data.responder_cfg.toa_offset =
714 							cpu_to_le16(value);
715 		goto out;
716 	}
717 
718 	data = iwl_dbgfs_is_match("center_freq=", buf);
719 	if (data) {
720 		struct iwl_tof_responder_config_cmd *cmd =
721 			&mvm->tof_data.responder_cfg;
722 
723 		ret = kstrtou32(data, 10, &value);
724 		if (ret == 0 && value) {
725 			enum ieee80211_band band = (cmd->channel_num <= 14) ?
726 						   IEEE80211_BAND_2GHZ :
727 						   IEEE80211_BAND_5GHZ;
728 			struct ieee80211_channel chn = {
729 				.band = band,
730 				.center_freq = ieee80211_channel_to_frequency(
731 					cmd->channel_num, band),
732 				};
733 			struct cfg80211_chan_def chandef = {
734 				.chan =  &chn,
735 				.center_freq1 =
736 					ieee80211_channel_to_frequency(value,
737 								       band),
738 			};
739 
740 			cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef);
741 		}
742 		goto out;
743 	}
744 
745 	data = iwl_dbgfs_is_match("ftm_per_burst=", buf);
746 	if (data) {
747 		ret = kstrtou32(data, 10, &value);
748 		if (ret == 0)
749 			mvm->tof_data.responder_cfg.ftm_per_burst = value;
750 		goto out;
751 	}
752 
753 	data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf);
754 	if (data) {
755 		ret = kstrtou32(data, 10, &value);
756 		if (ret == 0)
757 			mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value;
758 		goto out;
759 	}
760 
761 	data = iwl_dbgfs_is_match("asap_mode=", buf);
762 	if (data) {
763 		ret = kstrtou32(data, 10, &value);
764 		if (ret == 0)
765 			mvm->tof_data.responder_cfg.asap_mode = value;
766 		goto out;
767 	}
768 
769 	data = iwl_dbgfs_is_match("send_responder_cfg=", buf);
770 	if (data) {
771 		ret = kstrtou32(data, 10, &value);
772 		if (ret == 0 && value) {
773 			ret = iwl_mvm_tof_responder_cmd(mvm, vif);
774 			goto out;
775 		}
776 	}
777 
778 out:
779 	mutex_unlock(&mvm->mutex);
780 
781 	return ret ?: count;
782 }
783 
784 static ssize_t iwl_dbgfs_tof_responder_params_read(struct file *file,
785 						   char __user *user_buf,
786 						   size_t count, loff_t *ppos)
787 {
788 	struct ieee80211_vif *vif = file->private_data;
789 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
790 	struct iwl_mvm *mvm = mvmvif->mvm;
791 	char buf[256];
792 	int pos = 0;
793 	const size_t bufsz = sizeof(buf);
794 	struct iwl_tof_responder_config_cmd *cmd;
795 
796 	cmd = &mvm->tof_data.responder_cfg;
797 
798 	mutex_lock(&mvm->mutex);
799 
800 	pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n",
801 			 le16_to_cpu(cmd->burst_period));
802 	pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n",
803 			 cmd->burst_duration);
804 	pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n",
805 			 cmd->bandwidth);
806 	pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n",
807 			 cmd->channel_num);
808 	pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n",
809 			 cmd->ctrl_ch_position);
810 	pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n",
811 			 cmd->bssid);
812 	pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n",
813 			 cmd->min_delta_ftm);
814 	pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n",
815 			 cmd->num_of_burst_exp);
816 	pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate);
817 	pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n",
818 			 cmd->abort_responder);
819 	pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n",
820 			 cmd->get_ch_est);
821 	pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n",
822 			 cmd->recv_sta_req_params);
823 	pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n",
824 			 cmd->ftm_per_burst);
825 	pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n",
826 			 cmd->ftm_resp_ts_avail);
827 	pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n",
828 			 cmd->asap_mode);
829 	pos += scnprintf(buf + pos, bufsz - pos,
830 			 "tsf_timer_offset_msecs = %d\n",
831 			 le16_to_cpu(cmd->tsf_timer_offset_msecs));
832 	pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n",
833 			 le16_to_cpu(cmd->toa_offset));
834 
835 	mutex_unlock(&mvm->mutex);
836 
837 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
838 }
839 
840 static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif,
841 						 char *buf, size_t count,
842 						 loff_t *ppos)
843 {
844 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
845 	struct iwl_mvm *mvm = mvmvif->mvm;
846 	u32 value;
847 	int ret = 0;
848 	char *data;
849 
850 	mutex_lock(&mvm->mutex);
851 
852 	data = iwl_dbgfs_is_match("request_id=", buf);
853 	if (data) {
854 		ret = kstrtou32(data, 10, &value);
855 		if (ret == 0)
856 			mvm->tof_data.range_req.request_id = value;
857 		goto out;
858 	}
859 
860 	data = iwl_dbgfs_is_match("initiator=", buf);
861 	if (data) {
862 		ret = kstrtou32(data, 10, &value);
863 		if (ret == 0)
864 			mvm->tof_data.range_req.initiator = value;
865 		goto out;
866 	}
867 
868 	data = iwl_dbgfs_is_match("one_sided_los_disable=", buf);
869 	if (data) {
870 		ret = kstrtou32(data, 10, &value);
871 		if (ret == 0)
872 			mvm->tof_data.range_req.one_sided_los_disable = value;
873 		goto out;
874 	}
875 
876 	data = iwl_dbgfs_is_match("req_timeout=", buf);
877 	if (data) {
878 		ret = kstrtou32(data, 10, &value);
879 		if (ret == 0)
880 			mvm->tof_data.range_req.req_timeout = value;
881 		goto out;
882 	}
883 
884 	data = iwl_dbgfs_is_match("report_policy=", buf);
885 	if (data) {
886 		ret = kstrtou32(data, 10, &value);
887 		if (ret == 0)
888 			mvm->tof_data.range_req.report_policy = value;
889 		goto out;
890 	}
891 
892 	data = iwl_dbgfs_is_match("macaddr_random=", buf);
893 	if (data) {
894 		ret = kstrtou32(data, 10, &value);
895 		if (ret == 0)
896 			mvm->tof_data.range_req.macaddr_random = value;
897 		goto out;
898 	}
899 
900 	data = iwl_dbgfs_is_match("num_of_ap=", buf);
901 	if (data) {
902 		ret = kstrtou32(data, 10, &value);
903 		if (ret == 0)
904 			mvm->tof_data.range_req.num_of_ap = value;
905 		goto out;
906 	}
907 
908 	data = iwl_dbgfs_is_match("macaddr_template=", buf);
909 	if (data) {
910 		u8 mac[ETH_ALEN];
911 
912 		if (!mac_pton(data, mac)) {
913 			ret = -EINVAL;
914 			goto out;
915 		}
916 		memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN);
917 		goto out;
918 	}
919 
920 	data = iwl_dbgfs_is_match("macaddr_mask=", buf);
921 	if (data) {
922 		u8 mac[ETH_ALEN];
923 
924 		if (!mac_pton(data, mac)) {
925 			ret = -EINVAL;
926 			goto out;
927 		}
928 		memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN);
929 		goto out;
930 	}
931 
932 	data = iwl_dbgfs_is_match("ap=", buf);
933 	if (data) {
934 		struct iwl_tof_range_req_ap_entry ap = {};
935 		int size = sizeof(struct iwl_tof_range_req_ap_entry);
936 		u16 burst_period;
937 		u8 *mac = ap.bssid;
938 		unsigned int i;
939 
940 		if (sscanf(data, "%u %hhd %hhd %hhd"
941 			   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"
942 			   "%hhd %hhd %hd"
943 			   "%hhd %hhd %d"
944 			   "%hhx %hhd %hhd %hhd",
945 			   &i, &ap.channel_num, &ap.bandwidth,
946 			   &ap.ctrl_ch_position,
947 			   mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5,
948 			   &ap.measure_type, &ap.num_of_bursts,
949 			   &burst_period,
950 			   &ap.samples_per_burst, &ap.retries_per_sample,
951 			   &ap.tsf_delta, &ap.location_req, &ap.asap_mode,
952 			   &ap.enable_dyn_ack, &ap.rssi) != 20) {
953 			ret = -EINVAL;
954 			goto out;
955 		}
956 		if (i >= IWL_MVM_TOF_MAX_APS) {
957 			IWL_ERR(mvm, "Invalid AP index %d\n", i);
958 			ret = -EINVAL;
959 			goto out;
960 		}
961 
962 		ap.burst_period = cpu_to_le16(burst_period);
963 
964 		memcpy(&mvm->tof_data.range_req.ap[i], &ap, size);
965 		goto out;
966 	}
967 
968 	data = iwl_dbgfs_is_match("send_range_request=", buf);
969 	if (data) {
970 		ret = kstrtou32(data, 10, &value);
971 		if (ret == 0 && value)
972 			ret = iwl_mvm_tof_range_request_cmd(mvm, vif);
973 		goto out;
974 	}
975 
976 	ret = -EINVAL;
977 out:
978 	mutex_unlock(&mvm->mutex);
979 	return ret ?: count;
980 }
981 
982 static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file,
983 						char __user *user_buf,
984 						size_t count, loff_t *ppos)
985 {
986 	struct ieee80211_vif *vif = file->private_data;
987 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
988 	struct iwl_mvm *mvm = mvmvif->mvm;
989 	char buf[512];
990 	int pos = 0;
991 	const size_t bufsz = sizeof(buf);
992 	struct iwl_tof_range_req_cmd *cmd;
993 	int i;
994 
995 	cmd = &mvm->tof_data.range_req;
996 
997 	mutex_lock(&mvm->mutex);
998 
999 	pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n",
1000 			 cmd->request_id);
1001 	pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n",
1002 			 cmd->initiator);
1003 	pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n",
1004 			 cmd->one_sided_los_disable);
1005 	pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n",
1006 			 cmd->req_timeout);
1007 	pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n",
1008 			 cmd->report_policy);
1009 	pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n",
1010 			 cmd->macaddr_random);
1011 	pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n",
1012 			 cmd->macaddr_template);
1013 	pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n",
1014 			 cmd->macaddr_mask);
1015 	pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n",
1016 			 cmd->num_of_ap);
1017 	for (i = 0; i < cmd->num_of_ap; i++) {
1018 		struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i];
1019 
1020 		pos += scnprintf(buf + pos, bufsz - pos,
1021 				"ap %.2d: channel_num=%hhd bw=%hhd"
1022 				" control=%hhd bssid=%pM type=%hhd"
1023 				" num_of_bursts=%hhd burst_period=%hd ftm=%hhd"
1024 				" retries=%hhd tsf_delta=%d"
1025 				" tsf_delta_direction=%hhd location_req=0x%hhx "
1026 				" asap=%hhd enable=%hhd rssi=%hhd\n",
1027 				i, ap->channel_num, ap->bandwidth,
1028 				ap->ctrl_ch_position, ap->bssid,
1029 				ap->measure_type, ap->num_of_bursts,
1030 				ap->burst_period, ap->samples_per_burst,
1031 				ap->retries_per_sample, ap->tsf_delta,
1032 				ap->tsf_delta_direction,
1033 				ap->location_req, ap->asap_mode,
1034 				ap->enable_dyn_ack, ap->rssi);
1035 	}
1036 
1037 	mutex_unlock(&mvm->mutex);
1038 
1039 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1040 }
1041 
1042 static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif,
1043 						 char *buf,
1044 						 size_t count, loff_t *ppos)
1045 {
1046 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1047 	struct iwl_mvm *mvm = mvmvif->mvm;
1048 	u32 value;
1049 	int ret = 0;
1050 	char *data;
1051 
1052 	mutex_lock(&mvm->mutex);
1053 
1054 	data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf);
1055 	if (data) {
1056 		ret = kstrtou32(data, 10, &value);
1057 		if (ret == 0)
1058 			mvm->tof_data.range_req_ext.tsf_timer_offset_msec =
1059 							cpu_to_le16(value);
1060 		goto out;
1061 	}
1062 
1063 	data = iwl_dbgfs_is_match("min_delta_ftm=", buf);
1064 	if (data) {
1065 		ret = kstrtou32(data, 10, &value);
1066 		if (ret == 0)
1067 			mvm->tof_data.range_req_ext.min_delta_ftm = value;
1068 		goto out;
1069 	}
1070 
1071 	data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf);
1072 	if (data) {
1073 		ret = kstrtou32(data, 10, &value);
1074 		if (ret == 0)
1075 			mvm->tof_data.range_req_ext.ftm_format_and_bw20M =
1076 									value;
1077 		goto out;
1078 	}
1079 
1080 	data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf);
1081 	if (data) {
1082 		ret = kstrtou32(data, 10, &value);
1083 		if (ret == 0)
1084 			mvm->tof_data.range_req_ext.ftm_format_and_bw40M =
1085 									value;
1086 		goto out;
1087 	}
1088 
1089 	data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf);
1090 	if (data) {
1091 		ret = kstrtou32(data, 10, &value);
1092 		if (ret == 0)
1093 			mvm->tof_data.range_req_ext.ftm_format_and_bw80M =
1094 									value;
1095 		goto out;
1096 	}
1097 
1098 	data = iwl_dbgfs_is_match("send_range_req_ext=", buf);
1099 	if (data) {
1100 		ret = kstrtou32(data, 10, &value);
1101 		if (ret == 0 && value)
1102 			ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif);
1103 		goto out;
1104 	}
1105 
1106 	ret = -EINVAL;
1107 out:
1108 	mutex_unlock(&mvm->mutex);
1109 	return ret ?: count;
1110 }
1111 
1112 static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file,
1113 						char __user *user_buf,
1114 						size_t count, loff_t *ppos)
1115 {
1116 	struct ieee80211_vif *vif = file->private_data;
1117 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1118 	struct iwl_mvm *mvm = mvmvif->mvm;
1119 	char buf[256];
1120 	int pos = 0;
1121 	const size_t bufsz = sizeof(buf);
1122 	struct iwl_tof_range_req_ext_cmd *cmd;
1123 
1124 	cmd = &mvm->tof_data.range_req_ext;
1125 
1126 	mutex_lock(&mvm->mutex);
1127 
1128 	pos += scnprintf(buf + pos, bufsz - pos,
1129 			 "tsf_timer_offset_msec = %hd\n",
1130 			 cmd->tsf_timer_offset_msec);
1131 	pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n",
1132 			 cmd->min_delta_ftm);
1133 	pos += scnprintf(buf + pos, bufsz - pos,
1134 			 "ftm_format_and_bw20M = %hhd\n",
1135 			 cmd->ftm_format_and_bw20M);
1136 	pos += scnprintf(buf + pos, bufsz - pos,
1137 			 "ftm_format_and_bw40M = %hhd\n",
1138 			 cmd->ftm_format_and_bw40M);
1139 	pos += scnprintf(buf + pos, bufsz - pos,
1140 			 "ftm_format_and_bw80M = %hhd\n",
1141 			 cmd->ftm_format_and_bw80M);
1142 
1143 	mutex_unlock(&mvm->mutex);
1144 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1145 }
1146 
1147 static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif,
1148 					       char *buf,
1149 					       size_t count, loff_t *ppos)
1150 {
1151 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1152 	struct iwl_mvm *mvm = mvmvif->mvm;
1153 	u32 value;
1154 	int abort_id, ret = 0;
1155 	char *data;
1156 
1157 	mutex_lock(&mvm->mutex);
1158 
1159 	data = iwl_dbgfs_is_match("abort_id=", buf);
1160 	if (data) {
1161 		ret = kstrtou32(data, 10, &value);
1162 		if (ret == 0)
1163 			mvm->tof_data.last_abort_id = value;
1164 		goto out;
1165 	}
1166 
1167 	data = iwl_dbgfs_is_match("send_range_abort=", buf);
1168 	if (data) {
1169 		ret = kstrtou32(data, 10, &value);
1170 		if (ret == 0 && value) {
1171 			abort_id = mvm->tof_data.last_abort_id;
1172 			ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id);
1173 			goto out;
1174 		}
1175 	}
1176 
1177 out:
1178 	mutex_unlock(&mvm->mutex);
1179 	return ret ?: count;
1180 }
1181 
1182 static ssize_t iwl_dbgfs_tof_range_abort_read(struct file *file,
1183 					      char __user *user_buf,
1184 					      size_t count, loff_t *ppos)
1185 {
1186 	struct ieee80211_vif *vif = file->private_data;
1187 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1188 	struct iwl_mvm *mvm = mvmvif->mvm;
1189 	char buf[32];
1190 	int pos = 0;
1191 	const size_t bufsz = sizeof(buf);
1192 	int last_abort_id;
1193 
1194 	mutex_lock(&mvm->mutex);
1195 	last_abort_id = mvm->tof_data.last_abort_id;
1196 	mutex_unlock(&mvm->mutex);
1197 
1198 	pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n",
1199 			 last_abort_id);
1200 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1201 }
1202 
1203 static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file,
1204 						 char __user *user_buf,
1205 						 size_t count, loff_t *ppos)
1206 {
1207 	struct ieee80211_vif *vif = file->private_data;
1208 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1209 	struct iwl_mvm *mvm = mvmvif->mvm;
1210 	char *buf;
1211 	int pos = 0;
1212 	const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256;
1213 	struct iwl_tof_range_rsp_ntfy *cmd;
1214 	int i, ret;
1215 
1216 	buf = kzalloc(bufsz, GFP_KERNEL);
1217 	if (!buf)
1218 		return -ENOMEM;
1219 
1220 	mutex_lock(&mvm->mutex);
1221 	cmd = &mvm->tof_data.range_resp;
1222 
1223 	pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n",
1224 			 cmd->request_id);
1225 	pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n",
1226 			 cmd->request_status);
1227 	pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n",
1228 			 cmd->last_in_batch);
1229 	pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n",
1230 			 cmd->num_of_aps);
1231 	for (i = 0; i < cmd->num_of_aps; i++) {
1232 		struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i];
1233 
1234 		pos += scnprintf(buf + pos, bufsz - pos,
1235 				"ap %.2d: bssid=%pM status=%hhd bw=%hhd"
1236 				" rtt=%d rtt_var=%d rtt_spread=%d"
1237 				" rssi=%hhd  rssi_spread=%hhd"
1238 				" range=%d range_var=%d"
1239 				" time_stamp=%d\n",
1240 				i, ap->bssid, ap->measure_status,
1241 				ap->measure_bw,
1242 				ap->rtt, ap->rtt_variance, ap->rtt_spread,
1243 				ap->rssi, ap->rssi_spread, ap->range,
1244 				ap->range_variance, ap->timestamp);
1245 	}
1246 	mutex_unlock(&mvm->mutex);
1247 
1248 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1249 	kfree(buf);
1250 	return ret;
1251 }
1252 
1253 static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
1254 					   size_t count, loff_t *ppos)
1255 {
1256 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1257 	struct iwl_mvm *mvm = mvmvif->mvm;
1258 	u8 value;
1259 	int ret;
1260 
1261 	ret = kstrtou8(buf, 0, &value);
1262 	if (ret)
1263 		return ret;
1264 	if (value > 1)
1265 		return -EINVAL;
1266 
1267 	mutex_lock(&mvm->mutex);
1268 	iwl_mvm_update_low_latency(mvm, vif, value);
1269 	mutex_unlock(&mvm->mutex);
1270 
1271 	return count;
1272 }
1273 
1274 static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
1275 					  char __user *user_buf,
1276 					  size_t count, loff_t *ppos)
1277 {
1278 	struct ieee80211_vif *vif = file->private_data;
1279 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1280 	char buf[2];
1281 
1282 	buf[0] = mvmvif->low_latency ? '1' : '0';
1283 	buf[1] = '\n';
1284 	return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
1285 }
1286 
1287 static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file,
1288 						char __user *user_buf,
1289 						size_t count, loff_t *ppos)
1290 {
1291 	struct ieee80211_vif *vif = file->private_data;
1292 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1293 	char buf[20];
1294 	int len;
1295 
1296 	len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid);
1297 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1298 }
1299 
1300 static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif,
1301 						 char *buf, size_t count,
1302 						 loff_t *ppos)
1303 {
1304 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1305 	struct iwl_mvm *mvm = mvmvif->mvm;
1306 	bool ret;
1307 
1308 	mutex_lock(&mvm->mutex);
1309 	ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid);
1310 	mutex_unlock(&mvm->mutex);
1311 
1312 	return ret ? count : -EINVAL;
1313 }
1314 
1315 static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf,
1316 					  size_t count, loff_t *ppos)
1317 {
1318 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1319 	struct iwl_mvm *mvm = mvmvif->mvm;
1320 	struct ieee80211_chanctx_conf *chanctx_conf;
1321 	struct iwl_mvm_phy_ctxt *phy_ctxt;
1322 	u16 value;
1323 	int ret;
1324 
1325 	ret = kstrtou16(buf, 0, &value);
1326 	if (ret)
1327 		return ret;
1328 
1329 	mutex_lock(&mvm->mutex);
1330 	rcu_read_lock();
1331 
1332 	chanctx_conf = rcu_dereference(vif->chanctx_conf);
1333 	/* make sure the channel context is assigned */
1334 	if (!chanctx_conf) {
1335 		rcu_read_unlock();
1336 		mutex_unlock(&mvm->mutex);
1337 		return -EINVAL;
1338 	}
1339 
1340 	phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
1341 	rcu_read_unlock();
1342 
1343 	mvm->dbgfs_rx_phyinfo = value;
1344 
1345 	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
1346 				       chanctx_conf->rx_chains_static,
1347 				       chanctx_conf->rx_chains_dynamic);
1348 	mutex_unlock(&mvm->mutex);
1349 
1350 	return ret ?: count;
1351 }
1352 
1353 static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
1354 					 char __user *user_buf,
1355 					 size_t count, loff_t *ppos)
1356 {
1357 	struct ieee80211_vif *vif = file->private_data;
1358 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1359 	char buf[8];
1360 
1361 	snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo);
1362 
1363 	return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
1364 }
1365 
1366 #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
1367 	_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
1368 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
1369 	_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
1370 #define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {		\
1371 		if (!debugfs_create_file(#name, mode, parent, vif,	\
1372 					 &iwl_dbgfs_##name##_ops))	\
1373 			goto err;					\
1374 	} while (0)
1375 
1376 MVM_DEBUGFS_READ_FILE_OPS(mac_params);
1377 MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt);
1378 MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
1379 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
1380 MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
1381 MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
1382 MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
1383 MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32);
1384 MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512);
1385 MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32);
1386 MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32);
1387 MVM_DEBUGFS_READ_FILE_OPS(tof_range_response);
1388 MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32);
1389 
1390 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
1391 {
1392 	struct dentry *dbgfs_dir = vif->debugfs_dir;
1393 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1394 	char buf[100];
1395 
1396 	/*
1397 	 * Check if debugfs directory already exist before creating it.
1398 	 * This may happen when, for example, resetting hw or suspend-resume
1399 	 */
1400 	if (!dbgfs_dir || mvmvif->dbgfs_dir)
1401 		return;
1402 
1403 	mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
1404 
1405 	if (!mvmvif->dbgfs_dir) {
1406 		IWL_ERR(mvm, "Failed to create debugfs directory under %s\n",
1407 			dbgfs_dir->d_name.name);
1408 		return;
1409 	}
1410 
1411 	if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
1412 	    ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
1413 	     (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
1414 	      mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)))
1415 		MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
1416 					 S_IRUSR);
1417 
1418 	MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR);
1419 	MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
1420 	MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
1421 				 S_IRUSR | S_IWUSR);
1422 	MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir,
1423 				 S_IRUSR | S_IWUSR);
1424 	MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir,
1425 				 S_IRUSR | S_IWUSR);
1426 
1427 	if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
1428 	    mvmvif == mvm->bf_allowed_vif)
1429 		MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
1430 					 S_IRUSR | S_IWUSR);
1431 
1432 	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) &&
1433 	    !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) {
1434 		if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP)
1435 			MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params,
1436 						 mvmvif->dbgfs_dir,
1437 						 S_IRUSR | S_IWUSR);
1438 
1439 		MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir,
1440 					 S_IRUSR | S_IWUSR);
1441 		MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir,
1442 					 S_IRUSR | S_IWUSR);
1443 		MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir,
1444 					 S_IRUSR | S_IWUSR);
1445 		MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir,
1446 					 S_IRUSR | S_IWUSR);
1447 		MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir,
1448 					 S_IRUSR);
1449 	}
1450 
1451 	/*
1452 	 * Create symlink for convenience pointing to interface specific
1453 	 * debugfs entries for the driver. For example, under
1454 	 * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
1455 	 * find
1456 	 * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
1457 	 */
1458 	snprintf(buf, 100, "../../../%s/%s/%s/%s",
1459 		 dbgfs_dir->d_parent->d_parent->d_name.name,
1460 		 dbgfs_dir->d_parent->d_name.name,
1461 		 dbgfs_dir->d_name.name,
1462 		 mvmvif->dbgfs_dir->d_name.name);
1463 
1464 	mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
1465 						     mvm->debugfs_dir, buf);
1466 	if (!mvmvif->dbgfs_slink)
1467 		IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n",
1468 			dbgfs_dir->d_name.name);
1469 	return;
1470 err:
1471 	IWL_ERR(mvm, "Can't create debugfs entity\n");
1472 }
1473 
1474 void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
1475 {
1476 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1477 
1478 	debugfs_remove(mvmvif->dbgfs_slink);
1479 	mvmvif->dbgfs_slink = NULL;
1480 
1481 	debugfs_remove_recursive(mvmvif->dbgfs_dir);
1482 	mvmvif->dbgfs_dir = NULL;
1483 }
1484