xref: /linux/net/mac80211/debugfs.c (revision 9399b86c0e9a058928e8b5f1e695056714814873)
1e9f207f0SJiri Benc /*
2e9f207f0SJiri Benc  * mac80211 debugfs for wireless PHYs
3e9f207f0SJiri Benc  *
4e9f207f0SJiri Benc  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
5d98ad83eSJohannes Berg  * Copyright 2013-2014  Intel Mobile Communications GmbH
6e9f207f0SJiri Benc  *
7e9f207f0SJiri Benc  * GPLv2
8e9f207f0SJiri Benc  *
9e9f207f0SJiri Benc  */
10e9f207f0SJiri Benc 
11e9f207f0SJiri Benc #include <linux/debugfs.h>
12e9f207f0SJiri Benc #include <linux/rtnetlink.h>
13*9399b86cSMichal Kazior #include <linux/vmalloc.h>
14e9f207f0SJiri Benc #include "ieee80211_i.h"
1524487981SJohannes Berg #include "driver-ops.h"
162c8dccc7SJohannes Berg #include "rate.h"
17e9f207f0SJiri Benc #include "debugfs.h"
18e9f207f0SJiri Benc 
1907caf9d6SEliad Peller #define DEBUGFS_FORMAT_BUFFER_SIZE 100
2007caf9d6SEliad Peller 
2107caf9d6SEliad Peller int mac80211_format_buffer(char __user *userbuf, size_t count,
2207caf9d6SEliad Peller 				  loff_t *ppos, char *fmt, ...)
2307caf9d6SEliad Peller {
2407caf9d6SEliad Peller 	va_list args;
2507caf9d6SEliad Peller 	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
2607caf9d6SEliad Peller 	int res;
2707caf9d6SEliad Peller 
2807caf9d6SEliad Peller 	va_start(args, fmt);
2907caf9d6SEliad Peller 	res = vscnprintf(buf, sizeof(buf), fmt, args);
3007caf9d6SEliad Peller 	va_end(args);
3107caf9d6SEliad Peller 
3207caf9d6SEliad Peller 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
3307caf9d6SEliad Peller }
3407caf9d6SEliad Peller 
35279daf64SBen Greear #define DEBUGFS_READONLY_FILE_FN(name, fmt, value...)			\
36e9f207f0SJiri Benc static ssize_t name## _read(struct file *file, char __user *userbuf,	\
37e9f207f0SJiri Benc 			    size_t count, loff_t *ppos)			\
38e9f207f0SJiri Benc {									\
39e9f207f0SJiri Benc 	struct ieee80211_local *local = file->private_data;		\
40e9f207f0SJiri Benc 									\
4107caf9d6SEliad Peller 	return mac80211_format_buffer(userbuf, count, ppos, 		\
4207caf9d6SEliad Peller 				      fmt "\n", ##value);		\
43279daf64SBen Greear }
44279daf64SBen Greear 
45279daf64SBen Greear #define DEBUGFS_READONLY_FILE_OPS(name)			\
46e9f207f0SJiri Benc static const struct file_operations name## _ops = {			\
47e9f207f0SJiri Benc 	.read = name## _read,						\
48234e3405SStephen Boyd 	.open = simple_open,						\
492b18ab36SArnd Bergmann 	.llseek = generic_file_llseek,					\
50e9f207f0SJiri Benc };
51e9f207f0SJiri Benc 
52279daf64SBen Greear #define DEBUGFS_READONLY_FILE(name, fmt, value...)		\
53279daf64SBen Greear 	DEBUGFS_READONLY_FILE_FN(name, fmt, value)		\
54279daf64SBen Greear 	DEBUGFS_READONLY_FILE_OPS(name)
55279daf64SBen Greear 
56e9f207f0SJiri Benc #define DEBUGFS_ADD(name)						\
577bcfaf2fSJohannes Berg 	debugfs_create_file(#name, 0400, phyd, local, &name## _ops);
58e9f207f0SJiri Benc 
59827b1fb4SJohannes Berg #define DEBUGFS_ADD_MODE(name, mode)					\
607bcfaf2fSJohannes Berg 	debugfs_create_file(#name, mode, phyd, local, &name## _ops);
61e9f207f0SJiri Benc 
62e9f207f0SJiri Benc 
6383bdf2a1SBen Greear DEBUGFS_READONLY_FILE(user_power, "%d",
6483bdf2a1SBen Greear 		      local->user_power_level);
6583bdf2a1SBen Greear DEBUGFS_READONLY_FILE(power, "%d",
6683bdf2a1SBen Greear 		      local->hw.conf.power_level);
6707caf9d6SEliad Peller DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",
68e9f207f0SJiri Benc 		      local->total_ps_buffered);
6907caf9d6SEliad Peller DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
70e9f207f0SJiri Benc 		      local->wep_iv & 0xffffff);
7107caf9d6SEliad Peller DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
72af65cd96SJohannes Berg 	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
733b5d665bSAlina Friedrichsen 
74*9399b86cSMichal Kazior struct aqm_info {
75*9399b86cSMichal Kazior 	struct ieee80211_local *local;
76*9399b86cSMichal Kazior 	size_t size;
77*9399b86cSMichal Kazior 	size_t len;
78*9399b86cSMichal Kazior 	unsigned char buf[0];
79*9399b86cSMichal Kazior };
80*9399b86cSMichal Kazior 
81*9399b86cSMichal Kazior #define AQM_HDR_LEN 200
82*9399b86cSMichal Kazior #define AQM_HW_ENTRY_LEN 40
83*9399b86cSMichal Kazior #define AQM_TXQ_ENTRY_LEN 110
84*9399b86cSMichal Kazior 
85*9399b86cSMichal Kazior static int aqm_open(struct inode *inode, struct file *file)
86*9399b86cSMichal Kazior {
87*9399b86cSMichal Kazior 	struct ieee80211_local *local = inode->i_private;
88*9399b86cSMichal Kazior 	struct ieee80211_sub_if_data *sdata;
89*9399b86cSMichal Kazior 	struct sta_info *sta;
90*9399b86cSMichal Kazior 	struct txq_info *txqi;
91*9399b86cSMichal Kazior 	struct fq *fq = &local->fq;
92*9399b86cSMichal Kazior 	struct aqm_info *info = NULL;
93*9399b86cSMichal Kazior 	int len = 0;
94*9399b86cSMichal Kazior 	int i;
95*9399b86cSMichal Kazior 
96*9399b86cSMichal Kazior 	if (!local->ops->wake_tx_queue)
97*9399b86cSMichal Kazior 		return -EOPNOTSUPP;
98*9399b86cSMichal Kazior 
99*9399b86cSMichal Kazior 	len += AQM_HDR_LEN;
100*9399b86cSMichal Kazior 	len += 6 * AQM_HW_ENTRY_LEN;
101*9399b86cSMichal Kazior 
102*9399b86cSMichal Kazior 	rcu_read_lock();
103*9399b86cSMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list)
104*9399b86cSMichal Kazior 		len += AQM_TXQ_ENTRY_LEN;
105*9399b86cSMichal Kazior 	list_for_each_entry_rcu(sta, &local->sta_list, list)
106*9399b86cSMichal Kazior 		len += AQM_TXQ_ENTRY_LEN * ARRAY_SIZE(sta->sta.txq);
107*9399b86cSMichal Kazior 	rcu_read_unlock();
108*9399b86cSMichal Kazior 
109*9399b86cSMichal Kazior 	info = vmalloc(len);
110*9399b86cSMichal Kazior 	if (!info)
111*9399b86cSMichal Kazior 		return -ENOMEM;
112*9399b86cSMichal Kazior 
113*9399b86cSMichal Kazior 	spin_lock_bh(&local->fq.lock);
114*9399b86cSMichal Kazior 	rcu_read_lock();
115*9399b86cSMichal Kazior 
116*9399b86cSMichal Kazior 	file->private_data = info;
117*9399b86cSMichal Kazior 	info->local = local;
118*9399b86cSMichal Kazior 	info->size = len;
119*9399b86cSMichal Kazior 	len = 0;
120*9399b86cSMichal Kazior 
121*9399b86cSMichal Kazior 	len += scnprintf(info->buf + len, info->size - len,
122*9399b86cSMichal Kazior 			 "* hw\n"
123*9399b86cSMichal Kazior 			 "access name value\n"
124*9399b86cSMichal Kazior 			 "R fq_flows_cnt %u\n"
125*9399b86cSMichal Kazior 			 "R fq_backlog %u\n"
126*9399b86cSMichal Kazior 			 "R fq_overlimit %u\n"
127*9399b86cSMichal Kazior 			 "R fq_collisions %u\n"
128*9399b86cSMichal Kazior 			 "RW fq_limit %u\n"
129*9399b86cSMichal Kazior 			 "RW fq_quantum %u\n",
130*9399b86cSMichal Kazior 			 fq->flows_cnt,
131*9399b86cSMichal Kazior 			 fq->backlog,
132*9399b86cSMichal Kazior 			 fq->overlimit,
133*9399b86cSMichal Kazior 			 fq->collisions,
134*9399b86cSMichal Kazior 			 fq->limit,
135*9399b86cSMichal Kazior 			 fq->quantum);
136*9399b86cSMichal Kazior 
137*9399b86cSMichal Kazior 	len += scnprintf(info->buf + len,
138*9399b86cSMichal Kazior 			 info->size - len,
139*9399b86cSMichal Kazior 			 "* vif\n"
140*9399b86cSMichal Kazior 			 "ifname addr ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n");
141*9399b86cSMichal Kazior 
142*9399b86cSMichal Kazior 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
143*9399b86cSMichal Kazior 		txqi = to_txq_info(sdata->vif.txq);
144*9399b86cSMichal Kazior 		len += scnprintf(info->buf + len, info->size - len,
145*9399b86cSMichal Kazior 				 "%s %pM %u %u %u %u %u %u %u %u\n",
146*9399b86cSMichal Kazior 				 sdata->name,
147*9399b86cSMichal Kazior 				 sdata->vif.addr,
148*9399b86cSMichal Kazior 				 txqi->txq.ac,
149*9399b86cSMichal Kazior 				 txqi->tin.backlog_bytes,
150*9399b86cSMichal Kazior 				 txqi->tin.backlog_packets,
151*9399b86cSMichal Kazior 				 txqi->tin.flows,
152*9399b86cSMichal Kazior 				 txqi->tin.overlimit,
153*9399b86cSMichal Kazior 				 txqi->tin.collisions,
154*9399b86cSMichal Kazior 				 txqi->tin.tx_bytes,
155*9399b86cSMichal Kazior 				 txqi->tin.tx_packets);
156*9399b86cSMichal Kazior 	}
157*9399b86cSMichal Kazior 
158*9399b86cSMichal Kazior 	len += scnprintf(info->buf + len,
159*9399b86cSMichal Kazior 			 info->size - len,
160*9399b86cSMichal Kazior 			 "* sta\n"
161*9399b86cSMichal Kazior 			 "ifname addr tid ac backlog-bytes backlog-packets flows overlimit collisions tx-bytes tx-packets\n");
162*9399b86cSMichal Kazior 
163*9399b86cSMichal Kazior 	list_for_each_entry_rcu(sta, &local->sta_list, list) {
164*9399b86cSMichal Kazior 		sdata = sta->sdata;
165*9399b86cSMichal Kazior 		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
166*9399b86cSMichal Kazior 			txqi = to_txq_info(sta->sta.txq[i]);
167*9399b86cSMichal Kazior 			len += scnprintf(info->buf + len, info->size - len,
168*9399b86cSMichal Kazior 					 "%s %pM %d %d %u %u %u %u %u %u %u\n",
169*9399b86cSMichal Kazior 					 sdata->name,
170*9399b86cSMichal Kazior 					 sta->sta.addr,
171*9399b86cSMichal Kazior 					 txqi->txq.tid,
172*9399b86cSMichal Kazior 					 txqi->txq.ac,
173*9399b86cSMichal Kazior 					 txqi->tin.backlog_bytes,
174*9399b86cSMichal Kazior 					 txqi->tin.backlog_packets,
175*9399b86cSMichal Kazior 					 txqi->tin.flows,
176*9399b86cSMichal Kazior 					 txqi->tin.overlimit,
177*9399b86cSMichal Kazior 					 txqi->tin.collisions,
178*9399b86cSMichal Kazior 					 txqi->tin.tx_bytes,
179*9399b86cSMichal Kazior 					 txqi->tin.tx_packets);
180*9399b86cSMichal Kazior 		}
181*9399b86cSMichal Kazior 	}
182*9399b86cSMichal Kazior 
183*9399b86cSMichal Kazior 	info->len = len;
184*9399b86cSMichal Kazior 
185*9399b86cSMichal Kazior 	rcu_read_unlock();
186*9399b86cSMichal Kazior 	spin_unlock_bh(&local->fq.lock);
187*9399b86cSMichal Kazior 
188*9399b86cSMichal Kazior 	return 0;
189*9399b86cSMichal Kazior }
190*9399b86cSMichal Kazior 
191*9399b86cSMichal Kazior static int aqm_release(struct inode *inode, struct file *file)
192*9399b86cSMichal Kazior {
193*9399b86cSMichal Kazior 	vfree(file->private_data);
194*9399b86cSMichal Kazior 	return 0;
195*9399b86cSMichal Kazior }
196*9399b86cSMichal Kazior 
197*9399b86cSMichal Kazior static ssize_t aqm_read(struct file *file,
198*9399b86cSMichal Kazior 			char __user *user_buf,
199*9399b86cSMichal Kazior 			size_t count,
200*9399b86cSMichal Kazior 			loff_t *ppos)
201*9399b86cSMichal Kazior {
202*9399b86cSMichal Kazior 	struct aqm_info *info = file->private_data;
203*9399b86cSMichal Kazior 
204*9399b86cSMichal Kazior 	return simple_read_from_buffer(user_buf, count, ppos,
205*9399b86cSMichal Kazior 				       info->buf, info->len);
206*9399b86cSMichal Kazior }
207*9399b86cSMichal Kazior 
208*9399b86cSMichal Kazior static ssize_t aqm_write(struct file *file,
209*9399b86cSMichal Kazior 			 const char __user *user_buf,
210*9399b86cSMichal Kazior 			 size_t count,
211*9399b86cSMichal Kazior 			 loff_t *ppos)
212*9399b86cSMichal Kazior {
213*9399b86cSMichal Kazior 	struct aqm_info *info = file->private_data;
214*9399b86cSMichal Kazior 	struct ieee80211_local *local = info->local;
215*9399b86cSMichal Kazior 	char buf[100];
216*9399b86cSMichal Kazior 	size_t len;
217*9399b86cSMichal Kazior 
218*9399b86cSMichal Kazior 	if (count > sizeof(buf))
219*9399b86cSMichal Kazior 		return -EINVAL;
220*9399b86cSMichal Kazior 
221*9399b86cSMichal Kazior 	if (copy_from_user(buf, user_buf, count))
222*9399b86cSMichal Kazior 		return -EFAULT;
223*9399b86cSMichal Kazior 
224*9399b86cSMichal Kazior 	buf[sizeof(buf) - 1] = '\0';
225*9399b86cSMichal Kazior 	len = strlen(buf);
226*9399b86cSMichal Kazior 	if (len > 0 && buf[len-1] == '\n')
227*9399b86cSMichal Kazior 		buf[len-1] = 0;
228*9399b86cSMichal Kazior 
229*9399b86cSMichal Kazior 	if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1)
230*9399b86cSMichal Kazior 		return count;
231*9399b86cSMichal Kazior 	else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1)
232*9399b86cSMichal Kazior 		return count;
233*9399b86cSMichal Kazior 
234*9399b86cSMichal Kazior 	return -EINVAL;
235*9399b86cSMichal Kazior }
236*9399b86cSMichal Kazior 
237*9399b86cSMichal Kazior static const struct file_operations aqm_ops = {
238*9399b86cSMichal Kazior 	.write = aqm_write,
239*9399b86cSMichal Kazior 	.read = aqm_read,
240*9399b86cSMichal Kazior 	.open = aqm_open,
241*9399b86cSMichal Kazior 	.release = aqm_release,
242*9399b86cSMichal Kazior 	.llseek = default_llseek,
243*9399b86cSMichal Kazior };
244*9399b86cSMichal Kazior 
2452ad4814fSJohannes Berg #ifdef CONFIG_PM
246827b1fb4SJohannes Berg static ssize_t reset_write(struct file *file, const char __user *user_buf,
247827b1fb4SJohannes Berg 			   size_t count, loff_t *ppos)
248827b1fb4SJohannes Berg {
249827b1fb4SJohannes Berg 	struct ieee80211_local *local = file->private_data;
250827b1fb4SJohannes Berg 
251827b1fb4SJohannes Berg 	rtnl_lock();
252eecc4800SJohannes Berg 	__ieee80211_suspend(&local->hw, NULL);
253827b1fb4SJohannes Berg 	__ieee80211_resume(&local->hw);
254827b1fb4SJohannes Berg 	rtnl_unlock();
255827b1fb4SJohannes Berg 
256827b1fb4SJohannes Berg 	return count;
257827b1fb4SJohannes Berg }
258827b1fb4SJohannes Berg 
259827b1fb4SJohannes Berg static const struct file_operations reset_ops = {
260827b1fb4SJohannes Berg 	.write = reset_write,
261234e3405SStephen Boyd 	.open = simple_open,
2626038f373SArnd Bergmann 	.llseek = noop_llseek,
263827b1fb4SJohannes Berg };
2642ad4814fSJohannes Berg #endif
265827b1fb4SJohannes Berg 
26668920c97SAndrey Ryabinin static const char *hw_flag_names[] = {
26730686bf7SJohannes Berg #define FLAG(F)	[IEEE80211_HW_##F] = #F
26830686bf7SJohannes Berg 	FLAG(HAS_RATE_CONTROL),
26930686bf7SJohannes Berg 	FLAG(RX_INCLUDES_FCS),
27030686bf7SJohannes Berg 	FLAG(HOST_BROADCAST_PS_BUFFERING),
27130686bf7SJohannes Berg 	FLAG(SIGNAL_UNSPEC),
27230686bf7SJohannes Berg 	FLAG(SIGNAL_DBM),
27330686bf7SJohannes Berg 	FLAG(NEED_DTIM_BEFORE_ASSOC),
27430686bf7SJohannes Berg 	FLAG(SPECTRUM_MGMT),
27530686bf7SJohannes Berg 	FLAG(AMPDU_AGGREGATION),
27630686bf7SJohannes Berg 	FLAG(SUPPORTS_PS),
27730686bf7SJohannes Berg 	FLAG(PS_NULLFUNC_STACK),
27830686bf7SJohannes Berg 	FLAG(SUPPORTS_DYNAMIC_PS),
27930686bf7SJohannes Berg 	FLAG(MFP_CAPABLE),
28030686bf7SJohannes Berg 	FLAG(WANT_MONITOR_VIF),
28130686bf7SJohannes Berg 	FLAG(NO_AUTO_VIF),
28230686bf7SJohannes Berg 	FLAG(SW_CRYPTO_CONTROL),
28330686bf7SJohannes Berg 	FLAG(SUPPORT_FAST_XMIT),
28430686bf7SJohannes Berg 	FLAG(REPORTS_TX_ACK_STATUS),
28530686bf7SJohannes Berg 	FLAG(CONNECTION_MONITOR),
28630686bf7SJohannes Berg 	FLAG(QUEUE_CONTROL),
28730686bf7SJohannes Berg 	FLAG(SUPPORTS_PER_STA_GTK),
28830686bf7SJohannes Berg 	FLAG(AP_LINK_PS),
28930686bf7SJohannes Berg 	FLAG(TX_AMPDU_SETUP_IN_HW),
29030686bf7SJohannes Berg 	FLAG(SUPPORTS_RC_TABLE),
29130686bf7SJohannes Berg 	FLAG(P2P_DEV_ADDR_FOR_INTF),
29230686bf7SJohannes Berg 	FLAG(TIMING_BEACON_ONLY),
29330686bf7SJohannes Berg 	FLAG(SUPPORTS_HT_CCK_RATES),
29430686bf7SJohannes Berg 	FLAG(CHANCTX_STA_CSA),
29530686bf7SJohannes Berg 	FLAG(SUPPORTS_CLONED_SKBS),
29630686bf7SJohannes Berg 	FLAG(SINGLE_SCAN_ON_ALL_BANDS),
297b98fb44fSArik Nemtsov 	FLAG(TDLS_WIDER_BW),
29899e7ca44SEmmanuel Grumbach 	FLAG(SUPPORTS_AMSDU_IN_AMPDU),
29935afa588SHelmut Schaa 	FLAG(BEACON_TX_STATUS),
30031104891SJohannes Berg 	FLAG(NEEDS_UNIQUE_STA_ADDR),
301412a6d80SSara Sharon 	FLAG(SUPPORTS_REORDERING_BUFFER),
302c9c5962bSJohannes Berg 	FLAG(USES_RSS),
3036e0456b5SFelix Fietkau 	FLAG(TX_AMSDU),
3046e0456b5SFelix Fietkau 	FLAG(TX_FRAG_LIST),
30530686bf7SJohannes Berg #undef FLAG
30630686bf7SJohannes Berg };
30730686bf7SJohannes Berg 
308279daf64SBen Greear static ssize_t hwflags_read(struct file *file, char __user *user_buf,
309279daf64SBen Greear 			    size_t count, loff_t *ppos)
310279daf64SBen Greear {
311279daf64SBen Greear 	struct ieee80211_local *local = file->private_data;
31230686bf7SJohannes Berg 	size_t bufsz = 30 * NUM_IEEE80211_HW_FLAGS;
31330686bf7SJohannes Berg 	char *buf = kzalloc(bufsz, GFP_KERNEL);
31430686bf7SJohannes Berg 	char *pos = buf, *end = buf + bufsz - 1;
315279daf64SBen Greear 	ssize_t rv;
31630686bf7SJohannes Berg 	int i;
317279daf64SBen Greear 
318d15b8459SJoe Perches 	if (!buf)
31930686bf7SJohannes Berg 		return -ENOMEM;
320d15b8459SJoe Perches 
32130686bf7SJohannes Berg 	/* fail compilation if somebody adds or removes
32230686bf7SJohannes Berg 	 * a flag without updating the name array above
32330686bf7SJohannes Berg 	 */
32468920c97SAndrey Ryabinin 	BUILD_BUG_ON(ARRAY_SIZE(hw_flag_names) != NUM_IEEE80211_HW_FLAGS);
32530686bf7SJohannes Berg 
32630686bf7SJohannes Berg 	for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) {
32730686bf7SJohannes Berg 		if (test_bit(i, local->hw.flags))
3284633dfc3SMohammed Shafi Shajakhan 			pos += scnprintf(pos, end - pos, "%s\n",
32930686bf7SJohannes Berg 					 hw_flag_names[i]);
33030686bf7SJohannes Berg 	}
331279daf64SBen Greear 
332279daf64SBen Greear 	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
333279daf64SBen Greear 	kfree(buf);
334279daf64SBen Greear 	return rv;
335279daf64SBen Greear }
336199d69f2SBenoit Papillault 
337db2e6bd4SJohannes Berg static ssize_t queues_read(struct file *file, char __user *user_buf,
338db2e6bd4SJohannes Berg 			   size_t count, loff_t *ppos)
339db2e6bd4SJohannes Berg {
340db2e6bd4SJohannes Berg 	struct ieee80211_local *local = file->private_data;
341db2e6bd4SJohannes Berg 	unsigned long flags;
342db2e6bd4SJohannes Berg 	char buf[IEEE80211_MAX_QUEUES * 20];
343db2e6bd4SJohannes Berg 	int q, res = 0;
344db2e6bd4SJohannes Berg 
345db2e6bd4SJohannes Berg 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
346db2e6bd4SJohannes Berg 	for (q = 0; q < local->hw.queues; q++)
347db2e6bd4SJohannes Berg 		res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
348db2e6bd4SJohannes Berg 				local->queue_stop_reasons[q],
3493b8d81e0SJohannes Berg 				skb_queue_len(&local->pending[q]));
350db2e6bd4SJohannes Berg 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
351db2e6bd4SJohannes Berg 
352db2e6bd4SJohannes Berg 	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
353db2e6bd4SJohannes Berg }
354db2e6bd4SJohannes Berg 
355279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(hwflags);
356279daf64SBen Greear DEBUGFS_READONLY_FILE_OPS(queues);
357db2e6bd4SJohannes Berg 
358e9f207f0SJiri Benc /* statistics stuff */
359e9f207f0SJiri Benc 
360e9f207f0SJiri Benc static ssize_t format_devstat_counter(struct ieee80211_local *local,
361e9f207f0SJiri Benc 	char __user *userbuf,
362e9f207f0SJiri Benc 	size_t count, loff_t *ppos,
363e9f207f0SJiri Benc 	int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf,
364e9f207f0SJiri Benc 			  int buflen))
365e9f207f0SJiri Benc {
366e9f207f0SJiri Benc 	struct ieee80211_low_level_stats stats;
367e9f207f0SJiri Benc 	char buf[20];
368e9f207f0SJiri Benc 	int res;
369e9f207f0SJiri Benc 
37075636525SJohannes Berg 	rtnl_lock();
37124487981SJohannes Berg 	res = drv_get_stats(local, &stats);
372e9f207f0SJiri Benc 	rtnl_unlock();
37324487981SJohannes Berg 	if (res)
37424487981SJohannes Berg 		return res;
375e9f207f0SJiri Benc 	res = printvalue(&stats, buf, sizeof(buf));
376e9f207f0SJiri Benc 	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
377e9f207f0SJiri Benc }
378e9f207f0SJiri Benc 
379e9f207f0SJiri Benc #define DEBUGFS_DEVSTATS_FILE(name)					\
380e9f207f0SJiri Benc static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\
381e9f207f0SJiri Benc 				 char *buf, int buflen)			\
382e9f207f0SJiri Benc {									\
383e9f207f0SJiri Benc 	return scnprintf(buf, buflen, "%u\n", stats->name);		\
384e9f207f0SJiri Benc }									\
385e9f207f0SJiri Benc static ssize_t stats_ ##name## _read(struct file *file,			\
386e9f207f0SJiri Benc 				     char __user *userbuf,		\
387e9f207f0SJiri Benc 				     size_t count, loff_t *ppos)	\
388e9f207f0SJiri Benc {									\
389e9f207f0SJiri Benc 	return format_devstat_counter(file->private_data,		\
390e9f207f0SJiri Benc 				      userbuf,				\
391e9f207f0SJiri Benc 				      count,				\
392e9f207f0SJiri Benc 				      ppos,				\
393e9f207f0SJiri Benc 				      print_devstats_##name);		\
394e9f207f0SJiri Benc }									\
395e9f207f0SJiri Benc 									\
396e9f207f0SJiri Benc static const struct file_operations stats_ ##name## _ops = {		\
397e9f207f0SJiri Benc 	.read = stats_ ##name## _read,					\
398234e3405SStephen Boyd 	.open = simple_open,						\
3992b18ab36SArnd Bergmann 	.llseek = generic_file_llseek,					\
400e9f207f0SJiri Benc };
401e9f207f0SJiri Benc 
402f1160434SJohannes Berg #define DEBUGFS_STATS_ADD(name)					\
403f1160434SJohannes Berg 	debugfs_create_u32(#name, 0400, statsd, &local->name);
4042826bcd8SFelix Fietkau #define DEBUGFS_DEVSTATS_ADD(name)					\
4057bcfaf2fSJohannes Berg 	debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
406e9f207f0SJiri Benc 
407e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
408e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount);
409e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount);
410e9f207f0SJiri Benc DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount);
411e9f207f0SJiri Benc 
412e9f207f0SJiri Benc void debugfs_hw_add(struct ieee80211_local *local)
413e9f207f0SJiri Benc {
414e9f207f0SJiri Benc 	struct dentry *phyd = local->hw.wiphy->debugfsdir;
415e9f207f0SJiri Benc 	struct dentry *statsd;
416e9f207f0SJiri Benc 
417e9f207f0SJiri Benc 	if (!phyd)
418e9f207f0SJiri Benc 		return;
419e9f207f0SJiri Benc 
420e9f207f0SJiri Benc 	local->debugfs.keys = debugfs_create_dir("keys", phyd);
421e9f207f0SJiri Benc 
422e9f207f0SJiri Benc 	DEBUGFS_ADD(total_ps_buffered);
423e9f207f0SJiri Benc 	DEBUGFS_ADD(wep_iv);
424db2e6bd4SJohannes Berg 	DEBUGFS_ADD(queues);
4252ad4814fSJohannes Berg #ifdef CONFIG_PM
426827b1fb4SJohannes Berg 	DEBUGFS_ADD_MODE(reset, 0200);
4272ad4814fSJohannes Berg #endif
428279daf64SBen Greear 	DEBUGFS_ADD(hwflags);
42983bdf2a1SBen Greear 	DEBUGFS_ADD(user_power);
43083bdf2a1SBen Greear 	DEBUGFS_ADD(power);
431*9399b86cSMichal Kazior 	DEBUGFS_ADD_MODE(aqm, 0600);
432e9f207f0SJiri Benc 
433e9f207f0SJiri Benc 	statsd = debugfs_create_dir("statistics", phyd);
434e9f207f0SJiri Benc 
435e9f207f0SJiri Benc 	/* if the dir failed, don't put all the other things into the root! */
436e9f207f0SJiri Benc 	if (!statsd)
437e9f207f0SJiri Benc 		return;
438e9f207f0SJiri Benc 
439c206ca67SJohannes Berg #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
440f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11TransmittedFragmentCount);
441f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MulticastTransmittedFrameCount);
442f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11FailedCount);
443f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11RetryCount);
444f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MultipleRetryCount);
445f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11FrameDuplicateCount);
446f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11ReceivedFragmentCount);
447f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11MulticastReceivedFrameCount);
448f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(dot11TransmittedFrameCount);
449f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop);
450f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_queued);
451f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_wep);
452f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc);
453f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port);
454f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop);
455f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_queued);
456f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
457f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
458f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_expand_skb_head);
459f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
460f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
461f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(rx_handlers_fragments);
462f1160434SJohannes Berg 	DEBUGFS_STATS_ADD(tx_status_drop);
463e9f207f0SJiri Benc #endif
4642826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount);
4652826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
4662826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
4672826bcd8SFelix Fietkau 	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
468e9f207f0SJiri Benc }
469