xref: /linux/net/mac80211/debugfs.c (revision e322c07f8371164eaa9e76d46d70b1b88ad5d153)
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
7caf56338SSara Sharon  * Copyright (C) 2018 - 2019 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)						\
567bcfaf2fSJohannes Berg 	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 	size_t len;
1249399b86cSMichal Kazior 
1259399b86cSMichal Kazior 	if (count > sizeof(buf))
1269399b86cSMichal Kazior 		return -EINVAL;
1279399b86cSMichal Kazior 
1289399b86cSMichal Kazior 	if (copy_from_user(buf, user_buf, count))
1299399b86cSMichal Kazior 		return -EFAULT;
1309399b86cSMichal Kazior 
1319399b86cSMichal Kazior 	buf[sizeof(buf) - 1] = '\0';
1329399b86cSMichal Kazior 	len = strlen(buf);
1339399b86cSMichal Kazior 	if (len > 0 && buf[len-1] == '\n')
1349399b86cSMichal Kazior 		buf[len-1] = 0;
1359399b86cSMichal Kazior 
1369399b86cSMichal Kazior 	if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1)
1379399b86cSMichal Kazior 		return count;
1382a4e675dSToke Høiland-Jørgensen 	else if (sscanf(buf, "fq_memory_limit %u", &local->fq.memory_limit) == 1)
1392a4e675dSToke Høiland-Jørgensen 		return count;
1409399b86cSMichal Kazior 	else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1)
1419399b86cSMichal Kazior 		return count;
1429399b86cSMichal Kazior 
1439399b86cSMichal Kazior 	return -EINVAL;
1449399b86cSMichal Kazior }
1459399b86cSMichal Kazior 
1469399b86cSMichal Kazior static const struct file_operations aqm_ops = {
1479399b86cSMichal Kazior 	.write = aqm_write,
1489399b86cSMichal Kazior 	.read = aqm_read,
1498d51dbb8SToke Høiland-Jørgensen 	.open = simple_open,
1509399b86cSMichal Kazior 	.llseek = default_llseek,
1519399b86cSMichal Kazior };
1529399b86cSMichal Kazior 
153*e322c07fSLorenzo Bianconi static ssize_t airtime_flags_read(struct file *file,
154*e322c07fSLorenzo Bianconi 				  char __user *user_buf,
155*e322c07fSLorenzo Bianconi 				  size_t count, loff_t *ppos)
156*e322c07fSLorenzo Bianconi {
157*e322c07fSLorenzo Bianconi 	struct ieee80211_local *local = file->private_data;
158*e322c07fSLorenzo Bianconi 	char buf[128] = {}, *pos, *end;
159*e322c07fSLorenzo Bianconi 
160*e322c07fSLorenzo Bianconi 	pos = buf;
161*e322c07fSLorenzo Bianconi 	end = pos + sizeof(buf) - 1;
162*e322c07fSLorenzo Bianconi 
163*e322c07fSLorenzo Bianconi 	if (local->airtime_flags & AIRTIME_USE_TX)
164*e322c07fSLorenzo Bianconi 		pos += scnprintf(pos, end - pos, "AIRTIME_TX\t(%lx)\n",
165*e322c07fSLorenzo Bianconi 				 AIRTIME_USE_TX);
166*e322c07fSLorenzo Bianconi 	if (local->airtime_flags & AIRTIME_USE_RX)
167*e322c07fSLorenzo Bianconi 		pos += scnprintf(pos, end - pos, "AIRTIME_RX\t(%lx)\n",
168*e322c07fSLorenzo Bianconi 				 AIRTIME_USE_RX);
169*e322c07fSLorenzo Bianconi 
170*e322c07fSLorenzo Bianconi 	return simple_read_from_buffer(user_buf, count, ppos, buf,
171*e322c07fSLorenzo Bianconi 				       strlen(buf));
172*e322c07fSLorenzo Bianconi }
173*e322c07fSLorenzo Bianconi 
174*e322c07fSLorenzo Bianconi static ssize_t airtime_flags_write(struct file *file,
175*e322c07fSLorenzo Bianconi 				   const char __user *user_buf,
176*e322c07fSLorenzo Bianconi 				   size_t count, loff_t *ppos)
177*e322c07fSLorenzo Bianconi {
178*e322c07fSLorenzo Bianconi 	struct ieee80211_local *local = file->private_data;
179*e322c07fSLorenzo Bianconi 	char buf[16];
180*e322c07fSLorenzo Bianconi 	size_t len;
181*e322c07fSLorenzo Bianconi 
182*e322c07fSLorenzo Bianconi 	if (count > sizeof(buf))
183*e322c07fSLorenzo Bianconi 		return -EINVAL;
184*e322c07fSLorenzo Bianconi 
185*e322c07fSLorenzo Bianconi 	if (copy_from_user(buf, user_buf, count))
186*e322c07fSLorenzo Bianconi 		return -EFAULT;
187*e322c07fSLorenzo Bianconi 
188*e322c07fSLorenzo Bianconi 	buf[sizeof(buf) - 1] = 0;
189*e322c07fSLorenzo Bianconi 	len = strlen(buf);
190*e322c07fSLorenzo Bianconi 	if (len > 0 && buf[len - 1] == '\n')
191*e322c07fSLorenzo Bianconi 		buf[len - 1] = 0;
192*e322c07fSLorenzo Bianconi 
193*e322c07fSLorenzo Bianconi 	if (kstrtou16(buf, 0, &local->airtime_flags))
194*e322c07fSLorenzo Bianconi 		return -EINVAL;
195*e322c07fSLorenzo Bianconi 
196*e322c07fSLorenzo Bianconi 	return count;
197*e322c07fSLorenzo Bianconi }
198*e322c07fSLorenzo Bianconi 
199*e322c07fSLorenzo Bianconi static const struct file_operations airtime_flags_ops = {
200*e322c07fSLorenzo Bianconi 	.write = airtime_flags_write,
201*e322c07fSLorenzo Bianconi 	.read = airtime_flags_read,
202*e322c07fSLorenzo Bianconi 	.open = simple_open,
203*e322c07fSLorenzo Bianconi 	.llseek = default_llseek,
204*e322c07fSLorenzo Bianconi };
205*e322c07fSLorenzo Bianconi 
2063ace10f5SKan Yan static ssize_t aql_txq_limit_read(struct file *file,
2073ace10f5SKan Yan 				  char __user *user_buf,
2083ace10f5SKan Yan 				  size_t count,
2093ace10f5SKan Yan 				  loff_t *ppos)
2103ace10f5SKan Yan {
2113ace10f5SKan Yan 	struct ieee80211_local *local = file->private_data;
2123ace10f5SKan Yan 	char buf[400];
2133ace10f5SKan Yan 	int len = 0;
2143ace10f5SKan Yan 
2153ace10f5SKan Yan 	len = scnprintf(buf, sizeof(buf),
2163ace10f5SKan Yan 			"AC	AQL limit low	AQL limit high\n"
2173ace10f5SKan Yan 			"VO	%u		%u\n"
2183ace10f5SKan Yan 			"VI	%u		%u\n"
2193ace10f5SKan Yan 			"BE	%u		%u\n"
2203ace10f5SKan Yan 			"BK	%u		%u\n",
2213ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_VO],
2223ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_VO],
2233ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_VI],
2243ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_VI],
2253ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_BE],
2263ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_BE],
2273ace10f5SKan Yan 			local->aql_txq_limit_low[IEEE80211_AC_BK],
2283ace10f5SKan Yan 			local->aql_txq_limit_high[IEEE80211_AC_BK]);
2293ace10f5SKan Yan 	return simple_read_from_buffer(user_buf, count, ppos,
2303ace10f5SKan Yan 				       buf, len);
2313ace10f5SKan Yan }
2323ace10f5SKan Yan 
2333ace10f5SKan Yan static ssize_t aql_txq_limit_write(struct file *file,
2343ace10f5SKan Yan 				   const char __user *user_buf,
2353ace10f5SKan Yan 				   size_t count,
2363ace10f5SKan Yan 				   loff_t *ppos)
2373ace10f5SKan Yan {
2383ace10f5SKan Yan 	struct ieee80211_local *local = file->private_data;
2393ace10f5SKan Yan 	char buf[100];
2403ace10f5SKan Yan 	size_t len;
2413ace10f5SKan Yan 	u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old;
2423ace10f5SKan Yan 	struct sta_info *sta;
2433ace10f5SKan Yan 
2443ace10f5SKan Yan 	if (count > sizeof(buf))
2453ace10f5SKan Yan 		return -EINVAL;
2463ace10f5SKan Yan 
2473ace10f5SKan Yan 	if (copy_from_user(buf, user_buf, count))
2483ace10f5SKan Yan 		return -EFAULT;
2493ace10f5SKan Yan 
2503ace10f5SKan Yan 	buf[sizeof(buf) - 1] = 0;
2513ace10f5SKan Yan 	len = strlen(buf);
2523ace10f5SKan Yan 	if (len > 0 && buf[len - 1] == '\n')
2533ace10f5SKan Yan 		buf[len - 1] = 0;
2543ace10f5SKan Yan 
2553ace10f5SKan Yan 	if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
2563ace10f5SKan Yan 		return -EINVAL;
2573ace10f5SKan Yan 
2583ace10f5SKan Yan 	if (ac >= IEEE80211_NUM_ACS)
2593ace10f5SKan Yan 		return -EINVAL;
2603ace10f5SKan Yan 
2613ace10f5SKan Yan 	q_limit_low_old = local->aql_txq_limit_low[ac];
2623ace10f5SKan Yan 	q_limit_high_old = local->aql_txq_limit_high[ac];
2633ace10f5SKan Yan 
2643ace10f5SKan Yan 	local->aql_txq_limit_low[ac] = q_limit_low;
2653ace10f5SKan Yan 	local->aql_txq_limit_high[ac] = q_limit_high;
2663ace10f5SKan Yan 
2673ace10f5SKan Yan 	mutex_lock(&local->sta_mtx);
2683ace10f5SKan Yan 	list_for_each_entry(sta, &local->sta_list, list) {
2693ace10f5SKan Yan 		/* If a sta has customized queue limits, keep it */
2703ace10f5SKan Yan 		if (sta->airtime[ac].aql_limit_low == q_limit_low_old &&
2713ace10f5SKan Yan 		    sta->airtime[ac].aql_limit_high == q_limit_high_old) {
2723ace10f5SKan Yan 			sta->airtime[ac].aql_limit_low = q_limit_low;
2733ace10f5SKan Yan 			sta->airtime[ac].aql_limit_high = q_limit_high;
2743ace10f5SKan Yan 		}
2753ace10f5SKan Yan 	}
2763ace10f5SKan Yan 	mutex_unlock(&local->sta_mtx);
2773ace10f5SKan Yan 	return count;
2783ace10f5SKan Yan }
2793ace10f5SKan Yan 
2803ace10f5SKan Yan static const struct file_operations aql_txq_limit_ops = {
2813ace10f5SKan Yan 	.write = aql_txq_limit_write,
2823ace10f5SKan Yan 	.read = aql_txq_limit_read,
2833ace10f5SKan Yan 	.open = simple_open,
2843ace10f5SKan Yan 	.llseek = default_llseek,
2853ace10f5SKan Yan };
2863ace10f5SKan Yan 
287276d9e82SJulius Niedworok static ssize_t force_tx_status_read(struct file *file,
288276d9e82SJulius Niedworok 				    char __user *user_buf,
289276d9e82SJulius Niedworok 				    size_t count,
290276d9e82SJulius Niedworok 				    loff_t *ppos)
291276d9e82SJulius Niedworok {
292276d9e82SJulius Niedworok 	struct ieee80211_local *local = file->private_data;
293276d9e82SJulius Niedworok 	char buf[3];
294276d9e82SJulius Niedworok 	int len = 0;
295276d9e82SJulius Niedworok 
296276d9e82SJulius Niedworok 	len = scnprintf(buf, sizeof(buf), "%d\n", (int)local->force_tx_status);
297276d9e82SJulius Niedworok 
298276d9e82SJulius Niedworok 	return simple_read_from_buffer(user_buf, count, ppos,
299276d9e82SJulius Niedworok 				       buf, len);
300276d9e82SJulius Niedworok }
301276d9e82SJulius Niedworok 
302276d9e82SJulius Niedworok static ssize_t force_tx_status_write(struct file *file,
303276d9e82SJulius Niedworok 				     const char __user *user_buf,
304276d9e82SJulius Niedworok 				     size_t count,
305276d9e82SJulius Niedworok 				     loff_t *ppos)
306276d9e82SJulius Niedworok {
307276d9e82SJulius Niedworok 	struct ieee80211_local *local = file->private_data;
308276d9e82SJulius Niedworok 	char buf[3];
309276d9e82SJulius Niedworok 	size_t len;
310276d9e82SJulius Niedworok 
311276d9e82SJulius Niedworok 	if (count > sizeof(buf))
312276d9e82SJulius Niedworok 		return -EINVAL;
313276d9e82SJulius Niedworok 
314276d9e82SJulius Niedworok 	if (copy_from_user(buf, user_buf, count))
315276d9e82SJulius Niedworok 		return -EFAULT;
316276d9e82SJulius Niedworok 
317276d9e82SJulius Niedworok 	buf[sizeof(buf) - 1] = '\0';
318276d9e82SJulius Niedworok 	len = strlen(buf);
319276d9e82SJulius Niedworok 	if (len > 0 && buf[len - 1] == '\n')
320276d9e82SJulius Niedworok 		buf[len - 1] = 0;
321276d9e82SJulius Niedworok 
322276d9e82SJulius Niedworok 	if (buf[0] == '0' && buf[1] == '\0')
323276d9e82SJulius Niedworok 		local->force_tx_status = 0;
324276d9e82SJulius Niedworok 	else if (buf[0] == '1' && buf[1] == '\0')
325276d9e82SJulius Niedworok 		local->force_tx_status = 1;
326276d9e82SJulius Niedworok 	else
327276d9e82SJulius Niedworok 		return -EINVAL;
328276d9e82SJulius Niedworok 
329276d9e82SJulius Niedworok 	return count;
330276d9e82SJulius Niedworok }
331276d9e82SJulius Niedworok 
332276d9e82SJulius Niedworok static const struct file_operations force_tx_status_ops = {
333276d9e82SJulius Niedworok 	.write = force_tx_status_write,
334276d9e82SJulius Niedworok 	.read = force_tx_status_read,
335276d9e82SJulius Niedworok 	.open = simple_open,
336276d9e82SJulius Niedworok 	.llseek = default_llseek,
337276d9e82SJulius Niedworok };
338276d9e82SJulius Niedworok 
3392ad4814fSJohannes Berg #ifdef CONFIG_PM
340827b1fb4SJohannes Berg static ssize_t reset_write(struct file *file, const char __user *user_buf,
341827b1fb4SJohannes Berg 			   size_t count, loff_t *ppos)
342827b1fb4SJohannes Berg {
343827b1fb4SJohannes Berg 	struct ieee80211_local *local = file->private_data;
344827b1fb4SJohannes Berg 
345827b1fb4SJohannes Berg 	rtnl_lock();
346eecc4800SJohannes Berg 	__ieee80211_suspend(&local->hw, NULL);
347827b1fb4SJohannes Berg 	__ieee80211_resume(&local->hw);
348827b1fb4SJohannes Berg 	rtnl_unlock();
349827b1fb4SJohannes Berg 
350827b1fb4SJohannes Berg 	return count;
351827b1fb4SJohannes Berg }
352827b1fb4SJohannes Berg 
353827b1fb4SJohannes Berg static const struct file_operations reset_ops = {
354827b1fb4SJohannes Berg 	.write = reset_write,
355234e3405SStephen Boyd 	.open = simple_open,
3566038f373SArnd Bergmann 	.llseek = noop_llseek,
357827b1fb4SJohannes Berg };
3582ad4814fSJohannes Berg #endif
359827b1fb4SJohannes Berg 
36068920c97SAndrey Ryabinin static const char *hw_flag_names[] = {
36130686bf7SJohannes Berg #define FLAG(F)	[IEEE80211_HW_##F] = #F
36230686bf7SJohannes Berg 	FLAG(HAS_RATE_CONTROL),
36330686bf7SJohannes Berg 	FLAG(RX_INCLUDES_FCS),
36430686bf7SJohannes Berg 	FLAG(HOST_BROADCAST_PS_BUFFERING),
36530686bf7SJohannes Berg 	FLAG(SIGNAL_UNSPEC),
36630686bf7SJohannes Berg 	FLAG(SIGNAL_DBM),
36730686bf7SJohannes Berg 	FLAG(NEED_DTIM_BEFORE_ASSOC),
36830686bf7SJohannes Berg 	FLAG(SPECTRUM_MGMT),
36930686bf7SJohannes Berg 	FLAG(AMPDU_AGGREGATION),
37030686bf7SJohannes Berg 	FLAG(SUPPORTS_PS),
37130686bf7SJohannes Berg 	FLAG(PS_NULLFUNC_STACK),
37230686bf7SJohannes Berg 	FLAG(SUPPORTS_DYNAMIC_PS),
37330686bf7SJohannes Berg 	FLAG(MFP_CAPABLE),
37430686bf7SJohannes Berg 	FLAG(WANT_MONITOR_VIF),
37530686bf7SJohannes Berg 	FLAG(NO_AUTO_VIF),
37630686bf7SJohannes Berg 	FLAG(SW_CRYPTO_CONTROL),
37730686bf7SJohannes Berg 	FLAG(SUPPORT_FAST_XMIT),
37830686bf7SJohannes Berg 	FLAG(REPORTS_TX_ACK_STATUS),
37930686bf7SJohannes Berg 	FLAG(CONNECTION_MONITOR),
38030686bf7SJohannes Berg 	FLAG(QUEUE_CONTROL),
38130686bf7SJohannes Berg 	FLAG(SUPPORTS_PER_STA_GTK),
38230686bf7SJohannes Berg 	FLAG(AP_LINK_PS),
38330686bf7SJohannes Berg 	FLAG(TX_AMPDU_SETUP_IN_HW),
38430686bf7SJohannes Berg 	FLAG(SUPPORTS_RC_TABLE),
38530686bf7SJohannes Berg 	FLAG(P2P_DEV_ADDR_FOR_INTF),
38630686bf7SJohannes Berg 	FLAG(TIMING_BEACON_ONLY),
38730686bf7SJohannes Berg 	FLAG(SUPPORTS_HT_CCK_RATES),
38830686bf7SJohannes Berg 	FLAG(CHANCTX_STA_CSA),
38930686bf7SJohannes Berg 	FLAG(SUPPORTS_CLONED_SKBS),
39030686bf7SJohannes Berg 	FLAG(SINGLE_SCAN_ON_ALL_BANDS),
391b98fb44fSArik Nemtsov 	FLAG(TDLS_WIDER_BW),
39299e7ca44SEmmanuel Grumbach 	FLAG(SUPPORTS_AMSDU_IN_AMPDU),
39335afa588SHelmut Schaa 	FLAG(BEACON_TX_STATUS),
39431104891SJohannes Berg 	FLAG(NEEDS_UNIQUE_STA_ADDR),
395412a6d80SSara Sharon 	FLAG(SUPPORTS_REORDERING_BUFFER),
396c9c5962bSJohannes Berg 	FLAG(USES_RSS),
3976e0456b5SFelix Fietkau 	FLAG(TX_AMSDU),
3986e0456b5SFelix Fietkau 	FLAG(TX_FRAG_LIST),
399e8a24cd4SRajkumar Manoharan 	FLAG(REPORTS_LOW_ACK),
400f3fe4e93SSara Sharon 	FLAG(SUPPORTS_TX_FRAG),
401e2fb1b83SYingying Tang 	FLAG(SUPPORTS_TDLS_BUFFER_STA),
40294ba9271SIlan Peer 	FLAG(DEAUTH_NEED_MGD_TX_PREP),
4037c181f4fSBen Caradoc-Davies 	FLAG(DOESNT_SUPPORT_QOS_NDP),
404adf8ed01SJohannes Berg 	FLAG(BUFF_MMPDU_TXQ),
40509b4a4faSJohannes Berg 	FLAG(SUPPORTS_VHT_EXT_NSS_BW),
4060eeb2b67SSara Sharon 	FLAG(STA_MMPDU_TXQ),
40777f7ffdcSFelix Fietkau 	FLAG(TX_STATUS_NO_AMPDU_LEN),
408caf56338SSara Sharon 	FLAG(SUPPORTS_MULTI_BSSID),
409caf56338SSara Sharon 	FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
410dc3998ecSAlexander Wetzel 	FLAG(AMPDU_KEYBORDER_SUPPORT),
41130686bf7SJohannes Berg #undef FLAG
41230686bf7SJohannes Berg };
41330686bf7SJohannes Berg 
414279daf64SBen Greear static ssize_t hwflags_read(struct file *file, char __user *user_buf,
415279daf64SBen Greear 			    size_t count, loff_t *ppos)
416279daf64SBen Greear {
417279daf64SBen Greear 	struct ieee80211_local *local = file->private_data;
41830686bf7SJohannes Berg 	size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS;
41930686bf7SJohannes Berg 	char *buf = kzalloc(bufsz, GFP_KERNEL);
42030686bf7SJohannes Berg 	char *pos = buf, *end = buf + bufsz - 1;
421279daf64SBen Greear 	ssize_t rv;
42230686bf7SJohannes Berg 	int i;
423279daf64SBen Greear 
424d15b8459SJoe Perches 	if (!buf)
42530686bf7SJohannes Berg 		return -ENOMEM;
426d15b8459SJoe Perches 
42730686bf7SJohannes Berg 	/* fail compilation if somebody adds or removes
42830686bf7SJohannes Berg 	 * a flag without updating the name array above
42930686bf7SJohannes Berg 	 */
43068920c97SAndrey Ryabinin 	BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS);
43130686bf7SJohannes Berg 
43230686bf7SJohannes Berg 	for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) {
43330686bf7SJohannes Berg 		if (test_bit(i, local->hw.flags))
4344633dfc3SMohammed Shafi Shajakhan 			pos += scnprintf(pos, end - pos, "%s\n",
43530686bf7SJohannes Berg 					 hw_flag_names[i]);
43630686bf7SJohannes Berg 	}
437279daf64SBen Greear 
438279daf64SBen Greear 	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
439279daf64SBen Greear 	kfree(buf);
440279daf64SBen Greear 	return rv;
441279daf64SBen Greear }
442199d69f2SBenoit Papillault 
4434a5eccaaSBen Greear static ssize_t misc_read(struct file *file, char __user *user_buf,
4444a5eccaaSBen Greear 			 size_t count, loff_t *ppos)
4454a5eccaaSBen Greear {
4464a5eccaaSBen Greear 	struct ieee80211_local *local = file->private_data;
4474a5eccaaSBen Greear 	/* Max len of each line is 16 characters, plus 9 for 'pending:\n' */
4484a5eccaaSBen Greear 	size_t bufsz = IEEE80211_MAX_QUEUES * 16 + 9;
449b2347a32SDan Carpenter 	char *buf;
450b2347a32SDan Carpenter 	char *pos, *end;
4514a5eccaaSBen Greear 	ssize_t rv;
4524a5eccaaSBen Greear 	int i;
4534a5eccaaSBen Greear 	int ln;
4544a5eccaaSBen Greear 
455b2347a32SDan Carpenter 	buf = kzalloc(bufsz, GFP_KERNEL);
456b2347a32SDan Carpenter 	if (!buf)
457b2347a32SDan Carpenter 		return -ENOMEM;
458b2347a32SDan Carpenter 
459b2347a32SDan Carpenter 	pos = buf;
460b2347a32SDan Carpenter 	end = buf + bufsz - 1;
461b2347a32SDan Carpenter 
4624a5eccaaSBen Greear 	pos += scnprintf(pos, end - pos, "pending:\n");
4634a5eccaaSBen Greear 
4644a5eccaaSBen Greear 	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
4654a5eccaaSBen Greear 		ln = skb_queue_len(&local->pending[i]);
4664a5eccaaSBen Greear 		pos += scnprintf(pos, end - pos, "[%i] %d\n",
4674a5eccaaSBen Greear 				 i, ln);
4684a5eccaaSBen Greear 	}
4694a5eccaaSBen Greear 
4704a5eccaaSBen Greear 	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
4714a5eccaaSBen Greear 	kfree(buf);
4724a5eccaaSBen Greear 	return rv;
4734a5eccaaSBen Greear }
4744a5eccaaSBen Greear 
475db2e6bd4SJohannes Berg static ssize_t queues_read(struct file *file, char __user *user_buf,
476db2e6bd4SJohannes Berg 			   size_t count, loff_t *ppos)
477db2e6bd4SJohannes Berg {
478db2e6bd4SJohannes Berg 	struct ieee80211_local *local = file->private_data;
479db2e6bd4SJohannes Berg 	unsigned long flags;
480db2e6bd4SJohannes Berg 	char buf[IEEE80211_MAX_QUEUES * 20];
481db2e6bd4SJohannes Berg 	int q, res = 0;
482db2e6bd4SJohannes Berg 
483db2e6bd4SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
484db2e6bd4SJohannes Berg 	for (q = 0; q < local->hw.queues; q++)
485db2e6bd4SJohannes Berg 		res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
486db2e6bd4SJohannes Berg 				local->queue_stop_reasons[q],
4873b8d81e0SJohannes Berg 				skb_queue_len(&local->pending[q]));
488db2e6bd4SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
489db2e6bd4SJohannes Berg 
490db2e6bd4SJohannes Berg 	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
491db2e6bd4SJohannes Berg }
492db2e6bd4SJohannes Berg 
493279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(hwflags);
494279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(queues);
4954a5eccaaSBen Greear DEBUGFS_READONLY_FILE_OPS(misc);
496db2e6bd4SJohannes Berg 
497e9f207f0SJiri Benc /* statistics stuff */
498e9f207f0SJiri Benc 
499e9f207f0SJiri Benc static ssize_t format_devstat_counter(struct ieee80211_local *local,
500e9f207f0SJiri Benc 	char __user *userbuf,
501e9f207f0SJiri Benc 	size_t count, loff_t *ppos,
502e9f207f0SJiri Benc 	int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf,
503e9f207f0SJiri Benc 			  int buflen))
504e9f207f0SJiri Benc {
505e9f207f0SJiri Benc 	struct ieee80211_low_level_stats stats;
506e9f207f0SJiri Benc 	char buf[20];
507e9f207f0SJiri Benc 	int res;
508e9f207f0SJiri Benc 
50975636525SJohannes Berg 	rtnl_lock();
51024487981SJohannes Berg 	res = drv_get_stats(local, &stats);
511e9f207f0SJiri Benc 	rtnl_unlock();
51224487981SJohannes Berg 	if (res)
51324487981SJohannes Berg 		return res;
514e9f207f0SJiri Benc 	res = printvalue(&stats, buf, sizeof(buf));
515e9f207f0SJiri Benc 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
516e9f207f0SJiri Benc }
517e9f207f0SJiri Benc 
518e9f207f0SJiri Benc #define DEBUGFS_DEVSTATS_FILE(name)					\
519e9f207f0SJiri Benc static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\
520e9f207f0SJiri Benc 				 char *buf, int buflen)			\
521e9f207f0SJiri Benc {									\
522e9f207f0SJiri Benc 	return scnprintf(buf, buflen, "%u\n", stats->name);		\
523e9f207f0SJiri Benc }									\
524e9f207f0SJiri Benc static ssize_t stats_ ##name## _read(struct file *file,			\
525e9f207f0SJiri Benc 				     char __user *userbuf,		\
526e9f207f0SJiri Benc 				     size_t count, loff_t *ppos)	\
527e9f207f0SJiri Benc {									\
528e9f207f0SJiri Benc 	return format_devstat_counter(file->private_data,		\
529e9f207f0SJiri Benc 				      userbuf,				\
530e9f207f0SJiri Benc 				      count,				\
531e9f207f0SJiri Benc 				      ppos,				\
532e9f207f0SJiri Benc 				      print_devstats_##name);		\
533e9f207f0SJiri Benc }									\
534e9f207f0SJiri Benc 									\
535e9f207f0SJiri Benc static const struct file_operations stats_ ##name## _ops = {		\
536e9f207f0SJiri Benc 	.read = stats_ ##name## _read,					\
537234e3405SStephen Boyd 	.open = simple_open,						\
5382b18ab36SArnd Bergmann 	.llseek = generic_file_llseek,					\
539e9f207f0SJiri Benc };
540e9f207f0SJiri Benc 
541f1160434SJohannes Berg #define DEBUGFS_STATS_ADD(name)					\
542f1160434SJohannes Berg 	debugfs_create_u32(#name, 0400, statsd, &local->name);
5432826bcd8SFelix Fietkau #define DEBUGFS_DEVSTATS_ADD(name)					\
5447bcfaf2fSJohannes Berg 	debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
545e9f207f0SJiri Benc 
546e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
547e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount);
548e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount);
549e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount);
550e9f207f0SJiri Benc 
551e9f207f0SJiri Benc void debugfs_hw_add(struct ieee80211_local *local)
552e9f207f0SJiri Benc {
553e9f207f0SJiri Benc 	struct dentry *phyd = local->hw.wiphy->debugfsdir;
554e9f207f0SJiri Benc 	struct dentry *statsd;
555e9f207f0SJiri Benc 
556e9f207f0SJiri Benc 	if (!phyd)
557e9f207f0SJiri Benc 		return;
558e9f207f0SJiri Benc 
559e9f207f0SJiri Benc 	local->debugfs.keys = debugfs_create_dir("keys", phyd);
560e9f207f0SJiri Benc 
561e9f207f0SJiri Benc 	DEBUGFS_ADD(total_ps_buffered);
562e9f207f0SJiri Benc 	DEBUGFS_ADD(wep_iv);
563bddb2afcSJohannes Berg 	DEBUGFS_ADD(rate_ctrl_alg);
564db2e6bd4SJohannes Berg 	DEBUGFS_ADD(queues);
5654a5eccaaSBen Greear 	DEBUGFS_ADD(misc);
5662ad4814fSJohannes Berg #ifdef CONFIG_PM
567827b1fb4SJohannes Berg 	DEBUGFS_ADD_MODE(reset, 0200);
5682ad4814fSJohannes Berg #endif
569279daf64SBen Greear 	DEBUGFS_ADD(hwflags);
57083bdf2a1SBen Greear 	DEBUGFS_ADD(user_power);
57183bdf2a1SBen Greear 	DEBUGFS_ADD(power);
572c90142a5SThomas Pedersen 	DEBUGFS_ADD(hw_conf);
573276d9e82SJulius Niedworok 	DEBUGFS_ADD_MODE(force_tx_status, 0600);
5748d51dbb8SToke Høiland-Jørgensen 
5758d51dbb8SToke Høiland-Jørgensen 	if (local->ops->wake_tx_queue)
5769399b86cSMichal Kazior 		DEBUGFS_ADD_MODE(aqm, 0600);
577e9f207f0SJiri Benc 
578*e322c07fSLorenzo Bianconi 	DEBUGFS_ADD_MODE(airtime_flags, 0600);
579b4809e94SToke Høiland-Jørgensen 
5803ace10f5SKan Yan 	DEBUGFS_ADD(aql_txq_limit);
5813ace10f5SKan Yan 	debugfs_create_u32("aql_threshold", 0600,
5823ace10f5SKan Yan 			   phyd, &local->aql_threshold);
5833ace10f5SKan Yan 
584e9f207f0SJiri Benc 	statsd = debugfs_create_dir("statistics", phyd);
585e9f207f0SJiri Benc 
586e9f207f0SJiri Benc 	/* if the dir failed, don't put all the other things into the root! */
587e9f207f0SJiri Benc 	if (!statsd)
588e9f207f0SJiri Benc 		return;
589e9f207f0SJiri Benc 
590c206ca67SJohannes Berg #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
591f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount);
592f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount);
593f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11FailedCount);
594f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11RetryCount);
595f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MultipleRetryCount);
596f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11FrameDuplicateCount);
597f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount);
598f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount);
599f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11TransmittedFrameCount);
600f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop);
601f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_queued);
602f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_wep);
603f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc);
604f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port);
605f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop);
606f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_queued);
607f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
608f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
609f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_expand_skb_head);
610f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
611f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
612f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_fragments);
613f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_status_drop);
614e9f207f0SJiri Benc #endif
6152826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount);
6162826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
6172826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
6182826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
619e9f207f0SJiri Benc }
620