summaryrefslogtreecommitdiff
path: root/net/wireless/wext-proc.c
blob: 8bafa31fa9f83d25909770b6b0202d6e2a282a37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * This file implement the Wireless Extensions proc API.
 *
 * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
 *
 * (As all part of the Linux kernel, this file is GPL)
 */

/*
 * The /proc/net/wireless file is a human readable user-space interface
 * exporting various wireless specific statistics from the wireless devices.
 * This is the most popular part of the Wireless Extensions ;-)
 *
 * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
 * The content of the file is basically the content of "struct iw_statistics".
 */

#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/wireless.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <net/iw_handler.h>
#include <net/wext.h>


static void wireless_seq_printf_stats(struct seq_file *seq,
				      struct net_device *dev)
{
	/* Get stats from the driver */
	struct iw_statistics *stats = get_wireless_stats(dev);
	static struct iw_statistics nullstats = {};

	/* show device if it's wireless regardless of current stats */
	if (!stats) {
#ifdef CONFIG_WIRELESS_EXT
		if (dev->wireless_handlers)
			stats = &nullstats;
#endif
#ifdef CONFIG_CFG80211
		if (dev->ieee80211_ptr)
			stats = &nullstats;
#endif
	}

	if (stats) {
		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
				"%6d %6d   %6d\n",
			   dev->name, stats->status, stats->qual.qual,
			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
			   ? '.' : ' ',
			   ((__s32) stats->qual.level) -
			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
			   ? '.' : ' ',
			   ((__s32) stats->qual.noise) -
			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
			   ? '.' : ' ',
			   stats->discard.nwid, stats->discard.code,
			   stats->discard.fragment, stats->discard.retries,
			   stats->discard.misc, stats->miss.beacon);

		if (stats != &nullstats)
			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
	}
}

/* ---------------------------------------------------------------- */
/*
 * Print info for /proc/net/wireless (print all entries)
 */
static int wireless_dev_seq_show(struct seq_file *seq, void *v)
{
	might_sleep();

	if (v == SEQ_START_TOKEN)
		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
				"packets               | Missed | WE\n"
				" face | tus | link level noise |  nwid  "
				"crypt   frag  retry   misc | beacon | %d\n",
			   WIRELESS_EXT);
	else
		wireless_seq_printf_stats(seq, v);
	return 0;
}

static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
{
	struct net *net = seq_file_net(seq);
	loff_t off;
	struct net_device *dev;

	rtnl_lock();
	if (!*pos)
		return SEQ_START_TOKEN;

	off = 1;
	for_each_netdev(net, dev)
		if (off++ == *pos)
			return dev;
	return NULL;
}

static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	struct net *net = seq_file_net(seq);

	++*pos;

	return v == SEQ_START_TOKEN ?
		first_net_device(net) : next_net_device(v);
}

static void wireless_dev_seq_stop(struct seq_file *seq, void *v)
{
	rtnl_unlock();
}

static const struct seq_operations wireless_seq_ops = {
	.start = wireless_dev_seq_start,
	.next  = wireless_dev_seq_next,
	.stop  = wireless_dev_seq_stop,
	.show  = wireless_dev_seq_show,
};

static int seq_open_wireless(struct inode *inode, struct file *file)
{
	return seq_open_net(inode, file, &wireless_seq_ops,
			    sizeof(struct seq_net_private));
}

static const struct file_operations wireless_seq_fops = {
	.owner	 = THIS_MODULE,
	.open    = seq_open_wireless,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = seq_release_net,
};

int __net_init wext_proc_init(struct net *net)
{
	/* Create /proc/net/wireless entry */
	if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops))
		return -ENOMEM;

	return 0;
}

void __net_exit wext_proc_exit(struct net *net)
{
	proc_net_remove(net, "wireless");
}