192daf3a6SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
292daf3a6SBjoern A. Zeeb /*
392daf3a6SBjoern A. Zeeb  * Copyright (C) 2012-2014, 2018-2021 Intel Corporation
492daf3a6SBjoern A. Zeeb  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
592daf3a6SBjoern A. Zeeb  * Copyright (C) 2016-2017 Intel Deutschland GmbH
692daf3a6SBjoern A. Zeeb  */
792daf3a6SBjoern A. Zeeb #include "mvm.h"
892daf3a6SBjoern A. Zeeb #include "debugfs.h"
992daf3a6SBjoern A. Zeeb #if defined(__FreeBSD__)
1092daf3a6SBjoern A. Zeeb #include <linux/math64.h>
1192daf3a6SBjoern A. Zeeb #endif
1292daf3a6SBjoern A. Zeeb 
1392daf3a6SBjoern A. Zeeb static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
1492daf3a6SBjoern A. Zeeb 				 struct ieee80211_vif *vif,
1592daf3a6SBjoern A. Zeeb 				 enum iwl_dbgfs_pm_mask param, int val)
1692daf3a6SBjoern A. Zeeb {
1792daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1892daf3a6SBjoern A. Zeeb 	struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
1992daf3a6SBjoern A. Zeeb 
2092daf3a6SBjoern A. Zeeb 	dbgfs_pm->mask |= param;
2192daf3a6SBjoern A. Zeeb 
2292daf3a6SBjoern A. Zeeb 	switch (param) {
2392daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_KEEP_ALIVE: {
2492daf3a6SBjoern A. Zeeb 		int dtimper = vif->bss_conf.dtim_period ?: 1;
2592daf3a6SBjoern A. Zeeb 		int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
2692daf3a6SBjoern A. Zeeb 
2792daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
2892daf3a6SBjoern A. Zeeb 		if (val * MSEC_PER_SEC < 3 * dtimper_msec)
2992daf3a6SBjoern A. Zeeb 			IWL_WARN(mvm,
3092daf3a6SBjoern A. Zeeb 				 "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
3192daf3a6SBjoern A. Zeeb 				 val * MSEC_PER_SEC, 3 * dtimper_msec);
3292daf3a6SBjoern A. Zeeb 		dbgfs_pm->keep_alive_seconds = val;
3392daf3a6SBjoern A. Zeeb 		break;
3492daf3a6SBjoern A. Zeeb 	}
3592daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
3692daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
3792daf3a6SBjoern A. Zeeb 				val ? "enabled" : "disabled");
3892daf3a6SBjoern A. Zeeb 		dbgfs_pm->skip_over_dtim = val;
3992daf3a6SBjoern A. Zeeb 		break;
4092daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
4192daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
4292daf3a6SBjoern A. Zeeb 		dbgfs_pm->skip_dtim_periods = val;
4392daf3a6SBjoern A. Zeeb 		break;
4492daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
4592daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
4692daf3a6SBjoern A. Zeeb 		dbgfs_pm->rx_data_timeout = val;
4792daf3a6SBjoern A. Zeeb 		break;
4892daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
4992daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
5092daf3a6SBjoern A. Zeeb 		dbgfs_pm->tx_data_timeout = val;
5192daf3a6SBjoern A. Zeeb 		break;
5292daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_LPRX_ENA:
5392daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
5492daf3a6SBjoern A. Zeeb 		dbgfs_pm->lprx_ena = val;
5592daf3a6SBjoern A. Zeeb 		break;
5692daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
5792daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
5892daf3a6SBjoern A. Zeeb 		dbgfs_pm->lprx_rssi_threshold = val;
5992daf3a6SBjoern A. Zeeb 		break;
6092daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
6192daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
6292daf3a6SBjoern A. Zeeb 		dbgfs_pm->snooze_ena = val;
6392daf3a6SBjoern A. Zeeb 		break;
6492daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
6592daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
6692daf3a6SBjoern A. Zeeb 		dbgfs_pm->uapsd_misbehaving = val;
6792daf3a6SBjoern A. Zeeb 		break;
6892daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_PM_USE_PS_POLL:
6992daf3a6SBjoern A. Zeeb 		IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val);
7092daf3a6SBjoern A. Zeeb 		dbgfs_pm->use_ps_poll = val;
7192daf3a6SBjoern A. Zeeb 		break;
7292daf3a6SBjoern A. Zeeb 	}
7392daf3a6SBjoern A. Zeeb }
7492daf3a6SBjoern A. Zeeb 
7592daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
7692daf3a6SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
7792daf3a6SBjoern A. Zeeb {
7892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
7992daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
8092daf3a6SBjoern A. Zeeb 	enum iwl_dbgfs_pm_mask param;
8192daf3a6SBjoern A. Zeeb 	int val, ret;
8292daf3a6SBjoern A. Zeeb 
8392daf3a6SBjoern A. Zeeb 	if (!strncmp("keep_alive=", buf, 11)) {
8492daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 11, "%d", &val) != 1)
8592daf3a6SBjoern A. Zeeb 			return -EINVAL;
8692daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_KEEP_ALIVE;
8792daf3a6SBjoern A. Zeeb 	} else if (!strncmp("skip_over_dtim=", buf, 15)) {
8892daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 15, "%d", &val) != 1)
8992daf3a6SBjoern A. Zeeb 			return -EINVAL;
9092daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
9192daf3a6SBjoern A. Zeeb 	} else if (!strncmp("skip_dtim_periods=", buf, 18)) {
9292daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 18, "%d", &val) != 1)
9392daf3a6SBjoern A. Zeeb 			return -EINVAL;
9492daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
9592daf3a6SBjoern A. Zeeb 	} else if (!strncmp("rx_data_timeout=", buf, 16)) {
9692daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 16, "%d", &val) != 1)
9792daf3a6SBjoern A. Zeeb 			return -EINVAL;
9892daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
9992daf3a6SBjoern A. Zeeb 	} else if (!strncmp("tx_data_timeout=", buf, 16)) {
10092daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 16, "%d", &val) != 1)
10192daf3a6SBjoern A. Zeeb 			return -EINVAL;
10292daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
10392daf3a6SBjoern A. Zeeb 	} else if (!strncmp("lprx=", buf, 5)) {
10492daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 5, "%d", &val) != 1)
10592daf3a6SBjoern A. Zeeb 			return -EINVAL;
10692daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_LPRX_ENA;
10792daf3a6SBjoern A. Zeeb 	} else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
10892daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 20, "%d", &val) != 1)
10992daf3a6SBjoern A. Zeeb 			return -EINVAL;
11092daf3a6SBjoern A. Zeeb 		if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
11192daf3a6SBjoern A. Zeeb 		    POWER_LPRX_RSSI_THRESHOLD_MIN)
11292daf3a6SBjoern A. Zeeb 			return -EINVAL;
11392daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
11492daf3a6SBjoern A. Zeeb 	} else if (!strncmp("snooze_enable=", buf, 14)) {
11592daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 14, "%d", &val) != 1)
11692daf3a6SBjoern A. Zeeb 			return -EINVAL;
11792daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
11892daf3a6SBjoern A. Zeeb 	} else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
11992daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 18, "%d", &val) != 1)
12092daf3a6SBjoern A. Zeeb 			return -EINVAL;
12192daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
12292daf3a6SBjoern A. Zeeb 	} else if (!strncmp("use_ps_poll=", buf, 12)) {
12392daf3a6SBjoern A. Zeeb 		if (sscanf(buf + 12, "%d", &val) != 1)
12492daf3a6SBjoern A. Zeeb 			return -EINVAL;
12592daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_PM_USE_PS_POLL;
12692daf3a6SBjoern A. Zeeb 	} else {
12792daf3a6SBjoern A. Zeeb 		return -EINVAL;
12892daf3a6SBjoern A. Zeeb 	}
12992daf3a6SBjoern A. Zeeb 
13092daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
13192daf3a6SBjoern A. Zeeb 	iwl_dbgfs_update_pm(mvm, vif, param, val);
13292daf3a6SBjoern A. Zeeb 	ret = iwl_mvm_power_update_mac(mvm);
13392daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
13492daf3a6SBjoern A. Zeeb 
13592daf3a6SBjoern A. Zeeb 	return ret ?: count;
13692daf3a6SBjoern A. Zeeb }
13792daf3a6SBjoern A. Zeeb 
13892daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file,
13992daf3a6SBjoern A. Zeeb 					 char __user *user_buf,
14092daf3a6SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
14192daf3a6SBjoern A. Zeeb {
14292daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
14392daf3a6SBjoern A. Zeeb 	char buf[64];
14492daf3a6SBjoern A. Zeeb 	int bufsz = sizeof(buf);
14592daf3a6SBjoern A. Zeeb 	int pos;
14692daf3a6SBjoern A. Zeeb 
14792daf3a6SBjoern A. Zeeb 	pos = scnprintf(buf, bufsz, "bss limit = %d\n",
14892daf3a6SBjoern A. Zeeb 			vif->bss_conf.txpower);
14992daf3a6SBjoern A. Zeeb 
15092daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
15192daf3a6SBjoern A. Zeeb }
15292daf3a6SBjoern A. Zeeb 
15392daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
15492daf3a6SBjoern A. Zeeb 					char __user *user_buf,
15592daf3a6SBjoern A. Zeeb 					size_t count, loff_t *ppos)
15692daf3a6SBjoern A. Zeeb {
15792daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
15892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
15992daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
16092daf3a6SBjoern A. Zeeb 	char buf[512];
16192daf3a6SBjoern A. Zeeb 	int bufsz = sizeof(buf);
16292daf3a6SBjoern A. Zeeb 	int pos;
16392daf3a6SBjoern A. Zeeb 
16492daf3a6SBjoern A. Zeeb 	pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz);
16592daf3a6SBjoern A. Zeeb 
16692daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
16792daf3a6SBjoern A. Zeeb }
16892daf3a6SBjoern A. Zeeb 
16992daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
17092daf3a6SBjoern A. Zeeb 					 char __user *user_buf,
17192daf3a6SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
17292daf3a6SBjoern A. Zeeb {
17392daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
17492daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
17592daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
17692daf3a6SBjoern A. Zeeb 	u8 ap_sta_id;
17792daf3a6SBjoern A. Zeeb 	struct ieee80211_chanctx_conf *chanctx_conf;
17892daf3a6SBjoern A. Zeeb 	char buf[512];
17992daf3a6SBjoern A. Zeeb 	int bufsz = sizeof(buf);
18092daf3a6SBjoern A. Zeeb 	int pos = 0;
18192daf3a6SBjoern A. Zeeb 	int i;
18292daf3a6SBjoern A. Zeeb 
18392daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
18492daf3a6SBjoern A. Zeeb 
18592daf3a6SBjoern A. Zeeb 	ap_sta_id = mvmvif->ap_sta_id;
18692daf3a6SBjoern A. Zeeb 
18792daf3a6SBjoern A. Zeeb 	switch (ieee80211_vif_type_p2p(vif)) {
18892daf3a6SBjoern A. Zeeb 	case NL80211_IFTYPE_ADHOC:
18992daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n");
19092daf3a6SBjoern A. Zeeb 		break;
19192daf3a6SBjoern A. Zeeb 	case NL80211_IFTYPE_STATION:
19292daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n");
19392daf3a6SBjoern A. Zeeb 		break;
19492daf3a6SBjoern A. Zeeb 	case NL80211_IFTYPE_AP:
19592daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n");
19692daf3a6SBjoern A. Zeeb 		break;
19792daf3a6SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_CLIENT:
19892daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n");
19992daf3a6SBjoern A. Zeeb 		break;
20092daf3a6SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_GO:
20192daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n");
20292daf3a6SBjoern A. Zeeb 		break;
20392daf3a6SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_DEVICE:
20492daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n");
20592daf3a6SBjoern A. Zeeb 		break;
20692daf3a6SBjoern A. Zeeb 	default:
20792daf3a6SBjoern A. Zeeb 		break;
20892daf3a6SBjoern A. Zeeb 	}
20992daf3a6SBjoern A. Zeeb 
21092daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n",
21192daf3a6SBjoern A. Zeeb 			 mvmvif->id, mvmvif->color);
21292daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n",
21392daf3a6SBjoern A. Zeeb 			 vif->bss_conf.bssid);
21492daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "Load: %d\n",
21592daf3a6SBjoern A. Zeeb 			 mvm->tcm.result.load[mvmvif->id]);
21692daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n");
21792daf3a6SBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++)
21892daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos,
21992daf3a6SBjoern A. Zeeb 				 "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n",
22092daf3a6SBjoern A. Zeeb 				 i, mvmvif->queue_params[i].txop,
22192daf3a6SBjoern A. Zeeb 				 mvmvif->queue_params[i].cw_min,
22292daf3a6SBjoern A. Zeeb 				 mvmvif->queue_params[i].cw_max,
22392daf3a6SBjoern A. Zeeb 				 mvmvif->queue_params[i].aifs,
22492daf3a6SBjoern A. Zeeb 				 mvmvif->queue_params[i].uapsd);
22592daf3a6SBjoern A. Zeeb 
22692daf3a6SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_STATION &&
22792daf3a6SBjoern A. Zeeb 	    ap_sta_id != IWL_MVM_INVALID_STA) {
22892daf3a6SBjoern A. Zeeb 		struct iwl_mvm_sta *mvm_sta;
22992daf3a6SBjoern A. Zeeb 
23092daf3a6SBjoern A. Zeeb 		mvm_sta = iwl_mvm_sta_from_staid_protected(mvm, ap_sta_id);
23192daf3a6SBjoern A. Zeeb 		if (mvm_sta) {
23292daf3a6SBjoern A. Zeeb 			pos += scnprintf(buf+pos, bufsz-pos,
23392daf3a6SBjoern A. Zeeb 					 "ap_sta_id %d - reduced Tx power %d\n",
23492daf3a6SBjoern A. Zeeb 					 ap_sta_id,
23592daf3a6SBjoern A. Zeeb 					 mvm_sta->bt_reduced_txpower);
23692daf3a6SBjoern A. Zeeb 		}
23792daf3a6SBjoern A. Zeeb 	}
23892daf3a6SBjoern A. Zeeb 
23992daf3a6SBjoern A. Zeeb 	rcu_read_lock();
24092daf3a6SBjoern A. Zeeb 	chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf);
24192daf3a6SBjoern A. Zeeb 	if (chanctx_conf)
24292daf3a6SBjoern A. Zeeb 		pos += scnprintf(buf+pos, bufsz-pos,
24392daf3a6SBjoern A. Zeeb 				 "idle rx chains %d, active rx chains: %d\n",
24492daf3a6SBjoern A. Zeeb 				 chanctx_conf->rx_chains_static,
24592daf3a6SBjoern A. Zeeb 				 chanctx_conf->rx_chains_dynamic);
24692daf3a6SBjoern A. Zeeb 	rcu_read_unlock();
24792daf3a6SBjoern A. Zeeb 
24892daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
24992daf3a6SBjoern A. Zeeb 
25092daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
25192daf3a6SBjoern A. Zeeb }
25292daf3a6SBjoern A. Zeeb 
25392daf3a6SBjoern A. Zeeb static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
25492daf3a6SBjoern A. Zeeb 				enum iwl_dbgfs_bf_mask param, int value)
25592daf3a6SBjoern A. Zeeb {
25692daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
25792daf3a6SBjoern A. Zeeb 	struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
25892daf3a6SBjoern A. Zeeb 
25992daf3a6SBjoern A. Zeeb 	dbgfs_bf->mask |= param;
26092daf3a6SBjoern A. Zeeb 
26192daf3a6SBjoern A. Zeeb 	switch (param) {
26292daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_ENERGY_DELTA:
26392daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_energy_delta = value;
26492daf3a6SBjoern A. Zeeb 		break;
26592daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
26692daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_roaming_energy_delta = value;
26792daf3a6SBjoern A. Zeeb 		break;
26892daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_ROAMING_STATE:
26992daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_roaming_state = value;
27092daf3a6SBjoern A. Zeeb 		break;
27192daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
27292daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_temp_threshold = value;
27392daf3a6SBjoern A. Zeeb 		break;
27492daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
27592daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_temp_fast_filter = value;
27692daf3a6SBjoern A. Zeeb 		break;
27792daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
27892daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_temp_slow_filter = value;
27992daf3a6SBjoern A. Zeeb 		break;
28092daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
28192daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_enable_beacon_filter = value;
28292daf3a6SBjoern A. Zeeb 		break;
28392daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_DEBUG_FLAG:
28492daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_debug_flag = value;
28592daf3a6SBjoern A. Zeeb 		break;
28692daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BF_ESCAPE_TIMER:
28792daf3a6SBjoern A. Zeeb 		dbgfs_bf->bf_escape_timer = value;
28892daf3a6SBjoern A. Zeeb 		break;
28992daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
29092daf3a6SBjoern A. Zeeb 		dbgfs_bf->ba_enable_beacon_abort = value;
29192daf3a6SBjoern A. Zeeb 		break;
29292daf3a6SBjoern A. Zeeb 	case MVM_DEBUGFS_BA_ESCAPE_TIMER:
29392daf3a6SBjoern A. Zeeb 		dbgfs_bf->ba_escape_timer = value;
29492daf3a6SBjoern A. Zeeb 		break;
29592daf3a6SBjoern A. Zeeb 	}
29692daf3a6SBjoern A. Zeeb }
29792daf3a6SBjoern A. Zeeb 
29892daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
29992daf3a6SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
30092daf3a6SBjoern A. Zeeb {
30192daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
30292daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
30392daf3a6SBjoern A. Zeeb 	enum iwl_dbgfs_bf_mask param;
30492daf3a6SBjoern A. Zeeb 	int value, ret = 0;
30592daf3a6SBjoern A. Zeeb 
30692daf3a6SBjoern A. Zeeb 	if (!strncmp("bf_energy_delta=", buf, 16)) {
30792daf3a6SBjoern A. Zeeb 		if (sscanf(buf+16, "%d", &value) != 1)
30892daf3a6SBjoern A. Zeeb 			return -EINVAL;
30992daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_ENERGY_DELTA_MIN ||
31092daf3a6SBjoern A. Zeeb 		    value > IWL_BF_ENERGY_DELTA_MAX)
31192daf3a6SBjoern A. Zeeb 			return -EINVAL;
31292daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_ENERGY_DELTA;
31392daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
31492daf3a6SBjoern A. Zeeb 		if (sscanf(buf+24, "%d", &value) != 1)
31592daf3a6SBjoern A. Zeeb 			return -EINVAL;
31692daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
31792daf3a6SBjoern A. Zeeb 		    value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
31892daf3a6SBjoern A. Zeeb 			return -EINVAL;
31992daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
32092daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_roaming_state=", buf, 17)) {
32192daf3a6SBjoern A. Zeeb 		if (sscanf(buf+17, "%d", &value) != 1)
32292daf3a6SBjoern A. Zeeb 			return -EINVAL;
32392daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_ROAMING_STATE_MIN ||
32492daf3a6SBjoern A. Zeeb 		    value > IWL_BF_ROAMING_STATE_MAX)
32592daf3a6SBjoern A. Zeeb 			return -EINVAL;
32692daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_ROAMING_STATE;
32792daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_temp_threshold=", buf, 18)) {
32892daf3a6SBjoern A. Zeeb 		if (sscanf(buf+18, "%d", &value) != 1)
32992daf3a6SBjoern A. Zeeb 			return -EINVAL;
33092daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
33192daf3a6SBjoern A. Zeeb 		    value > IWL_BF_TEMP_THRESHOLD_MAX)
33292daf3a6SBjoern A. Zeeb 			return -EINVAL;
33392daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
33492daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
33592daf3a6SBjoern A. Zeeb 		if (sscanf(buf+20, "%d", &value) != 1)
33692daf3a6SBjoern A. Zeeb 			return -EINVAL;
33792daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
33892daf3a6SBjoern A. Zeeb 		    value > IWL_BF_TEMP_FAST_FILTER_MAX)
33992daf3a6SBjoern A. Zeeb 			return -EINVAL;
34092daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
34192daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
34292daf3a6SBjoern A. Zeeb 		if (sscanf(buf+20, "%d", &value) != 1)
34392daf3a6SBjoern A. Zeeb 			return -EINVAL;
34492daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
34592daf3a6SBjoern A. Zeeb 		    value > IWL_BF_TEMP_SLOW_FILTER_MAX)
34692daf3a6SBjoern A. Zeeb 			return -EINVAL;
34792daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
34892daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
34992daf3a6SBjoern A. Zeeb 		if (sscanf(buf+24, "%d", &value) != 1)
35092daf3a6SBjoern A. Zeeb 			return -EINVAL;
35192daf3a6SBjoern A. Zeeb 		if (value < 0 || value > 1)
35292daf3a6SBjoern A. Zeeb 			return -EINVAL;
35392daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
35492daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_debug_flag=", buf, 14)) {
35592daf3a6SBjoern A. Zeeb 		if (sscanf(buf+14, "%d", &value) != 1)
35692daf3a6SBjoern A. Zeeb 			return -EINVAL;
35792daf3a6SBjoern A. Zeeb 		if (value < 0 || value > 1)
35892daf3a6SBjoern A. Zeeb 			return -EINVAL;
35992daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_DEBUG_FLAG;
36092daf3a6SBjoern A. Zeeb 	} else if (!strncmp("bf_escape_timer=", buf, 16)) {
36192daf3a6SBjoern A. Zeeb 		if (sscanf(buf+16, "%d", &value) != 1)
36292daf3a6SBjoern A. Zeeb 			return -EINVAL;
36392daf3a6SBjoern A. Zeeb 		if (value < IWL_BF_ESCAPE_TIMER_MIN ||
36492daf3a6SBjoern A. Zeeb 		    value > IWL_BF_ESCAPE_TIMER_MAX)
36592daf3a6SBjoern A. Zeeb 			return -EINVAL;
36692daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
36792daf3a6SBjoern A. Zeeb 	} else if (!strncmp("ba_escape_timer=", buf, 16)) {
36892daf3a6SBjoern A. Zeeb 		if (sscanf(buf+16, "%d", &value) != 1)
36992daf3a6SBjoern A. Zeeb 			return -EINVAL;
37092daf3a6SBjoern A. Zeeb 		if (value < IWL_BA_ESCAPE_TIMER_MIN ||
37192daf3a6SBjoern A. Zeeb 		    value > IWL_BA_ESCAPE_TIMER_MAX)
37292daf3a6SBjoern A. Zeeb 			return -EINVAL;
37392daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
37492daf3a6SBjoern A. Zeeb 	} else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
37592daf3a6SBjoern A. Zeeb 		if (sscanf(buf+23, "%d", &value) != 1)
37692daf3a6SBjoern A. Zeeb 			return -EINVAL;
37792daf3a6SBjoern A. Zeeb 		if (value < 0 || value > 1)
37892daf3a6SBjoern A. Zeeb 			return -EINVAL;
37992daf3a6SBjoern A. Zeeb 		param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
38092daf3a6SBjoern A. Zeeb 	} else {
38192daf3a6SBjoern A. Zeeb 		return -EINVAL;
38292daf3a6SBjoern A. Zeeb 	}
38392daf3a6SBjoern A. Zeeb 
38492daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
38592daf3a6SBjoern A. Zeeb 	iwl_dbgfs_update_bf(vif, param, value);
38692daf3a6SBjoern A. Zeeb 	if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
38792daf3a6SBjoern A. Zeeb 		ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
38892daf3a6SBjoern A. Zeeb 	else
38992daf3a6SBjoern A. Zeeb 		ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
39092daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
39192daf3a6SBjoern A. Zeeb 
39292daf3a6SBjoern A. Zeeb 	return ret ?: count;
39392daf3a6SBjoern A. Zeeb }
39492daf3a6SBjoern A. Zeeb 
39592daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
39692daf3a6SBjoern A. Zeeb 					char __user *user_buf,
39792daf3a6SBjoern A. Zeeb 					size_t count, loff_t *ppos)
39892daf3a6SBjoern A. Zeeb {
39992daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
40092daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
40192daf3a6SBjoern A. Zeeb 	char buf[256];
40292daf3a6SBjoern A. Zeeb 	int pos = 0;
40392daf3a6SBjoern A. Zeeb 	const size_t bufsz = sizeof(buf);
40492daf3a6SBjoern A. Zeeb 	struct iwl_beacon_filter_cmd cmd = {
40592daf3a6SBjoern A. Zeeb 		IWL_BF_CMD_CONFIG_DEFAULTS,
40692daf3a6SBjoern A. Zeeb 		.bf_enable_beacon_filter =
40792daf3a6SBjoern A. Zeeb 			cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
40892daf3a6SBjoern A. Zeeb 		.ba_enable_beacon_abort =
40992daf3a6SBjoern A. Zeeb 			cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
41092daf3a6SBjoern A. Zeeb 	};
41192daf3a6SBjoern A. Zeeb 
41292daf3a6SBjoern A. Zeeb 	iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
41392daf3a6SBjoern A. Zeeb 	if (mvmvif->bf_data.bf_enabled)
41492daf3a6SBjoern A. Zeeb 		cmd.bf_enable_beacon_filter = cpu_to_le32(1);
41592daf3a6SBjoern A. Zeeb 	else
41692daf3a6SBjoern A. Zeeb 		cmd.bf_enable_beacon_filter = 0;
41792daf3a6SBjoern A. Zeeb 
41892daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
41992daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_energy_delta));
42092daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
42192daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_roaming_energy_delta));
42292daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
42392daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_roaming_state));
42492daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
42592daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_temp_threshold));
42692daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
42792daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_temp_fast_filter));
42892daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
42992daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_temp_slow_filter));
43092daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
43192daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_enable_beacon_filter));
43292daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
43392daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_debug_flag));
43492daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
43592daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.bf_escape_timer));
43692daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
43792daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.ba_escape_timer));
43892daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
43992daf3a6SBjoern A. Zeeb 			 le32_to_cpu(cmd.ba_enable_beacon_abort));
44092daf3a6SBjoern A. Zeeb 
44192daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
44292daf3a6SBjoern A. Zeeb }
44392daf3a6SBjoern A. Zeeb 
44492daf3a6SBjoern A. Zeeb #if defined(__linux__)
44592daf3a6SBjoern A. Zeeb static inline char *iwl_dbgfs_is_match(char *name, char *buf)
44692daf3a6SBjoern A. Zeeb {
44792daf3a6SBjoern A. Zeeb 	int len = strlen(name);
44892daf3a6SBjoern A. Zeeb 
44992daf3a6SBjoern A. Zeeb 	return !strncmp(name, buf, len) ? buf + len : NULL;
45092daf3a6SBjoern A. Zeeb }
45192daf3a6SBjoern A. Zeeb #endif
45292daf3a6SBjoern A. Zeeb 
45392daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_os_device_timediff_read(struct file *file,
45492daf3a6SBjoern A. Zeeb 						 char __user *user_buf,
45592daf3a6SBjoern A. Zeeb 						 size_t count, loff_t *ppos)
45692daf3a6SBjoern A. Zeeb {
45792daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
45892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
45992daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
46092daf3a6SBjoern A. Zeeb 	u32 curr_gp2;
46192daf3a6SBjoern A. Zeeb 	u64 curr_os;
46292daf3a6SBjoern A. Zeeb 	s64 diff;
46392daf3a6SBjoern A. Zeeb 	char buf[64];
46492daf3a6SBjoern A. Zeeb 	const size_t bufsz = sizeof(buf);
46592daf3a6SBjoern A. Zeeb 	int pos = 0;
46692daf3a6SBjoern A. Zeeb 
46792daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
46892daf3a6SBjoern A. Zeeb 	iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2, &curr_os, NULL);
46992daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
47092daf3a6SBjoern A. Zeeb 
47192daf3a6SBjoern A. Zeeb 	do_div(curr_os, NSEC_PER_USEC);
47292daf3a6SBjoern A. Zeeb 	diff = curr_os - curr_gp2;
47392daf3a6SBjoern A. Zeeb 	pos += scnprintf(buf + pos, bufsz - pos, "diff=%lld\n", diff);
47492daf3a6SBjoern A. Zeeb 
47592daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
47692daf3a6SBjoern A. Zeeb }
47792daf3a6SBjoern A. Zeeb 
47892daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf,
47992daf3a6SBjoern A. Zeeb 					   size_t count, loff_t *ppos)
48092daf3a6SBjoern A. Zeeb {
48192daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
48292daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
48392daf3a6SBjoern A. Zeeb 	u8 value;
48492daf3a6SBjoern A. Zeeb 	int ret;
48592daf3a6SBjoern A. Zeeb 
48692daf3a6SBjoern A. Zeeb 	ret = kstrtou8(buf, 0, &value);
48792daf3a6SBjoern A. Zeeb 	if (ret)
48892daf3a6SBjoern A. Zeeb 		return ret;
48992daf3a6SBjoern A. Zeeb 	if (value > 1)
49092daf3a6SBjoern A. Zeeb 		return -EINVAL;
49192daf3a6SBjoern A. Zeeb 
49292daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
49392daf3a6SBjoern A. Zeeb 	iwl_mvm_update_low_latency(mvm, vif, value, LOW_LATENCY_DEBUGFS);
49492daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
49592daf3a6SBjoern A. Zeeb 
49692daf3a6SBjoern A. Zeeb 	return count;
49792daf3a6SBjoern A. Zeeb }
49892daf3a6SBjoern A. Zeeb 
49992daf3a6SBjoern A. Zeeb static ssize_t
50092daf3a6SBjoern A. Zeeb iwl_dbgfs_low_latency_force_write(struct ieee80211_vif *vif, char *buf,
50192daf3a6SBjoern A. Zeeb 				  size_t count, loff_t *ppos)
50292daf3a6SBjoern A. Zeeb {
50392daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
50492daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
50592daf3a6SBjoern A. Zeeb 	u8 value;
50692daf3a6SBjoern A. Zeeb 	int ret;
50792daf3a6SBjoern A. Zeeb 
50892daf3a6SBjoern A. Zeeb 	ret = kstrtou8(buf, 0, &value);
50992daf3a6SBjoern A. Zeeb 	if (ret)
51092daf3a6SBjoern A. Zeeb 		return ret;
51192daf3a6SBjoern A. Zeeb 
51292daf3a6SBjoern A. Zeeb 	if (value > NUM_LOW_LATENCY_FORCE)
51392daf3a6SBjoern A. Zeeb 		return -EINVAL;
51492daf3a6SBjoern A. Zeeb 
51592daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
51692daf3a6SBjoern A. Zeeb 	if (value == LOW_LATENCY_FORCE_UNSET) {
51792daf3a6SBjoern A. Zeeb 		iwl_mvm_update_low_latency(mvm, vif, false,
51892daf3a6SBjoern A. Zeeb 					   LOW_LATENCY_DEBUGFS_FORCE);
51992daf3a6SBjoern A. Zeeb 		iwl_mvm_update_low_latency(mvm, vif, false,
52092daf3a6SBjoern A. Zeeb 					   LOW_LATENCY_DEBUGFS_FORCE_ENABLE);
52192daf3a6SBjoern A. Zeeb 	} else {
52292daf3a6SBjoern A. Zeeb 		iwl_mvm_update_low_latency(mvm, vif,
52392daf3a6SBjoern A. Zeeb 					   value == LOW_LATENCY_FORCE_ON,
52492daf3a6SBjoern A. Zeeb 					   LOW_LATENCY_DEBUGFS_FORCE);
52592daf3a6SBjoern A. Zeeb 		iwl_mvm_update_low_latency(mvm, vif, true,
52692daf3a6SBjoern A. Zeeb 					   LOW_LATENCY_DEBUGFS_FORCE_ENABLE);
52792daf3a6SBjoern A. Zeeb 	}
52892daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
52992daf3a6SBjoern A. Zeeb 	return count;
53092daf3a6SBjoern A. Zeeb }
53192daf3a6SBjoern A. Zeeb 
53292daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_low_latency_read(struct file *file,
53392daf3a6SBjoern A. Zeeb 					  char __user *user_buf,
53492daf3a6SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
53592daf3a6SBjoern A. Zeeb {
53692daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
53792daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
53892daf3a6SBjoern A. Zeeb 	char format[] = "traffic=%d\ndbgfs=%d\nvcmd=%d\nvif_type=%d\n"
53992daf3a6SBjoern A. Zeeb 			"dbgfs_force_enable=%d\ndbgfs_force=%d\nactual=%d\n";
54092daf3a6SBjoern A. Zeeb 
54192daf3a6SBjoern A. Zeeb 	/*
54292daf3a6SBjoern A. Zeeb 	 * all values in format are boolean so the size of format is enough
54392daf3a6SBjoern A. Zeeb 	 * for holding the result string
54492daf3a6SBjoern A. Zeeb 	 */
54592daf3a6SBjoern A. Zeeb 	char buf[sizeof(format) + 1] = {};
54692daf3a6SBjoern A. Zeeb 	int len;
54792daf3a6SBjoern A. Zeeb 
54892daf3a6SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf) - 1, format,
54992daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency & LOW_LATENCY_TRAFFIC),
55092daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS),
55192daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency & LOW_LATENCY_VCMD),
55292daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency & LOW_LATENCY_VIF_TYPE),
55392daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency &
55492daf3a6SBjoern A. Zeeb 			   LOW_LATENCY_DEBUGFS_FORCE_ENABLE),
55592daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency & LOW_LATENCY_DEBUGFS_FORCE),
55692daf3a6SBjoern A. Zeeb 			!!(mvmvif->low_latency_actual));
55792daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
55892daf3a6SBjoern A. Zeeb }
55992daf3a6SBjoern A. Zeeb 
56092daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file,
56192daf3a6SBjoern A. Zeeb 						char __user *user_buf,
56292daf3a6SBjoern A. Zeeb 						size_t count, loff_t *ppos)
56392daf3a6SBjoern A. Zeeb {
56492daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
56592daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
56692daf3a6SBjoern A. Zeeb 	char buf[20];
56792daf3a6SBjoern A. Zeeb 	int len;
56892daf3a6SBjoern A. Zeeb 
56992daf3a6SBjoern A. Zeeb #if defined(__linux__)
57092daf3a6SBjoern A. Zeeb 	len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid);
57192daf3a6SBjoern A. Zeeb #elif defined(__FreeBSD__)
57292daf3a6SBjoern A. Zeeb 	len = sprintf(buf, "%6D\n", mvmvif->uapsd_misbehaving_bssid, ":");
57392daf3a6SBjoern A. Zeeb #endif
57492daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
57592daf3a6SBjoern A. Zeeb }
57692daf3a6SBjoern A. Zeeb 
57792daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif,
57892daf3a6SBjoern A. Zeeb 						 char *buf, size_t count,
57992daf3a6SBjoern A. Zeeb 						 loff_t *ppos)
58092daf3a6SBjoern A. Zeeb {
58192daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
58292daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
58392daf3a6SBjoern A. Zeeb 	bool ret;
58492daf3a6SBjoern A. Zeeb 
58592daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
58692daf3a6SBjoern A. Zeeb 	ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid);
58792daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
58892daf3a6SBjoern A. Zeeb 
58992daf3a6SBjoern A. Zeeb 	return ret ? count : -EINVAL;
59092daf3a6SBjoern A. Zeeb }
59192daf3a6SBjoern A. Zeeb 
59292daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf,
59392daf3a6SBjoern A. Zeeb 					  size_t count, loff_t *ppos)
59492daf3a6SBjoern A. Zeeb {
59592daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
59692daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
59792daf3a6SBjoern A. Zeeb 	struct ieee80211_chanctx_conf *chanctx_conf;
59892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_phy_ctxt *phy_ctxt;
59992daf3a6SBjoern A. Zeeb 	u16 value;
60092daf3a6SBjoern A. Zeeb 	int ret;
60192daf3a6SBjoern A. Zeeb 
60292daf3a6SBjoern A. Zeeb 	ret = kstrtou16(buf, 0, &value);
60392daf3a6SBjoern A. Zeeb 	if (ret)
60492daf3a6SBjoern A. Zeeb 		return ret;
60592daf3a6SBjoern A. Zeeb 
60692daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
60792daf3a6SBjoern A. Zeeb 	rcu_read_lock();
60892daf3a6SBjoern A. Zeeb 
60992daf3a6SBjoern A. Zeeb 	chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf);
61092daf3a6SBjoern A. Zeeb 	/* make sure the channel context is assigned */
61192daf3a6SBjoern A. Zeeb 	if (!chanctx_conf) {
61292daf3a6SBjoern A. Zeeb 		rcu_read_unlock();
61392daf3a6SBjoern A. Zeeb 		mutex_unlock(&mvm->mutex);
61492daf3a6SBjoern A. Zeeb 		return -EINVAL;
61592daf3a6SBjoern A. Zeeb 	}
61692daf3a6SBjoern A. Zeeb 
61792daf3a6SBjoern A. Zeeb 	phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
61892daf3a6SBjoern A. Zeeb 	rcu_read_unlock();
61992daf3a6SBjoern A. Zeeb 
62092daf3a6SBjoern A. Zeeb 	mvm->dbgfs_rx_phyinfo = value;
62192daf3a6SBjoern A. Zeeb 
62292daf3a6SBjoern A. Zeeb 	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
62392daf3a6SBjoern A. Zeeb 				       chanctx_conf->rx_chains_static,
62492daf3a6SBjoern A. Zeeb 				       chanctx_conf->rx_chains_dynamic);
62592daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
62692daf3a6SBjoern A. Zeeb 
62792daf3a6SBjoern A. Zeeb 	return ret ?: count;
62892daf3a6SBjoern A. Zeeb }
62992daf3a6SBjoern A. Zeeb 
63092daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
63192daf3a6SBjoern A. Zeeb 					 char __user *user_buf,
63292daf3a6SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
63392daf3a6SBjoern A. Zeeb {
63492daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
63592daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
63692daf3a6SBjoern A. Zeeb 	char buf[8];
63792daf3a6SBjoern A. Zeeb 	int len;
63892daf3a6SBjoern A. Zeeb 
63992daf3a6SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "0x%04x\n",
64092daf3a6SBjoern A. Zeeb 			mvmvif->mvm->dbgfs_rx_phyinfo);
64192daf3a6SBjoern A. Zeeb 
64292daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
64392daf3a6SBjoern A. Zeeb }
64492daf3a6SBjoern A. Zeeb 
64592daf3a6SBjoern A. Zeeb static void iwl_dbgfs_quota_check(void *data, u8 *mac,
64692daf3a6SBjoern A. Zeeb 				  struct ieee80211_vif *vif)
64792daf3a6SBjoern A. Zeeb {
64892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
64992daf3a6SBjoern A. Zeeb 	int *ret = data;
65092daf3a6SBjoern A. Zeeb 
65192daf3a6SBjoern A. Zeeb 	if (mvmvif->dbgfs_quota_min)
65292daf3a6SBjoern A. Zeeb 		*ret = -EINVAL;
65392daf3a6SBjoern A. Zeeb }
65492daf3a6SBjoern A. Zeeb 
65592daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_quota_min_write(struct ieee80211_vif *vif, char *buf,
65692daf3a6SBjoern A. Zeeb 					 size_t count, loff_t *ppos)
65792daf3a6SBjoern A. Zeeb {
65892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
65992daf3a6SBjoern A. Zeeb 	struct iwl_mvm *mvm = mvmvif->mvm;
66092daf3a6SBjoern A. Zeeb 	u16 value;
66192daf3a6SBjoern A. Zeeb 	int ret;
66292daf3a6SBjoern A. Zeeb 
66392daf3a6SBjoern A. Zeeb 	ret = kstrtou16(buf, 0, &value);
66492daf3a6SBjoern A. Zeeb 	if (ret)
66592daf3a6SBjoern A. Zeeb 		return ret;
66692daf3a6SBjoern A. Zeeb 
66792daf3a6SBjoern A. Zeeb 	if (value > 95)
66892daf3a6SBjoern A. Zeeb 		return -EINVAL;
66992daf3a6SBjoern A. Zeeb 
67092daf3a6SBjoern A. Zeeb 	mutex_lock(&mvm->mutex);
67192daf3a6SBjoern A. Zeeb 
67292daf3a6SBjoern A. Zeeb 	mvmvif->dbgfs_quota_min = 0;
67392daf3a6SBjoern A. Zeeb 	ieee80211_iterate_interfaces(mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
67492daf3a6SBjoern A. Zeeb 				     iwl_dbgfs_quota_check, &ret);
67592daf3a6SBjoern A. Zeeb 	if (ret == 0) {
67692daf3a6SBjoern A. Zeeb 		mvmvif->dbgfs_quota_min = value;
67792daf3a6SBjoern A. Zeeb 		iwl_mvm_update_quotas(mvm, false, NULL);
67892daf3a6SBjoern A. Zeeb 	}
67992daf3a6SBjoern A. Zeeb 	mutex_unlock(&mvm->mutex);
68092daf3a6SBjoern A. Zeeb 
68192daf3a6SBjoern A. Zeeb 	return ret ?: count;
68292daf3a6SBjoern A. Zeeb }
68392daf3a6SBjoern A. Zeeb 
68492daf3a6SBjoern A. Zeeb static ssize_t iwl_dbgfs_quota_min_read(struct file *file,
68592daf3a6SBjoern A. Zeeb 					char __user *user_buf,
68692daf3a6SBjoern A. Zeeb 					size_t count, loff_t *ppos)
68792daf3a6SBjoern A. Zeeb {
68892daf3a6SBjoern A. Zeeb 	struct ieee80211_vif *vif = file->private_data;
68992daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
69092daf3a6SBjoern A. Zeeb 	char buf[10];
69192daf3a6SBjoern A. Zeeb 	int len;
69292daf3a6SBjoern A. Zeeb 
69392daf3a6SBjoern A. Zeeb 	len = scnprintf(buf, sizeof(buf), "%d\n", mvmvif->dbgfs_quota_min);
69492daf3a6SBjoern A. Zeeb 
69592daf3a6SBjoern A. Zeeb 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
69692daf3a6SBjoern A. Zeeb }
69792daf3a6SBjoern A. Zeeb 
69892daf3a6SBjoern A. Zeeb #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
69992daf3a6SBjoern A. Zeeb 	_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
70092daf3a6SBjoern A. Zeeb #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
70192daf3a6SBjoern A. Zeeb 	_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
70292daf3a6SBjoern A. Zeeb #define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {		\
70392daf3a6SBjoern A. Zeeb 		debugfs_create_file(#name, mode, parent, vif,		\
70492daf3a6SBjoern A. Zeeb 				    &iwl_dbgfs_##name##_ops);		\
70592daf3a6SBjoern A. Zeeb 	} while (0)
70692daf3a6SBjoern A. Zeeb 
70792daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_FILE_OPS(mac_params);
70892daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt);
70992daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
71092daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
71192daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
71292daf3a6SBjoern A. Zeeb MVM_DEBUGFS_WRITE_FILE_OPS(low_latency_force, 10);
71392daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
71492daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
71592daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_WRITE_FILE_OPS(quota_min, 32);
71692daf3a6SBjoern A. Zeeb MVM_DEBUGFS_READ_FILE_OPS(os_device_timediff);
71792daf3a6SBjoern A. Zeeb 
71892daf3a6SBjoern A. Zeeb 
71992daf3a6SBjoern A. Zeeb void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
72092daf3a6SBjoern A. Zeeb {
72192daf3a6SBjoern A. Zeeb 	struct dentry *dbgfs_dir = vif->debugfs_dir;
72292daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
72392daf3a6SBjoern A. Zeeb #if defined(__linux__)
72492daf3a6SBjoern A. Zeeb 	char buf[100];
72592daf3a6SBjoern A. Zeeb #endif
72692daf3a6SBjoern A. Zeeb 
72792daf3a6SBjoern A. Zeeb 	/*
72892daf3a6SBjoern A. Zeeb 	 * Check if debugfs directory already exist before creating it.
72992daf3a6SBjoern A. Zeeb 	 * This may happen when, for example, resetting hw or suspend-resume
73092daf3a6SBjoern A. Zeeb 	 */
73192daf3a6SBjoern A. Zeeb 	if (!dbgfs_dir || mvmvif->dbgfs_dir)
73292daf3a6SBjoern A. Zeeb 		return;
73392daf3a6SBjoern A. Zeeb 
73492daf3a6SBjoern A. Zeeb 	mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir);
73592daf3a6SBjoern A. Zeeb 	if (IS_ERR_OR_NULL(mvmvif->dbgfs_dir)) {
73692daf3a6SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to create debugfs directory under %pd\n",
73792daf3a6SBjoern A. Zeeb 			dbgfs_dir);
73892daf3a6SBjoern A. Zeeb 		return;
73992daf3a6SBjoern A. Zeeb 	}
74092daf3a6SBjoern A. Zeeb 
74192daf3a6SBjoern A. Zeeb 	if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
74292daf3a6SBjoern A. Zeeb 	    ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
74392daf3a6SBjoern A. Zeeb 	     (vif->type == NL80211_IFTYPE_STATION && vif->p2p)))
74492daf3a6SBjoern A. Zeeb 		MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, 0600);
74592daf3a6SBjoern A. Zeeb 
74692daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, 0400);
74792daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, 0400);
74892daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, 0600);
74992daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(low_latency_force, mvmvif->dbgfs_dir, 0600);
75092daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, 0600);
75192daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, 0600);
75292daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(quota_min, mvmvif->dbgfs_dir, 0600);
75392daf3a6SBjoern A. Zeeb 	MVM_DEBUGFS_ADD_FILE_VIF(os_device_timediff, mvmvif->dbgfs_dir, 0400);
75492daf3a6SBjoern A. Zeeb 
75592daf3a6SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
75692daf3a6SBjoern A. Zeeb 	    mvmvif == mvm->bf_allowed_vif)
75792daf3a6SBjoern A. Zeeb 		MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, 0600);
75892daf3a6SBjoern A. Zeeb 
75992daf3a6SBjoern A. Zeeb #if defined(__linux__)
76092daf3a6SBjoern A. Zeeb 	/*
76192daf3a6SBjoern A. Zeeb 	 * Create symlink for convenience pointing to interface specific
76292daf3a6SBjoern A. Zeeb 	 * debugfs entries for the driver. For example, under
76392daf3a6SBjoern A. Zeeb 	 * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/
76492daf3a6SBjoern A. Zeeb 	 * find
76592daf3a6SBjoern A. Zeeb 	 * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/
76692daf3a6SBjoern A. Zeeb 	 */
76792daf3a6SBjoern A. Zeeb 	snprintf(buf, 100, "../../../%pd3/%pd",
76892daf3a6SBjoern A. Zeeb 		 dbgfs_dir,
76992daf3a6SBjoern A. Zeeb 		 mvmvif->dbgfs_dir);
77092daf3a6SBjoern A. Zeeb 
77192daf3a6SBjoern A. Zeeb 	mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name,
77292daf3a6SBjoern A. Zeeb 						     mvm->debugfs_dir, buf);
77392daf3a6SBjoern A. Zeeb #endif
77492daf3a6SBjoern A. Zeeb }
77592daf3a6SBjoern A. Zeeb 
77692daf3a6SBjoern A. Zeeb void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
77792daf3a6SBjoern A. Zeeb {
77892daf3a6SBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
77992daf3a6SBjoern A. Zeeb 
78092daf3a6SBjoern A. Zeeb 	debugfs_remove(mvmvif->dbgfs_slink);
78192daf3a6SBjoern A. Zeeb 	mvmvif->dbgfs_slink = NULL;
78292daf3a6SBjoern A. Zeeb 
78392daf3a6SBjoern A. Zeeb 	debugfs_remove_recursive(mvmvif->dbgfs_dir);
78492daf3a6SBjoern A. Zeeb 	mvmvif->dbgfs_dir = NULL;
78592daf3a6SBjoern A. Zeeb }
786