162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file implement the Wireless Extensions proc API.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
562306a36Sopenharmony_ci * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * (As all part of the Linux kernel, this file is GPL)
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * The /proc/net/wireless file is a human readable user-space interface
1262306a36Sopenharmony_ci * exporting various wireless specific statistics from the wireless devices.
1362306a36Sopenharmony_ci * This is the most popular part of the Wireless Extensions ;-)
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
1662306a36Sopenharmony_ci * The content of the file is basically the content of "struct iw_statistics".
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/proc_fs.h>
2162306a36Sopenharmony_ci#include <linux/seq_file.h>
2262306a36Sopenharmony_ci#include <linux/wireless.h>
2362306a36Sopenharmony_ci#include <linux/netdevice.h>
2462306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2562306a36Sopenharmony_ci#include <net/iw_handler.h>
2662306a36Sopenharmony_ci#include <net/wext.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic void wireless_seq_printf_stats(struct seq_file *seq,
3062306a36Sopenharmony_ci				      struct net_device *dev)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	/* Get stats from the driver */
3362306a36Sopenharmony_ci	struct iw_statistics *stats = get_wireless_stats(dev);
3462306a36Sopenharmony_ci	static struct iw_statistics nullstats = {};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* show device if it's wireless regardless of current stats */
3762306a36Sopenharmony_ci	if (!stats) {
3862306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT
3962306a36Sopenharmony_ci		if (dev->wireless_handlers)
4062306a36Sopenharmony_ci			stats = &nullstats;
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci#ifdef CONFIG_CFG80211
4362306a36Sopenharmony_ci		if (dev->ieee80211_ptr)
4462306a36Sopenharmony_ci			stats = &nullstats;
4562306a36Sopenharmony_ci#endif
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (stats) {
4962306a36Sopenharmony_ci		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
5062306a36Sopenharmony_ci				"%6d %6d   %6d\n",
5162306a36Sopenharmony_ci			   dev->name, stats->status, stats->qual.qual,
5262306a36Sopenharmony_ci			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
5362306a36Sopenharmony_ci			   ? '.' : ' ',
5462306a36Sopenharmony_ci			   ((__s32) stats->qual.level) -
5562306a36Sopenharmony_ci			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
5662306a36Sopenharmony_ci			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
5762306a36Sopenharmony_ci			   ? '.' : ' ',
5862306a36Sopenharmony_ci			   ((__s32) stats->qual.noise) -
5962306a36Sopenharmony_ci			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
6062306a36Sopenharmony_ci			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
6162306a36Sopenharmony_ci			   ? '.' : ' ',
6262306a36Sopenharmony_ci			   stats->discard.nwid, stats->discard.code,
6362306a36Sopenharmony_ci			   stats->discard.fragment, stats->discard.retries,
6462306a36Sopenharmony_ci			   stats->discard.misc, stats->miss.beacon);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		if (stats != &nullstats)
6762306a36Sopenharmony_ci			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* ---------------------------------------------------------------- */
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * Print info for /proc/net/wireless (print all entries)
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cistatic int wireless_dev_seq_show(struct seq_file *seq, void *v)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	might_sleep();
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN)
8062306a36Sopenharmony_ci		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
8162306a36Sopenharmony_ci				"packets               | Missed | WE\n"
8262306a36Sopenharmony_ci				" face | tus | link level noise |  nwid  "
8362306a36Sopenharmony_ci				"crypt   frag  retry   misc | beacon | %d\n",
8462306a36Sopenharmony_ci			   WIRELESS_EXT);
8562306a36Sopenharmony_ci	else
8662306a36Sopenharmony_ci		wireless_seq_printf_stats(seq, v);
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
9362306a36Sopenharmony_ci	loff_t off;
9462306a36Sopenharmony_ci	struct net_device *dev;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	rtnl_lock();
9762306a36Sopenharmony_ci	if (!*pos)
9862306a36Sopenharmony_ci		return SEQ_START_TOKEN;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	off = 1;
10162306a36Sopenharmony_ci	for_each_netdev(net, dev)
10262306a36Sopenharmony_ci		if (off++ == *pos)
10362306a36Sopenharmony_ci			return dev;
10462306a36Sopenharmony_ci	return NULL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	++*pos;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return v == SEQ_START_TOKEN ?
11462306a36Sopenharmony_ci		first_net_device(net) : next_net_device(v);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void wireless_dev_seq_stop(struct seq_file *seq, void *v)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	rtnl_unlock();
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic const struct seq_operations wireless_seq_ops = {
12362306a36Sopenharmony_ci	.start = wireless_dev_seq_start,
12462306a36Sopenharmony_ci	.next  = wireless_dev_seq_next,
12562306a36Sopenharmony_ci	.stop  = wireless_dev_seq_stop,
12662306a36Sopenharmony_ci	.show  = wireless_dev_seq_show,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciint __net_init wext_proc_init(struct net *net)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	/* Create /proc/net/wireless entry */
13262306a36Sopenharmony_ci	if (!proc_create_net("wireless", 0444, net->proc_net,
13362306a36Sopenharmony_ci			&wireless_seq_ops, sizeof(struct seq_net_private)))
13462306a36Sopenharmony_ci		return -ENOMEM;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_civoid __net_exit wext_proc_exit(struct net *net)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	remove_proc_entry("wireless", net->proc_net);
14262306a36Sopenharmony_ci}
143