xref: /linux/net/mac80211/debugfs.c (revision f5baf287f5da5641099ad5c809b3b4ebfc08506d)
17f904d7eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e9f207f0SJiri Benc /*
3e9f207f0SJiri Benc  * mac80211 debugfs for wireless PHYs
4e9f207f0SJiri Benc  *
5e9f207f0SJiri Benc  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
6d98ad83eSJohannes Berg  * Copyright 2013-2014  Intel Mobile Communications GmbH
7adaed1b9SJohannes Berg  * Copyright (C) 2018 - 2019, 2021 Intel Corporation
8e9f207f0SJiri Benc  */
9e9f207f0SJiri Benc 
10e9f207f0SJiri Benc #include <linux/debugfs.h>
11e9f207f0SJiri Benc #include <linux/rtnetlink.h>
129399b86cSMichal Kazior #include <linux/vmalloc.h>
13e9f207f0SJiri Benc #include "ieee80211_i.h"
1424487981SJohannes Berg #include "driver-ops.h"
152c8dccc7SJohannes Berg #include "rate.h"
16e9f207f0SJiri Benc #include "debugfs.h"
17e9f207f0SJiri Benc 
1807caf9d6SEliad Peller #define DEBUGFS_FORMAT_BUFFER_SIZE 100
1907caf9d6SEliad Peller 
2007caf9d6SEliad Peller int mac80211_format_buffer(char __user *userbuf, size_t count,
2107caf9d6SEliad Peller 				  loff_t *ppos, char *fmt, ...)
2207caf9d6SEliad Peller {
2307caf9d6SEliad Peller 	va_list args;
2407caf9d6SEliad Peller 	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
2507caf9d6SEliad Peller 	int res;
2607caf9d6SEliad Peller 
2707caf9d6SEliad Peller 	va_start(args, fmt);
2807caf9d6SEliad Peller 	res = vscnprintf(buf, sizeof(buf), fmt, args);
2907caf9d6SEliad Peller 	va_end(args);
3007caf9d6SEliad Peller 
3107caf9d6SEliad Peller 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
3207caf9d6SEliad Peller }
3307caf9d6SEliad Peller 
34279daf64SBen Greear #define DEBUGFS_READONLY_FILE_FN(name, fmt, value...)			\
35e9f207f0SJiri Benc static ssize_t name## _read(struct file *file, char __user *userbuf,	\
36e9f207f0SJiri Benc 			    size_t count, loff_t *ppos)			\
37e9f207f0SJiri Benc {									\
38e9f207f0SJiri Benc 	struct ieee80211_local *local = file->private_data;		\
39e9f207f0SJiri Benc 									\
4007caf9d6SEliad Peller 	return mac80211_format_buffer(userbuf, count, ppos, 		\
4107caf9d6SEliad Peller 				      fmt "\n", ##value);		\
42279daf64SBen Greear }
43279daf64SBen Greear 
44279daf64SBen Greear #define DEBUGFS_READONLY_FILE_OPS(name)			\
45e9f207f0SJiri Benc static const struct file_operations name## _ops = {			\
46e9f207f0SJiri Benc 	.read = name## _read,						\
47234e3405SStephen Boyd 	.open = simple_open,						\
482b18ab36SArnd Bergmann 	.llseek = generic_file_llseek,					\
49e9f207f0SJiri Benc };
50e9f207f0SJiri Benc 
51279daf64SBen Greear #define DEBUGFS_READONLY_FILE(name, fmt, value...)		\
52279daf64SBen Greear 	DEBUGFS_READONLY_FILE_FN(name, fmt, value)		\
53279daf64SBen Greear 	DEBUGFS_READONLY_FILE_OPS(name)
54279daf64SBen Greear 
55e9f207f0SJiri Benc #define DEBUGFS_ADD(name)						\
5684674ef4STom Rix 	debugfs_create_file(#name, 0400, phyd, local, &name## _ops)
57e9f207f0SJiri Benc 
58827b1fb4SJohannes Berg #define DEBUGFS_ADD_MODE(name, mode)					\
597bcfaf2fSJohannes Berg 	debugfs_create_file(#name, mode, phyd, local, &name## _ops);
60e9f207f0SJiri Benc 
61e9f207f0SJiri Benc 
62c90142a5SThomas Pedersen DEBUGFS_READONLY_FILE(hw_conf, "%x",
63c90142a5SThomas Pedersen 		      local->hw.conf.flags);
6483bdf2a1SBen Greear DEBUGFS_READONLY_FILE(user_power, "%d",
6583bdf2a1SBen Greear 		      local->user_power_level);
6683bdf2a1SBen Greear DEBUGFS_READONLY_FILE(power, "%d",
6783bdf2a1SBen Greear 		      local->hw.conf.power_level);
6807caf9d6SEliad Peller DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",
69e9f207f0SJiri Benc 		      local->total_ps_buffered);
7007caf9d6SEliad Peller DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
71e9f207f0SJiri Benc 		      local->wep_iv & 0xffffff);
7207caf9d6SEliad Peller DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
73af65cd96SJohannes Berg 	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
743b5d665bSAlina Friedrichsen 
758d51dbb8SToke Høiland-Jørgensen static ssize_t aqm_read(struct file *file,
768d51dbb8SToke Høiland-Jørgensen 			char __user *user_buf,
778d51dbb8SToke Høiland-Jørgensen 			size_t count,
788d51dbb8SToke Høiland-Jørgensen 			loff_t *ppos)
799399b86cSMichal Kazior {
808d51dbb8SToke Høiland-Jørgensen 	struct ieee80211_local *local = file->private_data;
819399b86cSMichal Kazior 	struct fq *fq = &local->fq;
828d51dbb8SToke Høiland-Jørgensen 	char buf[200];
839399b86cSMichal Kazior 	int len = 0;
849399b86cSMichal Kazior 
859399b86cSMichal Kazior 	spin_lock_bh(&local->fq.lock);
869399b86cSMichal Kazior 	rcu_read_lock();
879399b86cSMichal Kazior 
888d51dbb8SToke Høiland-Jørgensen 	len = scnprintf(buf, sizeof(buf),
899399b86cSMichal Kazior 			"access name value\n"
909399b86cSMichal Kazior 			"R fq_flows_cnt %u\n"
919399b86cSMichal Kazior 			"R fq_backlog %u\n"
929399b86cSMichal Kazior 			"R fq_overlimit %u\n"
932a4e675dSToke Høiland-Jørgensen 			"R fq_overmemory %u\n"
949399b86cSMichal Kazior 			"R fq_collisions %u\n"
952a4e675dSToke Høiland-Jørgensen 			"R fq_memory_usage %u\n"
962a4e675dSToke Høiland-Jørgensen 			"RW fq_memory_limit %u\n"
979399b86cSMichal Kazior 			"RW fq_limit %u\n"
989399b86cSMichal Kazior 			"RW fq_quantum %u\n",
999399b86cSMichal Kazior 			fq->flows_cnt,
1009399b86cSMichal Kazior 			fq->backlog,
1012a4e675dSToke Høiland-Jørgensen 			fq->overmemory,
1029399b86cSMichal Kazior 			fq->overlimit,
1039399b86cSMichal Kazior 			fq->collisions,
1042a4e675dSToke Høiland-Jørgensen 			fq->memory_usage,
1052a4e675dSToke Høiland-Jørgensen 			fq->memory_limit,
1069399b86cSMichal Kazior 			fq->limit,
1079399b86cSMichal Kazior 			fq->quantum);
1089399b86cSMichal Kazior 
1099399b86cSMichal Kazior 	rcu_read_unlock();
1109399b86cSMichal Kazior 	spin_unlock_bh(&local->fq.lock);
1119399b86cSMichal Kazior 
1129399b86cSMichal Kazior 	return simple_read_from_buffer(user_buf, count, ppos,
1138d51dbb8SToke Høiland-Jørgensen 				       buf, len);
1149399b86cSMichal Kazior }
1159399b86cSMichal Kazior 
1169399b86cSMichal Kazior static ssize_t aqm_write(struct file *file,
1179399b86cSMichal Kazior 			 const char __user *user_buf,
1189399b86cSMichal Kazior 			 size_t count,
1199399b86cSMichal Kazior 			 loff_t *ppos)
1209399b86cSMichal Kazior {
1218d51dbb8SToke Høiland-Jørgensen 	struct ieee80211_local *local = file->private_data;
1229399b86cSMichal Kazior 	char buf[100];
1239399b86cSMichal Kazior 
1246020d534SShayne Chen 	if (count >= sizeof(buf))
1259399b86cSMichal Kazior 		return -EINVAL;
1269399b86cSMichal Kazior 
1279399b86cSMichal Kazior 	if (copy_from_user(buf, user_buf, count))
1289399b86cSMichal Kazior 		return -EFAULT;
1299399b86cSMichal Kazior 
1306020d534SShayne Chen 	if (count && buf[count - 1] == '\n')
1316020d534SShayne Chen 		buf[count - 1] = '\0';
1326020d534SShayne Chen 	else
1336020d534SShayne Chen 		buf[count] = '\0';
1349399b86cSMichal Kazior 
1359399b86cSMichal Kazior 	if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1)
1369399b86cSMichal Kazior 		return count;
1372a4e675dSToke Høiland-Jørgensen 	else if (sscanf(buf, "fq_memory_limit %u", &local->fq.memory_limit) == 1)
1382a4e675dSToke Høiland-Jørgensen 		return count;
1399399b86cSMichal Kazior 	else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1)
1409399b86cSMichal Kazior 		return count;
1419399b86cSMichal Kazior 
1429399b86cSMichal Kazior 	return -EINVAL;
1439399b86cSMichal Kazior }
1449399b86cSMichal Kazior 
1459399b86cSMichal Kazior static const struct file_operations aqm_ops = {
1469399b86cSMichal Kazior 	.write = aqm_write,
1479399b86cSMichal Kazior 	.read = aqm_read,
1488d51dbb8SToke Høiland-Jørgensen 	.open = simple_open,
1499399b86cSMichal Kazior 	.llseek = default_llseek,
1509399b86cSMichal Kazior };
1519399b86cSMichal Kazior 
152e322c07fSLorenzo Bianconi static ssize_t airtime_flags_read(struct file *file,
153e322c07fSLorenzo Bianconi 				  char __user *user_buf,
154e322c07fSLorenzo Bianconi 				  size_t count, loff_t *ppos)
155e322c07fSLorenzo Bianconi {
156e322c07fSLorenzo Bianconi 	struct ieee80211_local *local = file->private_data;
157e322c07fSLorenzo Bianconi 	char buf[128] = {}, *pos, *end;
158e322c07fSLorenzo Bianconi 
159e322c07fSLorenzo Bianconi 	pos = buf;
160e322c07fSLorenzo Bianconi 	end = pos + sizeof(buf) - 1;
161e322c07fSLorenzo Bianconi 
162e322c07fSLorenzo Bianconi 	if (local->airtime_flags & AIRTIME_USE_TX)
163e322c07fSLorenzo Bianconi 		pos += scnprintf(pos, end - pos, "AIRTIME_TX\t(%lx)\n",
164e322c07fSLorenzo Bianconi 				 AIRTIME_USE_TX);
165e322c07fSLorenzo Bianconi 	if (local->airtime_flags & AIRTIME_USE_RX)
166e322c07fSLorenzo Bianconi 		pos += scnprintf(pos, end - pos, "AIRTIME_RX\t(%lx)\n",
167e322c07fSLorenzo Bianconi 				 AIRTIME_USE_RX);
168e322c07fSLorenzo Bianconi 
169e322c07fSLorenzo Bianconi 	return simple_read_from_buffer(user_buf, count, ppos, buf,
170e322c07fSLorenzo Bianconi 				       strlen(buf));
171e322c07fSLorenzo Bianconi }
172e322c07fSLorenzo Bianconi 
173e322c07fSLorenzo Bianconi static ssize_t airtime_flags_write(struct file *file,
174e322c07fSLorenzo Bianconi 				   const char __user *user_buf,
175e322c07fSLorenzo Bianconi 				   size_t count, loff_t *ppos)
176e322c07fSLorenzo Bianconi {
177e322c07fSLorenzo Bianconi 	struct ieee80211_local *local = file->private_data;
178e322c07fSLorenzo Bianconi 	char buf[16];
179e322c07fSLorenzo Bianconi 
1806020d534SShayne Chen 	if (count >= sizeof(buf))
181e322c07fSLorenzo Bianconi 		return -EINVAL;
182e322c07fSLorenzo Bianconi 
183e322c07fSLorenzo Bianconi 	if (copy_from_user(buf, user_buf, count))
184e322c07fSLorenzo Bianconi 		return -EFAULT;
185e322c07fSLorenzo Bianconi 
1866020d534SShayne Chen 	if (count && buf[count - 1] == '\n')
1876020d534SShayne Chen 		buf[count - 1] = '\0';
1886020d534SShayne Chen 	else
1896020d534SShayne Chen 		buf[count] = '\0';
190e322c07fSLorenzo Bianconi 
191e322c07fSLorenzo Bianconi 	if (kstrtou16(buf, 0, &local->airtime_flags))
192e322c07fSLorenzo Bianconi 		return -EINVAL;
193e322c07fSLorenzo Bianconi 
194e322c07fSLorenzo Bianconi 	return count;
195e322c07fSLorenzo Bianconi }
196e322c07fSLorenzo Bianconi 
197e322c07fSLorenzo Bianconi static const struct file_operations airtime_flags_ops = {
198e322c07fSLorenzo Bianconi 	.write = airtime_flags_write,
199e322c07fSLorenzo Bianconi 	.read = airtime_flags_read,
200e322c07fSLorenzo Bianconi 	.open = simple_open,
201e322c07fSLorenzo Bianconi 	.llseek = default_llseek,
202e322c07fSLorenzo Bianconi };
203e322c07fSLorenzo Bianconi 
2043ace10f5SKan Yan static ssize_t aql_txq_limit_read(struct file *file,
2053ace10f5SKan Yan 				  char __user *user_buf,
2063ace10f5SKan Yan 				  size_t count,
2073ace10f5SKan Yan 				  loff_t *ppos)
2083ace10f5SKan Yan {
2093ace10f5SKan Yan 	struct ieee80211_local *local = file->private_data;
2103ace10f5SKan Yan 	char buf[400];
2113ace10f5SKan Yan 	int len = 0;
2123ace10f5SKan Yan 
2133ace10f5SKan Yan 	len = scnprintf(buf, sizeof(buf),
2143ace10f5SKan Yan 			"AC	AQL limit low	AQL limit high\n"
2153ace10f5SKan Yan 			"VO	%u		%u\n"
2163ace10f5SKan Yan 			"VI	%u		%u\n"
2173ace10f5SKan Yan 			"BE	%u		%u\n"
2183ace10f5SKan Yan 			"BK	%u		%u\n",
2193ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_VO],
2203ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_VO],
2213ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_VI],
2223ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_VI],
2233ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_BE],
2243ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_BE],
2253ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_BK],
2263ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_BK]);
2273ace10f5SKan Yan 	return simple_read_from_buffer(user_buf, count, ppos,
2283ace10f5SKan Yan 				       buf, len);
2293ace10f5SKan Yan }
2303ace10f5SKan Yan 
2313ace10f5SKan Yan static ssize_t aql_txq_limit_write(struct file *file,
2323ace10f5SKan Yan 				   const char __user *user_buf,
2333ace10f5SKan Yan 				   size_t count,
2343ace10f5SKan Yan 				   loff_t *ppos)
2353ace10f5SKan Yan {
2363ace10f5SKan Yan 	struct ieee80211_local *local = file->private_data;
2373ace10f5SKan Yan 	char buf[100];
2383ace10f5SKan Yan 	u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old;
2393ace10f5SKan Yan 	struct sta_info *sta;
2403ace10f5SKan Yan 
2416020d534SShayne Chen 	if (count >= sizeof(buf))
2423ace10f5SKan Yan 		return -EINVAL;
2433ace10f5SKan Yan 
2443ace10f5SKan Yan 	if (copy_from_user(buf, user_buf, count))
2453ace10f5SKan Yan 		return -EFAULT;
2463ace10f5SKan Yan 
2476020d534SShayne Chen 	if (count && buf[count - 1] == '\n')
2486020d534SShayne Chen 		buf[count - 1] = '\0';
2496020d534SShayne Chen 	else
2506020d534SShayne Chen 		buf[count] = '\0';
2513ace10f5SKan Yan 
2523ace10f5SKan Yan 	if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
2533ace10f5SKan Yan 		return -EINVAL;
2543ace10f5SKan Yan 
2553ace10f5SKan Yan 	if (ac >= IEEE80211_NUM_ACS)
2563ace10f5SKan Yan 		return -EINVAL;
2573ace10f5SKan Yan 
2583ace10f5SKan Yan 	q_limit_low_old = local->aql_txq_limit_low[ac];
2593ace10f5SKan Yan 	q_limit_high_old = local->aql_txq_limit_high[ac];
2603ace10f5SKan Yan 
2613ace10f5SKan Yan 	local->aql_txq_limit_low[ac] = q_limit_low;
2623ace10f5SKan Yan 	local->aql_txq_limit_high[ac] = q_limit_high;
2633ace10f5SKan Yan 
2643ace10f5SKan Yan 	mutex_lock(&local->sta_mtx);
2653ace10f5SKan Yan 	list_for_each_entry(sta, &local->sta_list, list) {
2663ace10f5SKan Yan 		/* If a sta has customized queue limits, keep it */
2673ace10f5SKan Yan 		if (sta->airtime[ac].aql_limit_low == q_limit_low_old &&
2683ace10f5SKan Yan 		    sta->airtime[ac].aql_limit_high == q_limit_high_old) {
2693ace10f5SKan Yan 			sta->airtime[ac].aql_limit_low = q_limit_low;
2703ace10f5SKan Yan 			sta->airtime[ac].aql_limit_high = q_limit_high;
2713ace10f5SKan Yan 		}
2723ace10f5SKan Yan 	}
2733ace10f5SKan Yan 	mutex_unlock(&local->sta_mtx);
2743ace10f5SKan Yan 	return count;
2753ace10f5SKan Yan }
2763ace10f5SKan Yan 
2773ace10f5SKan Yan static const struct file_operations aql_txq_limit_ops = {
2783ace10f5SKan Yan 	.write = aql_txq_limit_write,
2793ace10f5SKan Yan 	.read = aql_txq_limit_read,
2803ace10f5SKan Yan 	.open = simple_open,
2813ace10f5SKan Yan 	.llseek = default_llseek,
2823ace10f5SKan Yan };
2833ace10f5SKan Yan 
284e908435eSLorenzo Bianconi static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
285e908435eSLorenzo Bianconi 			       size_t count, loff_t *ppos)
286e908435eSLorenzo Bianconi {
287e908435eSLorenzo Bianconi 	char buf[3];
288e908435eSLorenzo Bianconi 	int len;
289e908435eSLorenzo Bianconi 
290e908435eSLorenzo Bianconi 	len = scnprintf(buf, sizeof(buf), "%d\n",
291e908435eSLorenzo Bianconi 			!static_key_false(&aql_disable.key));
292e908435eSLorenzo Bianconi 
293e908435eSLorenzo Bianconi 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
294e908435eSLorenzo Bianconi }
295e908435eSLorenzo Bianconi 
296e908435eSLorenzo Bianconi static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
297e908435eSLorenzo Bianconi 				size_t count, loff_t *ppos)
298e908435eSLorenzo Bianconi {
299e908435eSLorenzo Bianconi 	bool aql_disabled = static_key_false(&aql_disable.key);
300e908435eSLorenzo Bianconi 	char buf[3];
301e908435eSLorenzo Bianconi 	size_t len;
302e908435eSLorenzo Bianconi 
303e908435eSLorenzo Bianconi 	if (count > sizeof(buf))
304e908435eSLorenzo Bianconi 		return -EINVAL;
305e908435eSLorenzo Bianconi 
306e908435eSLorenzo Bianconi 	if (copy_from_user(buf, user_buf, count))
307e908435eSLorenzo Bianconi 		return -EFAULT;
308e908435eSLorenzo Bianconi 
309e908435eSLorenzo Bianconi 	buf[sizeof(buf) - 1] = '\0';
310e908435eSLorenzo Bianconi 	len = strlen(buf);
311e908435eSLorenzo Bianconi 	if (len > 0 && buf[len - 1] == '\n')
312e908435eSLorenzo Bianconi 		buf[len - 1] = 0;
313e908435eSLorenzo Bianconi 
314e908435eSLorenzo Bianconi 	if (buf[0] == '0' && buf[1] == '\0') {
315e908435eSLorenzo Bianconi 		if (!aql_disabled)
316e908435eSLorenzo Bianconi 			static_branch_inc(&aql_disable);
317e908435eSLorenzo Bianconi 	} else if (buf[0] == '1' && buf[1] == '\0') {
318e908435eSLorenzo Bianconi 		if (aql_disabled)
319e908435eSLorenzo Bianconi 			static_branch_dec(&aql_disable);
320e908435eSLorenzo Bianconi 	} else {
321e908435eSLorenzo Bianconi 		return -EINVAL;
322e908435eSLorenzo Bianconi 	}
323e908435eSLorenzo Bianconi 
324e908435eSLorenzo Bianconi 	return count;
325e908435eSLorenzo Bianconi }
326e908435eSLorenzo Bianconi 
327e908435eSLorenzo Bianconi static const struct file_operations aql_enable_ops = {
328e908435eSLorenzo Bianconi 	.write = aql_enable_write,
329e908435eSLorenzo Bianconi 	.read = aql_enable_read,
330e908435eSLorenzo Bianconi 	.open = simple_open,
331e908435eSLorenzo Bianconi 	.llseek = default_llseek,
332e908435eSLorenzo Bianconi };
333e908435eSLorenzo Bianconi 
334276d9e82SJulius Niedworok static ssize_t force_tx_status_read(struct file *file,
335276d9e82SJulius Niedworok 				    char __user *user_buf,
336276d9e82SJulius Niedworok 				    size_t count,
337276d9e82SJulius Niedworok 				    loff_t *ppos)
338276d9e82SJulius Niedworok {
339276d9e82SJulius Niedworok 	struct ieee80211_local *local = file->private_data;
340276d9e82SJulius Niedworok 	char buf[3];
341276d9e82SJulius Niedworok 	int len = 0;
342276d9e82SJulius Niedworok 
343276d9e82SJulius Niedworok 	len = scnprintf(buf, sizeof(buf), "%d\n", (int)local->force_tx_status);
344276d9e82SJulius Niedworok 
345276d9e82SJulius Niedworok 	return simple_read_from_buffer(user_buf, count, ppos,
346276d9e82SJulius Niedworok 				       buf, len);
347276d9e82SJulius Niedworok }
348276d9e82SJulius Niedworok 
349276d9e82SJulius Niedworok static ssize_t force_tx_status_write(struct file *file,
350276d9e82SJulius Niedworok 				     const char __user *user_buf,
351276d9e82SJulius Niedworok 				     size_t count,
352276d9e82SJulius Niedworok 				     loff_t *ppos)
353276d9e82SJulius Niedworok {
354276d9e82SJulius Niedworok 	struct ieee80211_local *local = file->private_data;
355276d9e82SJulius Niedworok 	char buf[3];
356276d9e82SJulius Niedworok 
3576020d534SShayne Chen 	if (count >= sizeof(buf))
358276d9e82SJulius Niedworok 		return -EINVAL;
359276d9e82SJulius Niedworok 
360276d9e82SJulius Niedworok 	if (copy_from_user(buf, user_buf, count))
361276d9e82SJulius Niedworok 		return -EFAULT;
362276d9e82SJulius Niedworok 
3636020d534SShayne Chen 	if (count && buf[count - 1] == '\n')
3646020d534SShayne Chen 		buf[count - 1] = '\0';
3656020d534SShayne Chen 	else
3666020d534SShayne Chen 		buf[count] = '\0';
367276d9e82SJulius Niedworok 
368276d9e82SJulius Niedworok 	if (buf[0] == '0' && buf[1] == '\0')
369276d9e82SJulius Niedworok 		local->force_tx_status = 0;
370276d9e82SJulius Niedworok 	else if (buf[0] == '1' && buf[1] == '\0')
371276d9e82SJulius Niedworok 		local->force_tx_status = 1;
372276d9e82SJulius Niedworok 	else
373276d9e82SJulius Niedworok 		return -EINVAL;
374276d9e82SJulius Niedworok 
375276d9e82SJulius Niedworok 	return count;
376276d9e82SJulius Niedworok }
377276d9e82SJulius Niedworok 
378276d9e82SJulius Niedworok static const struct file_operations force_tx_status_ops = {
379276d9e82SJulius Niedworok 	.write = force_tx_status_write,
380276d9e82SJulius Niedworok 	.read = force_tx_status_read,
381276d9e82SJulius Niedworok 	.open = simple_open,
382276d9e82SJulius Niedworok 	.llseek = default_llseek,
383276d9e82SJulius Niedworok };
384276d9e82SJulius Niedworok 
3852ad4814fSJohannes Berg #ifdef CONFIG_PM
386827b1fb4SJohannes Berg static ssize_t reset_write(struct file *file, const char __user *user_buf,
387827b1fb4SJohannes Berg 			   size_t count, loff_t *ppos)
388827b1fb4SJohannes Berg {
389827b1fb4SJohannes Berg 	struct ieee80211_local *local = file->private_data;
390*f5baf287SJohannes Berg 	int ret;
391827b1fb4SJohannes Berg 
392827b1fb4SJohannes Berg 	rtnl_lock();
393adaed1b9SJohannes Berg 	wiphy_lock(local->hw.wiphy);
394eecc4800SJohannes Berg 	__ieee80211_suspend(&local->hw, NULL);
395*f5baf287SJohannes Berg 	ret = __ieee80211_resume(&local->hw);
396adaed1b9SJohannes Berg 	wiphy_unlock(local->hw.wiphy);
397*f5baf287SJohannes Berg 
398*f5baf287SJohannes Berg 	if (ret)
399*f5baf287SJohannes Berg 		cfg80211_shutdown_all_interfaces(local->hw.wiphy);
400*f5baf287SJohannes Berg 
401827b1fb4SJohannes Berg 	rtnl_unlock();
402827b1fb4SJohannes Berg 
403827b1fb4SJohannes Berg 	return count;
404827b1fb4SJohannes Berg }
405827b1fb4SJohannes Berg 
406827b1fb4SJohannes Berg static const struct file_operations reset_ops = {
407827b1fb4SJohannes Berg 	.write = reset_write,
408234e3405SStephen Boyd 	.open = simple_open,
4096038f373SArnd Bergmann 	.llseek = noop_llseek,
410827b1fb4SJohannes Berg };
4112ad4814fSJohannes Berg #endif
412827b1fb4SJohannes Berg 
41368920c97SAndrey Ryabinin static const char *hw_flag_names[] = {
41430686bf7SJohannes Berg #define FLAG(F)	[IEEE80211_HW_##F] = #F
41530686bf7SJohannes Berg 	FLAG(HAS_RATE_CONTROL),
41630686bf7SJohannes Berg 	FLAG(RX_INCLUDES_FCS),
41730686bf7SJohannes Berg 	FLAG(HOST_BROADCAST_PS_BUFFERING),
41830686bf7SJohannes Berg 	FLAG(SIGNAL_UNSPEC),
41930686bf7SJohannes Berg 	FLAG(SIGNAL_DBM),
42030686bf7SJohannes Berg 	FLAG(NEED_DTIM_BEFORE_ASSOC),
42130686bf7SJohannes Berg 	FLAG(SPECTRUM_MGMT),
42230686bf7SJohannes Berg 	FLAG(AMPDU_AGGREGATION),
42330686bf7SJohannes Berg 	FLAG(SUPPORTS_PS),
42430686bf7SJohannes Berg 	FLAG(PS_NULLFUNC_STACK),
42530686bf7SJohannes Berg 	FLAG(SUPPORTS_DYNAMIC_PS),
42630686bf7SJohannes Berg 	FLAG(MFP_CAPABLE),
42730686bf7SJohannes Berg 	FLAG(WANT_MONITOR_VIF),
42830686bf7SJohannes Berg 	FLAG(NO_AUTO_VIF),
42930686bf7SJohannes Berg 	FLAG(SW_CRYPTO_CONTROL),
43030686bf7SJohannes Berg 	FLAG(SUPPORT_FAST_XMIT),
43130686bf7SJohannes Berg 	FLAG(REPORTS_TX_ACK_STATUS),
43230686bf7SJohannes Berg 	FLAG(CONNECTION_MONITOR),
43330686bf7SJohannes Berg 	FLAG(QUEUE_CONTROL),
43430686bf7SJohannes Berg 	FLAG(SUPPORTS_PER_STA_GTK),
43530686bf7SJohannes Berg 	FLAG(AP_LINK_PS),
43630686bf7SJohannes Berg 	FLAG(TX_AMPDU_SETUP_IN_HW),
43730686bf7SJohannes Berg 	FLAG(SUPPORTS_RC_TABLE),
43830686bf7SJohannes Berg 	FLAG(P2P_DEV_ADDR_FOR_INTF),
43930686bf7SJohannes Berg 	FLAG(TIMING_BEACON_ONLY),
44030686bf7SJohannes Berg 	FLAG(SUPPORTS_HT_CCK_RATES),
44130686bf7SJohannes Berg 	FLAG(CHANCTX_STA_CSA),
44230686bf7SJohannes Berg 	FLAG(SUPPORTS_CLONED_SKBS),
44330686bf7SJohannes Berg 	FLAG(SINGLE_SCAN_ON_ALL_BANDS),
444b98fb44fSArik Nemtsov 	FLAG(TDLS_WIDER_BW),
44599e7ca44SEmmanuel Grumbach 	FLAG(SUPPORTS_AMSDU_IN_AMPDU),
44635afa588SHelmut Schaa 	FLAG(BEACON_TX_STATUS),
44731104891SJohannes Berg 	FLAG(NEEDS_UNIQUE_STA_ADDR),
448412a6d80SSara Sharon 	FLAG(SUPPORTS_REORDERING_BUFFER),
449c9c5962bSJohannes Berg 	FLAG(USES_RSS),
4506e0456b5SFelix Fietkau 	FLAG(TX_AMSDU),
4516e0456b5SFelix Fietkau 	FLAG(TX_FRAG_LIST),
452e8a24cd4SRajkumar Manoharan 	FLAG(REPORTS_LOW_ACK),
453f3fe4e93SSara Sharon 	FLAG(SUPPORTS_TX_FRAG),
454e2fb1b83SYingying Tang 	FLAG(SUPPORTS_TDLS_BUFFER_STA),
45594ba9271SIlan Peer 	FLAG(DEAUTH_NEED_MGD_TX_PREP),
4567c181f4fSBen Caradoc-Davies 	FLAG(DOESNT_SUPPORT_QOS_NDP),
457adf8ed01SJohannes Berg 	FLAG(BUFF_MMPDU_TXQ),
45809b4a4faSJohannes Berg 	FLAG(SUPPORTS_VHT_EXT_NSS_BW),
4590eeb2b67SSara Sharon 	FLAG(STA_MMPDU_TXQ),
46077f7ffdcSFelix Fietkau 	FLAG(TX_STATUS_NO_AMPDU_LEN),
461caf56338SSara Sharon 	FLAG(SUPPORTS_MULTI_BSSID),
462caf56338SSara Sharon 	FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
463dc3998ecSAlexander Wetzel 	FLAG(AMPDU_KEYBORDER_SUPPORT),
4646aea26ceSFelix Fietkau 	FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
46580a915ecSFelix Fietkau 	FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
46655f8205eSSriram R 	FLAG(SUPPORTS_CONC_MON_RX_DECAP),
46730686bf7SJohannes Berg #undef FLAG
46830686bf7SJohannes Berg };
46930686bf7SJohannes Berg 
470279daf64SBen Greear static ssize_t hwflags_read(struct file *file, char __user *user_buf,
471279daf64SBen Greear 			    size_t count, loff_t *ppos)
472279daf64SBen Greear {
473279daf64SBen Greear 	struct ieee80211_local *local = file->private_data;
47430686bf7SJohannes Berg 	size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS;
47530686bf7SJohannes Berg 	char *buf = kzalloc(bufsz, GFP_KERNEL);
47630686bf7SJohannes Berg 	char *pos = buf, *end = buf + bufsz - 1;
477279daf64SBen Greear 	ssize_t rv;
47830686bf7SJohannes Berg 	int i;
479279daf64SBen Greear 
480d15b8459SJoe Perches 	if (!buf)
48130686bf7SJohannes Berg 		return -ENOMEM;
482d15b8459SJoe Perches 
48330686bf7SJohannes Berg 	/* fail compilation if somebody adds or removes
48430686bf7SJohannes Berg 	 * a flag without updating the name array above
48530686bf7SJohannes Berg 	 */
48668920c97SAndrey Ryabinin 	BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS);
48730686bf7SJohannes Berg 
48830686bf7SJohannes Berg 	for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) {
48930686bf7SJohannes Berg 		if (test_bit(i, local->hw.flags))
4904633dfc3SMohammed Shafi Shajakhan 			pos += scnprintf(pos, end - pos, "%s\n",
49130686bf7SJohannes Berg 					 hw_flag_names[i]);
49230686bf7SJohannes Berg 	}
493279daf64SBen Greear 
494279daf64SBen Greear 	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
495279daf64SBen Greear 	kfree(buf);
496279daf64SBen Greear 	return rv;
497279daf64SBen Greear }
498199d69f2SBenoit Papillault 
4994a5eccaaSBen Greear static ssize_t misc_read(struct file *file, char __user *user_buf,
5004a5eccaaSBen Greear 			 size_t count, loff_t *ppos)
5014a5eccaaSBen Greear {
5024a5eccaaSBen Greear 	struct ieee80211_local *local = file->private_data;
5034a5eccaaSBen Greear 	/* Max len of each line is 16 characters, plus 9 for 'pending:\n' */
5044a5eccaaSBen Greear 	size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9;
505b2347a32SDan Carpenter 	char *buf;
506b2347a32SDan Carpenter 	char *pos, *end;
5074a5eccaaSBen Greear 	ssize_t rv;
5084a5eccaaSBen Greear 	int i;
5094a5eccaaSBen Greear 	int ln;
5104a5eccaaSBen Greear 
511b2347a32SDan Carpenter 	buf = kzalloc(bufsz, GFP_KERNEL);
512b2347a32SDan Carpenter 	if (!buf)
513b2347a32SDan Carpenter 		return -ENOMEM;
514b2347a32SDan Carpenter 
515b2347a32SDan Carpenter 	pos = buf;
516b2347a32SDan Carpenter 	end = buf + bufsz - 1;
517b2347a32SDan Carpenter 
5184a5eccaaSBen Greear 	pos += scnprintf(pos, end - pos, "pending:\n");
5194a5eccaaSBen Greear 
5204a5eccaaSBen Greear 	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
5214a5eccaaSBen Greear 		ln = skb_queue_len(&local->pending[i]);
5224a5eccaaSBen Greear 		pos += scnprintf(pos, end - pos, "[%i] %d\n",
5234a5eccaaSBen Greear 				 i, ln);
5244a5eccaaSBen Greear 	}
5254a5eccaaSBen Greear 
5264a5eccaaSBen Greear 	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
5274a5eccaaSBen Greear 	kfree(buf);
5284a5eccaaSBen Greear 	return rv;
5294a5eccaaSBen Greear }
5304a5eccaaSBen Greear 
531db2e6bd4SJohannes Berg static ssize_t queues_read(struct file *file, char __user *user_buf,
532db2e6bd4SJohannes Berg 			   size_t count, loff_t *ppos)
533db2e6bd4SJohannes Berg {
534db2e6bd4SJohannes Berg 	struct ieee80211_local *local = file->private_data;
535db2e6bd4SJohannes Berg 	unsigned long flags;
536db2e6bd4SJohannes Berg 	char buf[IEEE80211_MAX_QUEUES * 20];
537db2e6bd4SJohannes Berg 	int q, res = 0;
538db2e6bd4SJohannes Berg 
539db2e6bd4SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
540db2e6bd4SJohannes Berg 	for (q = 0; q < local->hw.queues; q++)
541db2e6bd4SJohannes Berg 		res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
542db2e6bd4SJohannes Berg 				local->queue_stop_reasons[q],
5433b8d81e0SJohannes Berg 				skb_queue_len(&local->pending[q]));
544db2e6bd4SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
545db2e6bd4SJohannes Berg 
546db2e6bd4SJohannes Berg 	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
547db2e6bd4SJohannes Berg }
548db2e6bd4SJohannes Berg 
549279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(hwflags);
550279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(queues);
5514a5eccaaSBen Greear DEBUGFS_READONLY_FILE_OPS(misc);
552db2e6bd4SJohannes Berg 
553e9f207f0SJiri Benc /* statistics stuff */
554e9f207f0SJiri Benc 
555e9f207f0SJiri Benc static ssize_t format_devstat_counter(struct ieee80211_local *local,
556e9f207f0SJiri Benc 	char __user *userbuf,
557e9f207f0SJiri Benc 	size_t count, loff_t *ppos,
558e9f207f0SJiri Benc 	int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf,
559e9f207f0SJiri Benc 			  int buflen))
560e9f207f0SJiri Benc {
561e9f207f0SJiri Benc 	struct ieee80211_low_level_stats stats;
562e9f207f0SJiri Benc 	char buf[20];
563e9f207f0SJiri Benc 	int res;
564e9f207f0SJiri Benc 
56575636525SJohannes Berg 	rtnl_lock();
56624487981SJohannes Berg 	res = drv_get_stats(local, &stats);
567e9f207f0SJiri Benc 	rtnl_unlock();
56824487981SJohannes Berg 	if (res)
56924487981SJohannes Berg 		return res;
570e9f207f0SJiri Benc 	res = printvalue(&stats, buf, sizeof(buf));
571e9f207f0SJiri Benc 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
572e9f207f0SJiri Benc }
573e9f207f0SJiri Benc 
574e9f207f0SJiri Benc #define DEBUGFS_DEVSTATS_FILE(name)					\
575e9f207f0SJiri Benc static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\
576e9f207f0SJiri Benc 				 char *buf, int buflen)			\
577e9f207f0SJiri Benc {									\
578e9f207f0SJiri Benc 	return scnprintf(buf, buflen, "%u\n", stats->name);		\
579e9f207f0SJiri Benc }									\
580e9f207f0SJiri Benc static ssize_t stats_ ##name## _read(struct file *file,			\
581e9f207f0SJiri Benc 				     char __user *userbuf,		\
582e9f207f0SJiri Benc 				     size_t count, loff_t *ppos)	\
583e9f207f0SJiri Benc {									\
584e9f207f0SJiri Benc 	return format_devstat_counter(file->private_data,		\
585e9f207f0SJiri Benc 				      userbuf,				\
586e9f207f0SJiri Benc 				      count,				\
587e9f207f0SJiri Benc 				      ppos,				\
588e9f207f0SJiri Benc 				      print_devstats_##name);		\
589e9f207f0SJiri Benc }									\
590e9f207f0SJiri Benc 									\
591e9f207f0SJiri Benc static const struct file_operations stats_ ##name## _ops = {		\
592e9f207f0SJiri Benc 	.read = stats_ ##name## _read,					\
593234e3405SStephen Boyd 	.open = simple_open,						\
5942b18ab36SArnd Bergmann 	.llseek = generic_file_llseek,					\
595e9f207f0SJiri Benc };
596e9f207f0SJiri Benc 
597f1160434SJohannes Berg #define DEBUGFS_STATS_ADD(name)					\
598f1160434SJohannes Berg 	debugfs_create_u32(#name, 0400, statsd, &local->name);
5992826bcd8SFelix Fietkau #define DEBUGFS_DEVSTATS_ADD(name)					\
6007bcfaf2fSJohannes Berg 	debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
601e9f207f0SJiri Benc 
602e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
603e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount);
604e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount);
605e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount);
606e9f207f0SJiri Benc 
607e9f207f0SJiri Benc void debugfs_hw_add(struct ieee80211_local *local)
608e9f207f0SJiri Benc {
609e9f207f0SJiri Benc 	struct dentry *phyd = local->hw.wiphy->debugfsdir;
610e9f207f0SJiri Benc 	struct dentry *statsd;
611e9f207f0SJiri Benc 
612e9f207f0SJiri Benc 	if (!phyd)
613e9f207f0SJiri Benc 		return;
614e9f207f0SJiri Benc 
615e9f207f0SJiri Benc 	local->debugfs.keys = debugfs_create_dir("keys", phyd);
616e9f207f0SJiri Benc 
617e9f207f0SJiri Benc 	DEBUGFS_ADD(total_ps_buffered);
618e9f207f0SJiri Benc 	DEBUGFS_ADD(wep_iv);
619bddb2afcSJohannes Berg 	DEBUGFS_ADD(rate_ctrl_alg);
620db2e6bd4SJohannes Berg 	DEBUGFS_ADD(queues);
6214a5eccaaSBen Greear 	DEBUGFS_ADD(misc);
6222ad4814fSJohannes Berg #ifdef CONFIG_PM
623827b1fb4SJohannes Berg 	DEBUGFS_ADD_MODE(reset, 0200);
6242ad4814fSJohannes Berg #endif
625279daf64SBen Greear 	DEBUGFS_ADD(hwflags);
62683bdf2a1SBen Greear 	DEBUGFS_ADD(user_power);
62783bdf2a1SBen Greear 	DEBUGFS_ADD(power);
628c90142a5SThomas Pedersen 	DEBUGFS_ADD(hw_conf);
629276d9e82SJulius Niedworok 	DEBUGFS_ADD_MODE(force_tx_status, 0600);
630e908435eSLorenzo Bianconi 	DEBUGFS_ADD_MODE(aql_enable, 0600);
6318d51dbb8SToke Høiland-Jørgensen 
6328d51dbb8SToke Høiland-Jørgensen 	if (local->ops->wake_tx_queue)
6339399b86cSMichal Kazior 		DEBUGFS_ADD_MODE(aqm, 0600);
634e9f207f0SJiri Benc 
635e322c07fSLorenzo Bianconi 	DEBUGFS_ADD_MODE(airtime_flags, 0600);
636b4809e94SToke Høiland-Jørgensen 
6373ace10f5SKan Yan 	DEBUGFS_ADD(aql_txq_limit);
6383ace10f5SKan Yan 	debugfs_create_u32("aql_threshold", 0600,
6393ace10f5SKan Yan 			   phyd, &local->aql_threshold);
6403ace10f5SKan Yan 
641e9f207f0SJiri Benc 	statsd = debugfs_create_dir("statistics", phyd);
642e9f207f0SJiri Benc 
643e9f207f0SJiri Benc 	/* if the dir failed, don't put all the other things into the root! */
644e9f207f0SJiri Benc 	if (!statsd)
645e9f207f0SJiri Benc 		return;
646e9f207f0SJiri Benc 
647c206ca67SJohannes Berg #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
648f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount);
649f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount);
650f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11FailedCount);
651f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11RetryCount);
652f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MultipleRetryCount);
653f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11FrameDuplicateCount);
654f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount);
655f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount);
656f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11TransmittedFrameCount);
657f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop);
658f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_queued);
659f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_wep);
660f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc);
661f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port);
662f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop);
663f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_queued);
664f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
665f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
666f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_expand_skb_head);
667f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
668f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
669f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_fragments);
670f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_status_drop);
671e9f207f0SJiri Benc #endif
6722826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount);
6732826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
6742826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
6752826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
676e9f207f0SJiri Benc }
677