debugfs.c 15.2 KB
Newer Older
1

J
Jiri Benc 已提交
2 3 4 5 6 7 8 9 10 11 12 13
/*
 * mac80211 debugfs for wireless PHYs
 *
 * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
 *
 * GPLv2
 *
 */

#include <linux/debugfs.h>
#include <linux/rtnetlink.h>
#include "ieee80211_i.h"
14
#include "driver-ops.h"
J
Johannes Berg 已提交
15
#include "rate.h"
J
Jiri Benc 已提交
16 17 18 19 20 21 22 23
#include "debugfs.h"

int mac80211_open_file_generic(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	return 0;
}

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
#define DEBUGFS_FORMAT_BUFFER_SIZE 100

int mac80211_format_buffer(char __user *userbuf, size_t count,
				  loff_t *ppos, char *fmt, ...)
{
	va_list args;
	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
	int res;

	va_start(args, fmt);
	res = vscnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);

	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}

B
Ben Greear 已提交
40
#define DEBUGFS_READONLY_FILE_FN(name, fmt, value...)			\
J
Jiri Benc 已提交
41 42 43 44 45
static ssize_t name## _read(struct file *file, char __user *userbuf,	\
			    size_t count, loff_t *ppos)			\
{									\
	struct ieee80211_local *local = file->private_data;		\
									\
46 47
	return mac80211_format_buffer(userbuf, count, ppos, 		\
				      fmt "\n", ##value);		\
B
Ben Greear 已提交
48 49 50
}

#define DEBUGFS_READONLY_FILE_OPS(name)			\
J
Jiri Benc 已提交
51 52 53
static const struct file_operations name## _ops = {			\
	.read = name## _read,						\
	.open = mac80211_open_file_generic,				\
54
	.llseek = generic_file_llseek,					\
J
Jiri Benc 已提交
55 56
};

B
Ben Greear 已提交
57 58 59 60
#define DEBUGFS_READONLY_FILE(name, fmt, value...)		\
	DEBUGFS_READONLY_FILE_FN(name, fmt, value)		\
	DEBUGFS_READONLY_FILE_OPS(name)

J
Jiri Benc 已提交
61
#define DEBUGFS_ADD(name)						\
62
	debugfs_create_file(#name, 0400, phyd, local, &name## _ops);
J
Jiri Benc 已提交
63

64
#define DEBUGFS_ADD_MODE(name, mode)					\
65
	debugfs_create_file(#name, mode, phyd, local, &name## _ops);
J
Jiri Benc 已提交
66 67


B
Ben Greear 已提交
68 69 70 71
DEBUGFS_READONLY_FILE(user_power, "%d",
		      local->user_power_level);
DEBUGFS_READONLY_FILE(power, "%d",
		      local->hw.conf.power_level);
72
DEBUGFS_READONLY_FILE(frequency, "%d",
73
		      local->hw.conf.channel->center_freq);
74
DEBUGFS_READONLY_FILE(total_ps_buffered, "%d",
J
Jiri Benc 已提交
75
		      local->total_ps_buffered);
76
DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
J
Jiri Benc 已提交
77
		      local->wep_iv & 0xffffff);
78
DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
79
	local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
80 81 82 83 84

static ssize_t tsf_read(struct file *file, char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
85
	u64 tsf;
86

87
	tsf = drv_get_tsf(local);
88

89 90
	return mac80211_format_buffer(user_buf, count, ppos, "0x%016llx\n",
				      (unsigned long long) tsf);
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
}

static ssize_t tsf_write(struct file *file,
                         const char __user *user_buf,
                         size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
	unsigned long long tsf;
	char buf[100];
	size_t len;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EFAULT;
	buf[len] = '\0';

	if (strncmp(buf, "reset", 5) == 0) {
		if (local->ops->reset_tsf) {
109
			drv_reset_tsf(local);
J
Joe Perches 已提交
110
			wiphy_info(local->hw.wiphy, "debugfs reset TSF\n");
111 112 113 114
		}
	} else {
		tsf = simple_strtoul(buf, NULL, 0);
		if (local->ops->set_tsf) {
115
			drv_set_tsf(local, tsf);
J
Joe Perches 已提交
116 117 118
			wiphy_info(local->hw.wiphy,
				   "debugfs set TSF to %#018llx\n", tsf);

119 120 121 122 123 124 125 126 127
		}
	}

	return count;
}

static const struct file_operations tsf_ops = {
	.read = tsf_read,
	.write = tsf_write,
128 129
	.open = mac80211_open_file_generic,
	.llseek = default_llseek,
130
};
J
Jiri Benc 已提交
131

132 133 134 135 136 137
static ssize_t reset_write(struct file *file, const char __user *user_buf,
			   size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;

	rtnl_lock();
138
	__ieee80211_suspend(&local->hw, NULL);
139 140 141 142 143 144 145 146 147
	__ieee80211_resume(&local->hw);
	rtnl_unlock();

	return count;
}

static const struct file_operations reset_ops = {
	.write = reset_write,
	.open = mac80211_open_file_generic,
148
	.llseek = noop_llseek,
149 150
};

151 152 153 154 155
static ssize_t noack_read(struct file *file, char __user *user_buf,
			  size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;

156 157
	return mac80211_format_buffer(user_buf, count, ppos, "%d\n",
				      local->wifi_wme_noack_test);
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
}

static ssize_t noack_write(struct file *file,
			   const char __user *user_buf,
			   size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
	char buf[10];
	size_t len;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EFAULT;
	buf[len] = '\0';

	local->wifi_wme_noack_test = !!simple_strtoul(buf, NULL, 0);

	return count;
}

static const struct file_operations noack_ops = {
	.read = noack_read,
	.write = noack_write,
181 182
	.open = mac80211_open_file_generic,
	.llseek = default_llseek,
183 184
};

185 186 187 188
static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf,
				 size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
189 190
	return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n",
				      local->uapsd_queues);
191 192 193 194 195 196 197
}

static ssize_t uapsd_queues_write(struct file *file,
				  const char __user *user_buf,
				  size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
198
	u8 val;
199 200
	int ret;

201
	ret = kstrtou8_from_user(user_buf, count, 0, &val);
202
	if (ret)
203
		return ret;
204 205 206 207 208 209 210 211 212 213 214 215

	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
		return -ERANGE;

	local->uapsd_queues = val;

	return count;
}

static const struct file_operations uapsd_queues_ops = {
	.read = uapsd_queues_read,
	.write = uapsd_queues_write,
216 217
	.open = mac80211_open_file_generic,
	.llseek = default_llseek,
218 219 220 221 222 223 224
};

static ssize_t uapsd_max_sp_len_read(struct file *file, char __user *user_buf,
				     size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;

225 226
	return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n",
				      local->uapsd_max_sp_len);
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
}

static ssize_t uapsd_max_sp_len_write(struct file *file,
				      const char __user *user_buf,
				      size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
	unsigned long val;
	char buf[10];
	size_t len;
	int ret;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EFAULT;
	buf[len] = '\0';

	ret = strict_strtoul(buf, 0, &val);

	if (ret)
		return -EINVAL;

	if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
		return -ERANGE;

	local->uapsd_max_sp_len = val;

	return count;
}

static const struct file_operations uapsd_max_sp_len_ops = {
	.read = uapsd_max_sp_len_read,
	.write = uapsd_max_sp_len_write,
260 261
	.open = mac80211_open_file_generic,
	.llseek = default_llseek,
262 263
};

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
static ssize_t channel_type_read(struct file *file, char __user *user_buf,
		       size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
	const char *buf;

	switch (local->hw.conf.channel_type) {
	case NL80211_CHAN_NO_HT:
		buf = "no ht\n";
		break;
	case NL80211_CHAN_HT20:
		buf = "ht20\n";
		break;
	case NL80211_CHAN_HT40MINUS:
		buf = "ht40-\n";
		break;
	case NL80211_CHAN_HT40PLUS:
		buf = "ht40+\n";
		break;
	default:
		buf = "???";
		break;
	}

	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}

B
Ben Greear 已提交
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
static ssize_t hwflags_read(struct file *file, char __user *user_buf,
			    size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
	int mxln = 500;
	ssize_t rv;
	char *buf = kzalloc(mxln, GFP_KERNEL);
	int sf = 0; /* how many written so far */

	sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
	if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
		sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
	if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
		sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
	if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
		sf += snprintf(buf + sf, mxln - sf,
			       "HOST_BCAST_PS_BUFFERING\n");
	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
		sf += snprintf(buf + sf, mxln - sf,
			       "2GHZ_SHORT_SLOT_INCAPABLE\n");
	if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
		sf += snprintf(buf + sf, mxln - sf,
			       "2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
	if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
		sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
		sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
	if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
		sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n");
	if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
		sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
	if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
		sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
		sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
	if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
		sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
	if (local->hw.flags & IEEE80211_HW_BEACON_FILTER)
		sf += snprintf(buf + sf, mxln - sf, "BEACON_FILTER\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
	if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
		sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
	if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
		sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_CQM_RSSI\n");
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
		sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
	if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
		sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");

	rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
	kfree(buf);
	return rv;
}
355

J
Johannes Berg 已提交
356 357 358 359 360 361 362 363 364 365 366 367
static ssize_t queues_read(struct file *file, char __user *user_buf,
			   size_t count, loff_t *ppos)
{
	struct ieee80211_local *local = file->private_data;
	unsigned long flags;
	char buf[IEEE80211_MAX_QUEUES * 20];
	int q, res = 0;

	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
	for (q = 0; q < local->hw.queues; q++)
		res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
				local->queue_stop_reasons[q],
J
Johannes Berg 已提交
368
				skb_queue_len(&local->pending[q]));
J
Johannes Berg 已提交
369 370 371 372 373
	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);

	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
}

B
Ben Greear 已提交
374 375 376
DEBUGFS_READONLY_FILE_OPS(hwflags);
DEBUGFS_READONLY_FILE_OPS(channel_type);
DEBUGFS_READONLY_FILE_OPS(queues);
J
Johannes Berg 已提交
377

J
Jiri Benc 已提交
378 379 380 381 382 383 384 385 386 387 388 389
/* statistics stuff */

static ssize_t format_devstat_counter(struct ieee80211_local *local,
	char __user *userbuf,
	size_t count, loff_t *ppos,
	int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf,
			  int buflen))
{
	struct ieee80211_low_level_stats stats;
	char buf[20];
	int res;

390
	rtnl_lock();
391
	res = drv_get_stats(local, &stats);
J
Jiri Benc 已提交
392
	rtnl_unlock();
393 394 395
	if (res)
		return res;
	res = printvalue(&stats, buf, sizeof(buf));
J
Jiri Benc 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}

#define DEBUGFS_DEVSTATS_FILE(name)					\
static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\
				 char *buf, int buflen)			\
{									\
	return scnprintf(buf, buflen, "%u\n", stats->name);		\
}									\
static ssize_t stats_ ##name## _read(struct file *file,			\
				     char __user *userbuf,		\
				     size_t count, loff_t *ppos)	\
{									\
	return format_devstat_counter(file->private_data,		\
				      userbuf,				\
				      count,				\
				      ppos,				\
				      print_devstats_##name);		\
}									\
									\
static const struct file_operations stats_ ##name## _ops = {		\
	.read = stats_ ##name## _read,					\
	.open = mac80211_open_file_generic,				\
419
	.llseek = generic_file_llseek,					\
J
Jiri Benc 已提交
420 421
};

422 423 424
#define DEBUGFS_STATS_ADD(name, field)					\
	debugfs_create_u32(#name, 0400, statsd, (u32 *) &field);
#define DEBUGFS_DEVSTATS_ADD(name)					\
425
	debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
J
Jiri Benc 已提交
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount);
DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount);
DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount);
DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount);

void debugfs_hw_add(struct ieee80211_local *local)
{
	struct dentry *phyd = local->hw.wiphy->debugfsdir;
	struct dentry *statsd;

	if (!phyd)
		return;

	local->debugfs.keys = debugfs_create_dir("keys", phyd);

	DEBUGFS_ADD(frequency);
	DEBUGFS_ADD(total_ps_buffered);
	DEBUGFS_ADD(wep_iv);
445
	DEBUGFS_ADD(tsf);
J
Johannes Berg 已提交
446
	DEBUGFS_ADD(queues);
447
	DEBUGFS_ADD_MODE(reset, 0200);
448
	DEBUGFS_ADD(noack);
449 450
	DEBUGFS_ADD(uapsd_queues);
	DEBUGFS_ADD(uapsd_max_sp_len);
451
	DEBUGFS_ADD(channel_type);
B
Ben Greear 已提交
452
	DEBUGFS_ADD(hwflags);
B
Ben Greear 已提交
453 454
	DEBUGFS_ADD(user_power);
	DEBUGFS_ADD(power);
J
Jiri Benc 已提交
455 456 457 458 459 460 461

	statsd = debugfs_create_dir("statistics", phyd);

	/* if the dir failed, don't put all the other things into the root! */
	if (!statsd)
		return;

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
	DEBUGFS_STATS_ADD(transmitted_fragment_count,
		local->dot11TransmittedFragmentCount);
	DEBUGFS_STATS_ADD(multicast_transmitted_frame_count,
		local->dot11MulticastTransmittedFrameCount);
	DEBUGFS_STATS_ADD(failed_count, local->dot11FailedCount);
	DEBUGFS_STATS_ADD(retry_count, local->dot11RetryCount);
	DEBUGFS_STATS_ADD(multiple_retry_count,
		local->dot11MultipleRetryCount);
	DEBUGFS_STATS_ADD(frame_duplicate_count,
		local->dot11FrameDuplicateCount);
	DEBUGFS_STATS_ADD(received_fragment_count,
		local->dot11ReceivedFragmentCount);
	DEBUGFS_STATS_ADD(multicast_received_frame_count,
		local->dot11MulticastReceivedFrameCount);
	DEBUGFS_STATS_ADD(transmitted_frame_count,
		local->dot11TransmittedFrameCount);
J
Jiri Benc 已提交
478
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
	DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop);
	DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued);
	DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted,
		local->tx_handlers_drop_unencrypted);
	DEBUGFS_STATS_ADD(tx_handlers_drop_fragment,
		local->tx_handlers_drop_fragment);
	DEBUGFS_STATS_ADD(tx_handlers_drop_wep,
		local->tx_handlers_drop_wep);
	DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc,
		local->tx_handlers_drop_not_assoc);
	DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port,
		local->tx_handlers_drop_unauth_port);
	DEBUGFS_STATS_ADD(rx_handlers_drop, local->rx_handlers_drop);
	DEBUGFS_STATS_ADD(rx_handlers_queued, local->rx_handlers_queued);
	DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc,
		local->rx_handlers_drop_nullfunc);
	DEBUGFS_STATS_ADD(rx_handlers_drop_defrag,
		local->rx_handlers_drop_defrag);
	DEBUGFS_STATS_ADD(rx_handlers_drop_short,
		local->rx_handlers_drop_short);
	DEBUGFS_STATS_ADD(rx_handlers_drop_passive_scan,
		local->rx_handlers_drop_passive_scan);
	DEBUGFS_STATS_ADD(tx_expand_skb_head,
		local->tx_expand_skb_head);
	DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned,
		local->tx_expand_skb_head_cloned);
	DEBUGFS_STATS_ADD(rx_expand_skb_head,
		local->rx_expand_skb_head);
	DEBUGFS_STATS_ADD(rx_expand_skb_head2,
		local->rx_expand_skb_head2);
	DEBUGFS_STATS_ADD(rx_handlers_fragments,
		local->rx_handlers_fragments);
	DEBUGFS_STATS_ADD(tx_status_drop,
		local->tx_status_drop);
J
Jiri Benc 已提交
513
#endif
514 515 516 517
	DEBUGFS_DEVSTATS_ADD(dot11ACKFailureCount);
	DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
	DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
	DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
J
Jiri Benc 已提交
518
}