1
2/*
3 * mac80211 debugfs for wireless PHYs
4 *
5 * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
6 * Copyright 2013-2014  Intel Mobile Communications GmbH
7 *
8 * GPLv2
9 *
10 */
11
12#include <linux/debugfs.h>
13#include <linux/rtnetlink.h>
14#include "ieee80211_i.h"
15#include "driver-ops.h"
16#include "rate.h"
17#include "debugfs.h"
18
19#define DEBUGFS_FORMAT_BUFFER_SIZE 100
20
21int mac80211_format_buffer(char __user *userbuf, size_t count,
22				  loff_t *ppos, char *fmt, ...)
23{
24	va_list args;
25	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
26	int res;
27
28	va_start(args, fmt);
29	res = vscnprintf(buf, sizeof(buf), fmt, args);
30	va_end(args);
31
32	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
33}
34
35#define DEBUGFS_READONLY_FILE_FN(name, fmt, value...)			\
36static ssize_t name## _read(struct file *file, char __user *userbuf,	\
37			    size_t count, loff_t *ppos)			\
38{									\
39	struct ieee80211_local *local = file->private_data;		\
40									\
41	return mac80211_format_buffer(userbuf, count, ppos, 		\
42				      fmt "\n", ##value);		\
43}
44
45#define DEBUGFS_READONLY_FILE_OPS(name)			\
46static const struct file_operations name## _ops = {			\
47	.read = name## _read,						\
48	.open = simple_open,						\
49	.llseek = generic_file_llseek,					\
50};
51
52#define DEBUGFS_READONLY_FILE(name, fmt, value...)		\
53	DEBUGFS_READONLY_FILE_FN(name, fmt, value)		\
54	DEBUGFS_READONLY_FILE_OPS(name)
55
56#define DEBUGFS_ADD(name)						\
57	debugfs_create_file(#name, 0400, phyd, local, &name## _ops);
58
59#define DEBUGFS_ADD_MODE(name, mode)					\
60	debugfs_create_file(#name, mode, phyd, local, &name## _ops);
61
62
63DEBUGFS_READONLY_FILE(user_power, "%d",
64		      local->user_power_level);
65DEBUGFS_READONLY_FILE(power, "%d",
66		      local->hw.conf.power_level);
67DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",
68		      local->total_ps_buffered);
69DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
70		      local->wep_iv & 0xffffff);
71DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
72	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
73
74#ifdef CONFIG_PM
75static ssize_t reset_write(struct file *file, const char __user *user_buf,
76			   size_t count, loff_t *ppos)
77{
78	struct ieee80211_local *local = file->private_data;
79
80	rtnl_lock();
81	__ieee80211_suspend(&local->hw, NULL);
82	__ieee80211_resume(&local->hw);
83	rtnl_unlock();
84
85	return count;
86}
87
88static const struct file_operations reset_ops = {
89	.write = reset_write,
90	.open = simple_open,
91	.llseek = noop_llseek,
92};
93#endif
94
95static ssize_t hwflags_read(struct file *file, char __user *user_buf,
96			    size_t count, loff_t *ppos)
97{
98	struct ieee80211_local *local = file->private_data;
99	int mxln = 500;
100	ssize_t rv;
101	char *buf = kzalloc(mxln, GFP_KERNEL);
102	int sf = 0; /* how many written so far */
103
104	if (!buf)
105		return 0;
106
107	sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
108	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
109		sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
110	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
111		sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
112	if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
113		sf += scnprintf(buf + sf, mxln - sf,
114				"HOST_BCAST_PS_BUFFERING\n");
115	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
116		sf += scnprintf(buf + sf, mxln - sf,
117				"2GHZ_SHORT_SLOT_INCAPABLE\n");
118	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
119		sf += scnprintf(buf + sf, mxln - sf,
120				"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
121	if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
122		sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
123	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
124		sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
125	if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
126		sf += scnprintf(buf + sf, mxln - sf,
127				"NEED_DTIM_BEFORE_ASSOC\n");
128	if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
129		sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
130	if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
131		sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
132	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
133		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
134	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
135		sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
136	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
137		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
138	if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
139		sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
140	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
141		sf += scnprintf(buf + sf, mxln - sf,
142				"REPORTS_TX_ACK_STATUS\n");
143	if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
144		sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
145	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
146		sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
147	if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
148		sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
149	if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)
150		sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
151
152	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
153	kfree(buf);
154	return rv;
155}
156
157static ssize_t queues_read(struct file *file, char __user *user_buf,
158			   size_t count, loff_t *ppos)
159{
160	struct ieee80211_local *local = file->private_data;
161	unsigned long flags;
162	char buf[IEEE80211_MAX_QUEUES * 20];
163	int q, res = 0;
164
165	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
166	for (q = 0; q < local->hw.queues; q++)
167		res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
168				local->queue_stop_reasons[q],
169				skb_queue_len(&local->pending[q]));
170	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
171
172	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
173}
174
175DEBUGFS_READONLY_FILE_OPS(hwflags);
176DEBUGFS_READONLY_FILE_OPS(queues);
177
178/* statistics stuff */
179
180static ssize_t format_devstat_counter(struct ieee80211_local *local,
181	char __user *userbuf,
182	size_t count, loff_t *ppos,
183	int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf,
184			  int buflen))
185{
186	struct ieee80211_low_level_stats stats;
187	char buf[20];
188	int res;
189
190	rtnl_lock();
191	res = drv_get_stats(local, &stats);
192	rtnl_unlock();
193	if (res)
194		return res;
195	res = printvalue(&stats, buf, sizeof(buf));
196	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
197}
198
199#define DEBUGFS_DEVSTATS_FILE(name)					\
200static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\
201				 char *buf, int buflen)			\
202{									\
203	return scnprintf(buf, buflen, "%u\n", stats->name);		\
204}									\
205static ssize_t stats_ ##name## _read(struct file *file,			\
206				     char __user *userbuf,		\
207				     size_t count, loff_t *ppos)	\
208{									\
209	return format_devstat_counter(file->private_data,		\
210				      userbuf,				\
211				      count,				\
212				      ppos,				\
213				      print_devstats_##name);		\
214}									\
215									\
216static const struct file_operations stats_ ##name## _ops = {		\
217	.read = stats_ ##name## _read,					\
218	.open = simple_open,						\
219	.llseek = generic_file_llseek,					\
220};
221
222#define DEBUGFS_STATS_ADD(name, field)					\
223	debugfs_create_u32(#name, 0400, statsd, (u32 *) &field);
224#define DEBUGFS_DEVSTATS_ADD(name)					\
225	debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
226
227DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
228DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount);
229DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount);
230DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount);
231
232void debugfs_hw_add(struct ieee80211_local *local)
233{
234	struct dentry *phyd = local->hw.wiphy->debugfsdir;
235	struct dentry *statsd;
236
237	if (!phyd)
238		return;
239
240	local->debugfs.keys = debugfs_create_dir("keys", phyd);
241
242	DEBUGFS_ADD(total_ps_buffered);
243	DEBUGFS_ADD(wep_iv);
244	DEBUGFS_ADD(queues);
245#ifdef CONFIG_PM
246	DEBUGFS_ADD_MODE(reset, 0200);
247#endif
248	DEBUGFS_ADD(hwflags);
249	DEBUGFS_ADD(user_power);
250	DEBUGFS_ADD(power);
251
252	statsd = debugfs_create_dir("statistics", phyd);
253
254	/* if the dir failed, don't put all the other things into the root! */
255	if (!statsd)
256		return;
257
258	DEBUGFS_STATS_ADD(transmitted_fragment_count,
259		local->dot11TransmittedFragmentCount);
260	DEBUGFS_STATS_ADD(multicast_transmitted_frame_count,
261		local->dot11MulticastTransmittedFrameCount);
262	DEBUGFS_STATS_ADD(failed_count, local->dot11FailedCount);
263	DEBUGFS_STATS_ADD(retry_count, local->dot11RetryCount);
264	DEBUGFS_STATS_ADD(multiple_retry_count,
265		local->dot11MultipleRetryCount);
266	DEBUGFS_STATS_ADD(frame_duplicate_count,
267		local->dot11FrameDuplicateCount);
268	DEBUGFS_STATS_ADD(received_fragment_count,
269		local->dot11ReceivedFragmentCount);
270	DEBUGFS_STATS_ADD(multicast_received_frame_count,
271		local->dot11MulticastReceivedFrameCount);
272	DEBUGFS_STATS_ADD(transmitted_frame_count,
273		local->dot11TransmittedFrameCount);
274#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
275	DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
276	DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
277	DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
278		local->tx_handlers_drop_fragment);
279	DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
280		local->tx_handlers_drop_wep);
281	DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc,
282		local->tx_handlers_drop_not_assoc);
283	DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port,
284		local->tx_handlers_drop_unauth_port);
285	DEBUGFS_STATS_ADD(rx_handlers_drop, local->rx_handlers_drop);
286	DEBUGFS_STATS_ADD(rx_handlers_queued, local->rx_handlers_queued);
287	DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc,
288		local->rx_handlers_drop_nullfunc);
289	DEBUGFS_STATS_ADD(rx_handlers_drop_defrag,
290		local->rx_handlers_drop_defrag);
291	DEBUGFS_STATS_ADD(rx_handlers_drop_short,
292		local->rx_handlers_drop_short);
293	DEBUGFS_STATS_ADD(tx_expand_skb_head,
294		local->tx_expand_skb_head);
295	DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned,
296		local->tx_expand_skb_head_cloned);
297	DEBUGFS_STATS_ADD(rx_expand_skb_head,
298		local->rx_expand_skb_head);
299	DEBUGFS_STATS_ADD(rx_expand_skb_head2,
300		local->rx_expand_skb_head2);
301	DEBUGFS_STATS_ADD(rx_handlers_fragments,
302		local->rx_handlers_fragments);
303	DEBUGFS_STATS_ADD(tx_status_drop,
304		local->tx_status_drop);
305#endif
306	DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount);
307	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
308	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
309	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
310}
311