Commit e9f207f0 authored by Jiri Benc's avatar Jiri Benc Committed by David S. Miller

[MAC80211]: Add debugfs attributes.

Export various mac80211 internal variables through debugfs.
Signed-off-by: default avatarJiri Benc <jbenc@suse.cz>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f0706e82
...@@ -20,6 +20,15 @@ config MAC80211_LEDS ...@@ -20,6 +20,15 @@ config MAC80211_LEDS
This option enables a few LED triggers for different This option enables a few LED triggers for different
packet receive/transmit events. packet receive/transmit events.
config MAC80211_DEBUGFS
bool "Export mac80211 internals in DebugFS"
depends on MAC80211 && DEBUG_FS
---help---
Select this to see extensive information about
the internal state of mac80211 in debugfs.
Say N unless you know you need this.
config MAC80211_DEBUG config MAC80211_DEBUG
bool "Enable debugging output" bool "Enable debugging output"
depends on MAC80211 depends on MAC80211
......
obj-$(CONFIG_MAC80211) += mac80211.o rc80211_simple.o obj-$(CONFIG_MAC80211) += mac80211.o rc80211_simple.o
mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o
mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += debugfs.o debugfs_sta.o debugfs_netdev.o debugfs_key.o
mac80211-objs := \ mac80211-objs := \
ieee80211.o \ ieee80211.o \
......
This diff is collapsed.
#ifndef __MAC80211_DEBUGFS_H
#define __MAC80211_DEBUGFS_H
#ifdef CONFIG_MAC80211_DEBUGFS
extern void debugfs_hw_add(struct ieee80211_local *local);
extern void debugfs_hw_del(struct ieee80211_local *local);
extern int mac80211_open_file_generic(struct inode *inode, struct file *file);
#else
static inline void debugfs_hw_add(struct ieee80211_local *local)
{
return;
}
static inline void debugfs_hw_del(struct ieee80211_local *local) {}
#endif
#endif /* __MAC80211_DEBUGFS_H */
/*
* Copyright 2003-2005 Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kobject.h>
#include "ieee80211_i.h"
#include "ieee80211_key.h"
#include "debugfs.h"
#include "debugfs_key.h"
#define KEY_READ(name, buflen, format_string) \
static ssize_t key_##name##_read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
char buf[buflen]; \
struct ieee80211_key *key = file->private_data; \
int res = scnprintf(buf, buflen, format_string, key->name); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
}
#define KEY_READ_D(name) KEY_READ(name, 20, "%d\n")
#define KEY_OPS(name) \
static const struct file_operations key_ ##name## _ops = { \
.read = key_##name##_read, \
.open = mac80211_open_file_generic, \
}
#define KEY_FILE(name, format) \
KEY_READ_##format(name) \
KEY_OPS(name)
KEY_FILE(keylen, D);
KEY_FILE(force_sw_encrypt, D);
KEY_FILE(keyidx, D);
KEY_FILE(hw_key_idx, D);
KEY_FILE(tx_rx_count, D);
static ssize_t key_algorithm_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
char *alg;
struct ieee80211_key *key = file->private_data;
switch (key->alg) {
case ALG_WEP:
alg = "WEP\n";
break;
case ALG_TKIP:
alg = "TKIP\n";
break;
case ALG_CCMP:
alg = "CCMP\n";
break;
default:
return 0;
}
return simple_read_from_buffer(userbuf, count, ppos, alg, strlen(alg));
}
KEY_OPS(algorithm);
static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
const u8 *tpn;
char buf[20];
int len;
struct ieee80211_key *key = file->private_data;
switch (key->alg) {
case ALG_WEP:
len = scnprintf(buf, sizeof(buf), "\n");
case ALG_TKIP:
len = scnprintf(buf, sizeof(buf), "%08x %04x\n",
key->u.tkip.iv32,
key->u.tkip.iv16);
case ALG_CCMP:
tpn = key->u.ccmp.tx_pn;
len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]);
default:
return 0;
}
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
KEY_OPS(tx_spec);
static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf;
int i, len;
const u8 *rpn;
switch (key->alg) {
case ALG_WEP:
len = scnprintf(buf, sizeof(buf), "\n");
case ALG_TKIP:
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
p += scnprintf(p, sizeof(buf)+buf-p,
"%08x %04x\n",
key->u.tkip.iv32_rx[i],
key->u.tkip.iv16_rx[i]);
len = p - buf;
case ALG_CCMP:
for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
rpn = key->u.ccmp.rx_pn[i];
p += scnprintf(p, sizeof(buf)+buf-p,
"%02x%02x%02x%02x%02x%02x\n",
rpn[0], rpn[1], rpn[2],
rpn[3], rpn[4], rpn[5]);
}
len = p - buf;
default:
return 0;
}
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
KEY_OPS(rx_spec);
static ssize_t key_replays_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
char buf[20];
int len;
if (key->alg != ALG_CCMP)
return 0;
len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays);
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
KEY_OPS(replays);
static ssize_t key_key_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
int i, res, bufsize = 2*key->keylen+2;
char *buf = kmalloc(bufsize, GFP_KERNEL);
char *p = buf;
for (i = 0; i < key->keylen; i++)
p += scnprintf(p, bufsize+buf-p, "%02x", key->key[i]);
p += scnprintf(p, bufsize+buf-p, "\n");
res = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
kfree(buf);
return res;
}
KEY_OPS(key);
#define DEBUGFS_ADD(name) \
key->debugfs.name = debugfs_create_file(#name, 0400,\
key->debugfs.dir, key, &key_##name##_ops);
void ieee80211_debugfs_key_add(struct ieee80211_local *local,
struct ieee80211_key *key)
{
char buf[20];
if (!local->debugfs.keys)
return;
sprintf(buf, "%d", key->keyidx);
key->debugfs.dir = debugfs_create_dir(buf,
local->debugfs.keys);
if (!key->debugfs.dir)
return;
DEBUGFS_ADD(keylen);
DEBUGFS_ADD(force_sw_encrypt);
DEBUGFS_ADD(keyidx);
DEBUGFS_ADD(hw_key_idx);
DEBUGFS_ADD(tx_rx_count);
DEBUGFS_ADD(algorithm);
DEBUGFS_ADD(tx_spec);
DEBUGFS_ADD(rx_spec);
DEBUGFS_ADD(replays);
DEBUGFS_ADD(key);
};
#define DEBUGFS_DEL(name) \
debugfs_remove(key->debugfs.name); key->debugfs.name = NULL;
void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
{
if (!key)
return;
DEBUGFS_DEL(keylen);
DEBUGFS_DEL(force_sw_encrypt);
DEBUGFS_DEL(keyidx);
DEBUGFS_DEL(hw_key_idx);
DEBUGFS_DEL(tx_rx_count);
DEBUGFS_DEL(algorithm);
DEBUGFS_DEL(tx_spec);
DEBUGFS_DEL(rx_spec);
DEBUGFS_DEL(replays);
DEBUGFS_DEL(key);
debugfs_remove(key->debugfs.stalink);
key->debugfs.stalink = NULL;
debugfs_remove(key->debugfs.dir);
key->debugfs.dir = NULL;
}
void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
{
char buf[50];
if (!sdata->debugfsdir)
return;
sprintf(buf, "../keys/%d", sdata->default_key->keyidx);
sdata->debugfs.default_key =
debugfs_create_symlink("default_key", sdata->debugfsdir, buf);
}
void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata)
{
if (!sdata)
return;
debugfs_remove(sdata->debugfs.default_key);
sdata->debugfs.default_key = NULL;
}
void ieee80211_debugfs_key_sta_link(struct ieee80211_key *key,
struct sta_info *sta)
{
char buf[50];
if (!key->debugfs.dir)
return;
sprintf(buf, "../sta/" MAC_FMT, MAC_ARG(sta->addr));
key->debugfs.stalink =
debugfs_create_symlink("station", key->debugfs.dir, buf);
}
void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key,
struct sta_info *sta)
{
debugfs_remove(key->debugfs.stalink);
key->debugfs.stalink = NULL;
}
#ifndef __MAC80211_DEBUGFS_KEY_H
#define __MAC80211_DEBUGFS_KEY_H
#ifdef CONFIG_MAC80211_DEBUGFS
void ieee80211_debugfs_key_add(struct ieee80211_local *local,
struct ieee80211_key *key);
void ieee80211_debugfs_key_remove(struct ieee80211_key *key);
void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata);
void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata);
void ieee80211_debugfs_key_sta_link(struct ieee80211_key *key,
struct sta_info *sta);
void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key,
struct sta_info *sta);
#else
static inline void ieee80211_debugfs_key_add(struct ieee80211_local *local,
struct ieee80211_key *key)
{}
static inline void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
{}
static inline void ieee80211_debugfs_key_add_default(
struct ieee80211_sub_if_data *sdata)
{}
static inline void ieee80211_debugfs_key_remove_default(
struct ieee80211_sub_if_data *sdata)
{}
static inline void ieee80211_debugfs_key_sta_link(
struct ieee80211_key *key, struct sta_info *sta)
{}
static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key,
struct sta_info *sta)
{}
#endif
#endif /* __MAC80211_DEBUGFS_KEY_H */
This diff is collapsed.
/* routines exported for debugfs handling */
#ifndef __IEEE80211_DEBUGFS_NETDEV_H
#define __IEEE80211_DEBUGFS_NETDEV_H
#ifdef CONFIG_MAC80211_DEBUGFS
void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata);
void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata);
void ieee80211_debugfs_change_if_type(struct ieee80211_sub_if_data *sdata,
int oldtype);
void ieee80211_debugfs_netdev_init(void);
void ieee80211_debugfs_netdev_exit(void);
#else
static inline void ieee80211_debugfs_add_netdev(
struct ieee80211_sub_if_data *sdata)
{}
static inline void ieee80211_debugfs_remove_netdev(
struct ieee80211_sub_if_data *sdata)
{}
static inline void ieee80211_debugfs_change_if_type(
struct ieee80211_sub_if_data *sdata, int oldtype)
{}
static inline void ieee80211_debugfs_netdev_init(void)
{}
static inline void ieee80211_debugfs_netdev_exit(void)
{}
#endif
#endif /* __IEEE80211_DEBUGFS_NETDEV_H */
/*
* Copyright 2003-2005 Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/debugfs.h>
#include <linux/ieee80211.h>
#include "ieee80211_i.h"
#include "debugfs.h"
#include "debugfs_sta.h"
#include "sta_info.h"
/* sta attributtes */
#define STA_READ(name, buflen, field, format_string) \
static ssize_t sta_ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
int res; \
struct sta_info *sta = file->private_data; \
char buf[buflen]; \
res = scnprintf(buf, buflen, format_string, sta->field); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
}
#define STA_READ_D(name, field) STA_READ(name, 20, field, "%d\n")
#define STA_READ_U(name, field) STA_READ(name, 20, field, "%u\n")
#define STA_READ_LU(name, field) STA_READ(name, 20, field, "%lu\n")
#define STA_READ_S(name, field) STA_READ(name, 20, field, "%s\n")
#define STA_READ_RATE(name, field) \
static ssize_t sta_##name##_read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct sta_info *sta = file->private_data; \
struct ieee80211_local *local = wdev_priv(sta->dev->ieee80211_ptr);\
struct ieee80211_hw_mode *mode = local->oper_hw_mode; \
char buf[20]; \
int res = scnprintf(buf, sizeof(buf), "%d\n", \
(sta->field >= 0 && \
sta->field < mode->num_rates) ? \
mode->rates[sta->field].rate : -1); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
}
#define STA_OPS(name) \
static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \
.open = mac80211_open_file_generic, \
}
#define STA_FILE(name, field, format) \
STA_READ_##format(name, field) \
STA_OPS(name)
STA_FILE(aid, aid, D);
STA_FILE(key_idx_compression, key_idx_compression, D);
STA_FILE(dev, dev->name, S);
STA_FILE(vlan_id, vlan_id, D);
STA_FILE(rx_packets, rx_packets, LU);
STA_FILE(tx_packets, tx_packets, LU);
STA_FILE(rx_bytes, rx_bytes, LU);
STA_FILE(tx_bytes, tx_bytes, LU);
STA_FILE(rx_duplicates, num_duplicates, LU);
STA_FILE(rx_fragments, rx_fragments, LU);
STA_FILE(rx_dropped, rx_dropped, LU);
STA_FILE(tx_fragments, tx_fragments, LU);
STA_FILE(tx_filtered, tx_filtered_count, LU);
STA_FILE(txrate, txrate, RATE);
STA_FILE(last_txrate, last_txrate, RATE);
STA_FILE(tx_retry_failed, tx_retry_failed, LU);
STA_FILE(tx_retry_count, tx_retry_count, LU);
STA_FILE(last_rssi, last_rssi, D);
STA_FILE(last_signal, last_signal, D);
STA_FILE(last_noise, last_noise, D);
STA_FILE(channel_use, channel_use, D);
STA_FILE(wep_weak_iv_count, wep_weak_iv_count, D);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[100];
struct sta_info *sta = file->private_data;
int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s",
sta->flags & WLAN_STA_AUTH ? "AUTH\n" : "",
sta->flags & WLAN_STA_ASSOC ? "ASSOC\n" : "",
sta->flags & WLAN_STA_PS ? "PS\n" : "",
sta->flags & WLAN_STA_TIM ? "TIM\n" : "",
sta->flags & WLAN_STA_PERM ? "PERM\n" : "",
sta->flags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "",
sta->flags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "",
sta->flags & WLAN_STA_WME ? "WME\n" : "",
sta->flags & WLAN_STA_WDS ? "WDS\n" : "");
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
STA_OPS(flags);
static ssize_t sta_num_ps_buf_frames_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[20];
struct sta_info *sta = file->private_data;
int res = scnprintf(buf, sizeof(buf), "%u\n",
skb_queue_len(&sta->ps_tx_buf));
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
STA_OPS(num_ps_buf_frames);
static ssize_t sta_last_ack_rssi_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[100];
struct sta_info *sta = file->private_data;
int res = scnprintf(buf, sizeof(buf), "%d %d %d\n",
sta->last_ack_rssi[0],
sta->last_ack_rssi[1],
sta->last_ack_rssi[2]);
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
STA_OPS(last_ack_rssi);
static ssize_t sta_last_ack_ms_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[20];
struct sta_info *sta = file->private_data;
int res = scnprintf(buf, sizeof(buf), "%d\n",
sta->last_ack ?
jiffies_to_msecs(jiffies - sta->last_ack) : -1);
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
STA_OPS(last_ack_ms);
static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[20];
struct sta_info *sta = file->private_data;
int res = scnprintf(buf, sizeof(buf), "%d\n",
jiffies_to_msecs(jiffies - sta->last_rx));
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
STA_OPS(inactive_ms);
static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[15*NUM_RX_DATA_QUEUES], *p = buf;
int i;
struct sta_info *sta = file->private_data;
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%x ",
sta->last_seq_ctrl[i]);
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
STA_OPS(last_seq_ctrl);
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
static ssize_t sta_wme_rx_queue_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[15*NUM_RX_DATA_QUEUES], *p = buf;
int i;
struct sta_info *sta = file->private_data;
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%u ",
sta->wme_rx_queue[i]);
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
STA_OPS(wme_rx_queue);
static ssize_t sta_wme_tx_queue_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[15*NUM_TX_DATA_QUEUES], *p = buf;
int i;
struct sta_info *sta = file->private_data;
for (i = 0; i < NUM_TX_DATA_QUEUES; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%u ",
sta->wme_tx_queue[i]);
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
STA_OPS(wme_tx_queue);
#endif
#define DEBUGFS_ADD(name) \
sta->debugfs.name = debugfs_create_file(#name, 0444, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
#define DEBUGFS_DEL(name) \
debugfs_remove(sta->debugfs.name);\
sta->debugfs.name = NULL;
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
char buf[3*6];
struct dentry *stations_dir = sta->local->debugfs.stations;
if (!stations_dir)
return;
sprintf(buf, MAC_FMT, MAC_ARG(sta->addr));
sta->debugfs.dir = debugfs_create_dir(buf, stations_dir);
if (!sta->debugfs.dir)
return;
DEBUGFS_ADD(flags);
DEBUGFS_ADD(num_ps_buf_frames);
DEBUGFS_ADD(last_ack_rssi);
DEBUGFS_ADD(last_ack_ms);
DEBUGFS_ADD(inactive_ms);
DEBUGFS_ADD(last_seq_ctrl);
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
DEBUGFS_ADD(wme_rx_queue);
DEBUGFS_ADD(wme_tx_queue);
#endif
}
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
{
DEBUGFS_DEL(flags);
DEBUGFS_DEL(num_ps_buf_frames);
DEBUGFS_DEL(last_ack_rssi);
DEBUGFS_DEL(last_ack_ms);
DEBUGFS_DEL(inactive_ms);
DEBUGFS_DEL(last_seq_ctrl);
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
DEBUGFS_DEL(wme_rx_queue);
DEBUGFS_DEL(wme_tx_queue);
#endif
debugfs_remove(sta->debugfs.dir);
sta->debugfs.dir = NULL;
}
#ifndef __MAC80211_DEBUGFS_STA_H
#define __MAC80211_DEBUGFS_STA_H
#ifdef CONFIG_MAC80211_DEBUGFS
void ieee80211_sta_debugfs_add(struct sta_info *sta);
void ieee80211_sta_debugfs_remove(struct sta_info *sta);
#else
static inline void ieee80211_sta_debugfs_add(struct sta_info *sta) {}
static inline void ieee80211_sta_debugfs_remove(struct sta_info *sta) {}
#endif
#endif /* __MAC80211_DEBUGFS_STA_H */
...@@ -35,6 +35,9 @@ ...@@ -35,6 +35,9 @@
#include "aes_ccm.h" #include "aes_ccm.h"
#include "ieee80211_led.h" #include "ieee80211_led.h"
#include "ieee80211_cfg.h" #include "ieee80211_cfg.h"
#include "debugfs.h"
#include "debugfs_netdev.h"
#include "debugfs_key.h"
/* privid for wiphys to determine whether they belong to us or not */ /* privid for wiphys to determine whether they belong to us or not */
void *mac80211_wiphy_privid = &mac80211_wiphy_privid; void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
...@@ -108,6 +111,7 @@ static void ieee80211_key_release(struct kref *kref) ...@@ -108,6 +111,7 @@ static void ieee80211_key_release(struct kref *kref)
key = container_of(kref, struct ieee80211_key, kref); key = container_of(kref, struct ieee80211_key, kref);
if (key->alg == ALG_CCMP) if (key->alg == ALG_CCMP)
ieee80211_aes_key_free(key->u.ccmp.tfm); ieee80211_aes_key_free(key->u.ccmp.tfm);
ieee80211_debugfs_key_remove(key);
kfree(key); kfree(key);
} }
...@@ -4704,6 +4708,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -4704,6 +4708,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
goto fail_workqueue; goto fail_workqueue;
} }
debugfs_hw_add(local);
local->hw.conf.beacon_int = 1000; local->hw.conf.beacon_int = 1000;
local->wstats_flags |= local->hw.max_rssi ? local->wstats_flags |= local->hw.max_rssi ?
...@@ -4731,6 +4737,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -4731,6 +4737,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (result < 0) if (result < 0)
goto fail_dev; goto fail_dev;
ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
result = ieee80211_init_rate_ctrl_alg(local, NULL); result = ieee80211_init_rate_ctrl_alg(local, NULL);
if (result < 0) { if (result < 0) {
printk(KERN_DEBUG "%s: Failed to initialize rate control " printk(KERN_DEBUG "%s: Failed to initialize rate control "
...@@ -4765,11 +4773,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -4765,11 +4773,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
fail_wep: fail_wep:
rate_control_deinitialize(local); rate_control_deinitialize(local);
fail_rate: fail_rate:
ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
unregister_netdevice(local->mdev); unregister_netdevice(local->mdev);
fail_dev: fail_dev:
rtnl_unlock(); rtnl_unlock();
sta_info_stop(local); sta_info_stop(local);
fail_sta_info: fail_sta_info:
debugfs_hw_del(local);
destroy_workqueue(local->hw.workqueue); destroy_workqueue(local->hw.workqueue);
fail_workqueue: fail_workqueue:
wiphy_unregister(local->hw.wiphy); wiphy_unregister(local->hw.wiphy);
...@@ -4844,6 +4854,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) ...@@ -4844,6 +4854,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
ieee80211_clear_tx_pending(local); ieee80211_clear_tx_pending(local);
sta_info_stop(local); sta_info_stop(local);
rate_control_deinitialize(local); rate_control_deinitialize(local);
debugfs_hw_del(local);
for (i = 0; i < NUM_IEEE80211_MODES; i++) { for (i = 0; i < NUM_IEEE80211_MODES; i++) {
kfree(local->supp_rates[i]); kfree(local->supp_rates[i]);
...@@ -4953,6 +4964,8 @@ static int __init ieee80211_init(void) ...@@ -4953,6 +4964,8 @@ static int __init ieee80211_init(void)
return ret; return ret;
} }
ieee80211_debugfs_netdev_init();
return 0; return 0;
} }
...@@ -4960,6 +4973,7 @@ static int __init ieee80211_init(void) ...@@ -4960,6 +4973,7 @@ static int __init ieee80211_init(void)
static void __exit ieee80211_exit(void) static void __exit ieee80211_exit(void)
{ {
ieee80211_wme_unregister(); ieee80211_wme_unregister();
ieee80211_debugfs_netdev_exit();
} }
......
...@@ -307,6 +307,65 @@ struct ieee80211_sub_if_data { ...@@ -307,6 +307,65 @@ struct ieee80211_sub_if_data {
} u; } u;
int channel_use; int channel_use;
int channel_use_raw; int channel_use_raw;
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *debugfsdir;
union {
struct {
struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *eapol;
struct dentry *ieee8021_x;
struct dentry *state;
struct dentry *bssid;
struct dentry *prev_bssid;
struct dentry *ssid_len;
struct dentry *aid;
struct dentry *ap_capab;
struct dentry *capab;
struct dentry *extra_ie_len;
struct dentry *auth_tries;
struct dentry *assoc_tries;
struct dentry *auth_algs;
struct dentry *auth_alg;
struct dentry *auth_transaction;
struct dentry *flags;
} sta;
struct {
struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *eapol;
struct dentry *ieee8021_x;
struct dentry *num_sta_ps;
struct dentry *dtim_period;
struct dentry *dtim_count;
struct dentry *num_beacons;
struct dentry *force_unicast_rateidx;
struct dentry *max_ratectrl_rateidx;
struct dentry *num_buffered_multicast;
struct dentry *beacon_head_len;
struct dentry *beacon_tail_len;
} ap;
struct {
struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *eapol;
struct dentry *ieee8021_x;
struct dentry *peer;
} wds;
struct {
struct dentry *channel_use;
struct dentry *drop_unencrypted;
struct dentry *eapol;
struct dentry *ieee8021_x;
struct dentry *vlan_id;
} vlan;
struct {
struct dentry *mode;
} monitor;
struct dentry *default_key;
} debugfs;
#endif
}; };
#define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev) #define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev)
...@@ -444,6 +503,10 @@ struct ieee80211_local { ...@@ -444,6 +503,10 @@ struct ieee80211_local {
u32 stat_time; u32 stat_time;
struct timer_list stat_timer; struct timer_list stat_timer;
#ifdef CONFIG_MAC80211_DEBUGFS
struct work_struct sta_debugfs_add;
#endif
enum { enum {
STA_ANTENNA_SEL_AUTO = 0, STA_ANTENNA_SEL_AUTO = 0,
STA_ANTENNA_SEL_SW_CTRL = 1, STA_ANTENNA_SEL_SW_CTRL = 1,
...@@ -500,6 +563,70 @@ struct ieee80211_local { ...@@ -500,6 +563,70 @@ struct ieee80211_local {
* (1 << MODE_*) */ * (1 << MODE_*) */
int user_space_mlme; int user_space_mlme;
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *channel;
struct dentry *frequency;
struct dentry *radar_detect;
struct dentry *antenna_sel_tx;
struct dentry *antenna_sel_rx;
struct dentry *bridge_packets;
struct dentry *key_tx_rx_threshold;
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
struct dentry *long_retry_limit;
struct dentry *total_ps_buffered;
struct dentry *mode;
struct dentry *wep_iv;
struct dentry *tx_power_reduction;
struct dentry *modes;
struct dentry *statistics;
struct local_debugfsdentries_statsdentries {
struct dentry *transmitted_fragment_count;
struct dentry *multicast_transmitted_frame_count;
struct dentry *failed_count;
struct dentry *retry_count;
struct dentry *multiple_retry_count;
struct dentry *frame_duplicate_count;
struct dentry *received_fragment_count;
struct dentry *multicast_received_frame_count;
struct dentry *transmitted_frame_count;
struct dentry *wep_undecryptable_count;
struct dentry *num_scans;
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
struct dentry *tx_handlers_drop;
struct dentry *tx_handlers_queued;
struct dentry *tx_handlers_drop_unencrypted;
struct dentry *tx_handlers_drop_fragment;
struct dentry *tx_handlers_drop_wep;
struct dentry *tx_handlers_drop_not_assoc;
struct dentry *tx_handlers_drop_unauth_port;
struct dentry *rx_handlers_drop;
struct dentry *rx_handlers_queued;
struct dentry *rx_handlers_drop_nullfunc;
struct dentry *rx_handlers_drop_defrag;
struct dentry *rx_handlers_drop_short;
struct dentry *rx_handlers_drop_passive_scan;
struct dentry *tx_expand_skb_head;
struct dentry *tx_expand_skb_head_cloned;
struct dentry *rx_expand_skb_head;
struct dentry *rx_expand_skb_head2;
struct dentry *rx_handlers_fragments;
struct dentry *tx_status_drop;
struct dentry *wme_tx_queue;
struct dentry *wme_rx_queue;
#endif
struct dentry *dot11ACKFailureCount;
struct dentry *dot11RTSFailureCount;
struct dentry *dot11FCSErrorCount;
struct dentry *dot11RTSSuccessCount;
} stats;
struct dentry *stations;
struct dentry *keys;
} debugfs;
#endif
}; };
static inline struct ieee80211_local *hw_to_local( static inline struct ieee80211_local *hw_to_local(
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <net/mac80211.h> #include <net/mac80211.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "sta_info.h" #include "sta_info.h"
#include "debugfs_netdev.h"
void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata) void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
{ {
...@@ -73,6 +74,7 @@ int ieee80211_if_add(struct net_device *dev, const char *name, ...@@ -73,6 +74,7 @@ int ieee80211_if_add(struct net_device *dev, const char *name,
if (ret) if (ret)
goto fail; goto fail;
ieee80211_debugfs_add_netdev(sdata);
ieee80211_if_set_type(ndev, type); ieee80211_if_set_type(ndev, type);
write_lock_bh(&local->sub_if_lock); write_lock_bh(&local->sub_if_lock);
...@@ -126,6 +128,8 @@ int ieee80211_if_add_mgmt(struct ieee80211_local *local) ...@@ -126,6 +128,8 @@ int ieee80211_if_add_mgmt(struct ieee80211_local *local)
if (ret) if (ret)
goto fail; goto fail;
ieee80211_debugfs_add_netdev(nsdata);
if (local->open_count > 0) if (local->open_count > 0)
dev_open(ndev); dev_open(ndev);
local->apdev = ndev; local->apdev = ndev;
...@@ -142,6 +146,7 @@ void ieee80211_if_del_mgmt(struct ieee80211_local *local) ...@@ -142,6 +146,7 @@ void ieee80211_if_del_mgmt(struct ieee80211_local *local)
ASSERT_RTNL(); ASSERT_RTNL();
apdev = local->apdev; apdev = local->apdev;
ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(apdev));
local->apdev = NULL; local->apdev = NULL;
unregister_netdevice(apdev); unregister_netdevice(apdev);
} }
...@@ -150,6 +155,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type) ...@@ -150,6 +155,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
int oldtype = sdata->type;
sdata->type = type; sdata->type = type;
switch (type) { switch (type) {
...@@ -195,6 +201,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type) ...@@ -195,6 +201,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x", printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x",
dev->name, __FUNCTION__, type); dev->name, __FUNCTION__, type);
} }
ieee80211_debugfs_change_if_type(sdata, oldtype);
ieee80211_update_default_wep_only(local); ieee80211_update_default_wep_only(local);
} }
...@@ -303,6 +310,7 @@ void __ieee80211_if_del(struct ieee80211_local *local, ...@@ -303,6 +310,7 @@ void __ieee80211_if_del(struct ieee80211_local *local,
{ {
struct net_device *dev = sdata->dev; struct net_device *dev = sdata->dev;
ieee80211_debugfs_remove_netdev(sdata);
unregister_netdevice(dev); unregister_netdevice(dev);
/* Except master interface, the net_device will be freed by /* Except master interface, the net_device will be freed by
* net_device->destructor (i. e. ieee80211_if_free). */ * net_device->destructor (i. e. ieee80211_if_free). */
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "ieee80211_rate.h" #include "ieee80211_rate.h"
#include "wpa.h" #include "wpa.h"
#include "aes_ccm.h" #include "aes_ccm.h"
#include "debugfs_key.h"
static int ieee80211_regdom = 0x10; /* FCC */ static int ieee80211_regdom = 0x10; /* FCC */
module_param(ieee80211_regdom, int, 0444); module_param(ieee80211_regdom, int, 0444);
...@@ -180,8 +181,11 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, ...@@ -180,8 +181,11 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
} }
kfree(keyconf); kfree(keyconf);
if (set_tx_key || sdata->default_key == key) if (set_tx_key || sdata->default_key == key) {
ieee80211_debugfs_key_remove_default(sdata);
sdata->default_key = NULL; sdata->default_key = NULL;
}
ieee80211_debugfs_key_remove(key);
if (sta) if (sta)
sta->key = NULL; sta->key = NULL;
else else
...@@ -221,13 +225,19 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, ...@@ -221,13 +225,19 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
} }
} }
if (set_tx_key || sdata->default_key == old_key) if (set_tx_key || sdata->default_key == old_key) {
ieee80211_debugfs_key_remove_default(sdata);
sdata->default_key = NULL; sdata->default_key = NULL;
}
ieee80211_debugfs_key_remove(old_key);
if (sta) if (sta)
sta->key = key; sta->key = key;
else else
sdata->keys[idx] = key; sdata->keys[idx] = key;
ieee80211_key_free(old_key); ieee80211_key_free(old_key);
ieee80211_debugfs_key_add(local, key);
if (sta)
ieee80211_debugfs_key_sta_link(key, sta);
if (try_hwaccel && if (try_hwaccel &&
(alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP)) (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP))
...@@ -236,6 +246,8 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, ...@@ -236,6 +246,8 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
if (set_tx_key || (!sta && !sdata->default_key && key)) { if (set_tx_key || (!sta && !sdata->default_key && key)) {
sdata->default_key = key; sdata->default_key = key;
if (key)
ieee80211_debugfs_key_add_default(sdata);
if (local->ops->set_key_idx && if (local->ops->set_key_idx &&
local->ops->set_key_idx(local_to_hw(local), idx)) local->ops->set_key_idx(local_to_hw(local), idx))
...@@ -1505,8 +1517,12 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev, ...@@ -1505,8 +1517,12 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
alg = ALG_NONE; alg = ALG_NONE;
else if (erq->length == 0) { else if (erq->length == 0) {
/* No key data - just set the default TX key index */ /* No key data - just set the default TX key index */
if (sdata->default_key != sdata->keys[idx]) if (sdata->default_key != sdata->keys[idx]) {
ieee80211_debugfs_key_remove_default(sdata);
sdata->default_key = sdata->keys[idx]; sdata->default_key = sdata->keys[idx];
if (sdata->default_key)
ieee80211_debugfs_key_add_default(sdata);
}
return 0; return 0;
} }
......
...@@ -83,6 +83,23 @@ struct ieee80211_key { ...@@ -83,6 +83,23 @@ struct ieee80211_key {
* (used only for broadcast keys). */ * (used only for broadcast keys). */
s8 keyidx; /* WEP key index */ s8 keyidx; /* WEP key index */
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
struct dentry *stalink;
struct dentry *dir;
struct dentry *keylen;
struct dentry *force_sw_encrypt;
struct dentry *keyidx;
struct dentry *hw_key_idx;
struct dentry *tx_rx_count;
struct dentry *algorithm;
struct dentry *tx_spec;
struct dentry *rx_spec;
struct dentry *replays;
struct dentry *key;
} debugfs;
#endif
u8 key[0]; u8 key[0];
}; };
......
...@@ -56,6 +56,9 @@ struct rate_control_ops { ...@@ -56,6 +56,9 @@ struct rate_control_ops {
int (*add_attrs)(void *priv, struct kobject *kobj); int (*add_attrs)(void *priv, struct kobject *kobj);
void (*remove_attrs)(void *priv, struct kobject *kobj); void (*remove_attrs)(void *priv, struct kobject *kobj);
void (*add_sta_debugfs)(void *priv, void *priv_sta,
struct dentry *dir);
void (*remove_sta_debugfs)(void *priv, void *priv_sta);
}; };
struct rate_control_ref { struct rate_control_ref {
...@@ -119,4 +122,23 @@ static inline void rate_control_free_sta(struct rate_control_ref *ref, ...@@ -119,4 +122,23 @@ static inline void rate_control_free_sta(struct rate_control_ref *ref,
ref->ops->free_sta(ref->priv, priv); ref->ops->free_sta(ref->priv, priv);
} }
static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_DEBUGFS
struct rate_control_ref *ref = sta->rate_ctrl;
if (sta->debugfs.dir && ref->ops->add_sta_debugfs)
ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv,
sta->debugfs.dir);
#endif
}
static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_DEBUGFS
struct rate_control_ref *ref = sta->rate_ctrl;
if (ref->ops->remove_sta_debugfs)
ref->ops->remove_sta_debugfs(ref->priv, sta->rate_ctrl_priv);
#endif
}
#endif /* IEEE80211_RATE_H */ #endif /* IEEE80211_RATE_H */
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <net/mac80211.h> #include <net/mac80211.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "ieee80211_rate.h" #include "ieee80211_rate.h"
#include "debugfs.h"
/* This is a minimal implementation of TX rate controlling that can be used /* This is a minimal implementation of TX rate controlling that can be used
...@@ -121,6 +122,11 @@ struct sta_rate_control { ...@@ -121,6 +122,11 @@ struct sta_rate_control {
unsigned long avg_rate_update; unsigned long avg_rate_update;
u32 tx_avg_rate_sum; u32 tx_avg_rate_sum;
u32 tx_avg_rate_num; u32 tx_avg_rate_num;
#ifdef CONFIG_MAC80211_DEBUGFS
struct dentry *tx_avg_rate_sum_dentry;
struct dentry *tx_avg_rate_num_dentry;
#endif
}; };
...@@ -327,6 +333,67 @@ static void rate_control_simple_free_sta(void *priv, void *priv_sta) ...@@ -327,6 +333,67 @@ static void rate_control_simple_free_sta(void *priv, void *priv_sta)
kfree(rctrl); kfree(rctrl);
} }
#ifdef CONFIG_MAC80211_DEBUGFS
static int open_file_generic(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t sta_tx_avg_rate_sum_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct sta_rate_control *srctrl = file->private_data;
char buf[20];
sprintf(buf, "%d\n", srctrl->tx_avg_rate_sum);
return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
}
static const struct file_operations sta_tx_avg_rate_sum_ops = {
.read = sta_tx_avg_rate_sum_read,
.open = open_file_generic,
};
static ssize_t sta_tx_avg_rate_num_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct sta_rate_control *srctrl = file->private_data;
char buf[20];
sprintf(buf, "%d\n", srctrl->tx_avg_rate_num);
return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
}
static const struct file_operations sta_tx_avg_rate_num_ops = {
.read = sta_tx_avg_rate_num_read,
.open = open_file_generic,
};
static void rate_control_simple_add_sta_debugfs(void *priv, void *priv_sta,
struct dentry *dir)
{
struct sta_rate_control *srctrl = priv_sta;
srctrl->tx_avg_rate_num_dentry =
debugfs_create_file("rc_simple_sta_tx_avg_rate_num", 0400,
dir, srctrl, &sta_tx_avg_rate_num_ops);
srctrl->tx_avg_rate_sum_dentry =
debugfs_create_file("rc_simple_sta_tx_avg_rate_sum", 0400,
dir, srctrl, &sta_tx_avg_rate_sum_ops);
}
static void rate_control_simple_remove_sta_debugfs(void *priv, void *priv_sta)
{
struct sta_rate_control *srctrl = priv_sta;
debugfs_remove(srctrl->tx_avg_rate_sum_dentry);
debugfs_remove(srctrl->tx_avg_rate_num_dentry);
}
#endif
static struct rate_control_ops rate_control_simple = { static struct rate_control_ops rate_control_simple = {
.module = THIS_MODULE, .module = THIS_MODULE,
...@@ -339,6 +406,10 @@ static struct rate_control_ops rate_control_simple = { ...@@ -339,6 +406,10 @@ static struct rate_control_ops rate_control_simple = {
.free = rate_control_simple_free, .free = rate_control_simple_free,
.alloc_sta = rate_control_simple_alloc_sta, .alloc_sta = rate_control_simple_alloc_sta,
.free_sta = rate_control_simple_free_sta, .free_sta = rate_control_simple_free_sta,
#ifdef CONFIG_MAC80211_DEBUGFS
.add_sta_debugfs = rate_control_simple_add_sta_debugfs,
.remove_sta_debugfs = rate_control_simple_remove_sta_debugfs,
#endif
}; };
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "ieee80211_rate.h" #include "ieee80211_rate.h"
#include "sta_info.h" #include "sta_info.h"
#include "debugfs_key.h"
#include "debugfs_sta.h"
/* Caller must hold local->sta_lock */ /* Caller must hold local->sta_lock */
static void sta_info_hash_add(struct ieee80211_local *local, static void sta_info_hash_add(struct ieee80211_local *local,
...@@ -120,6 +122,8 @@ static void sta_info_release(struct kref *kref) ...@@ -120,6 +122,8 @@ static void sta_info_release(struct kref *kref)
} }
rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
rate_control_put(sta->rate_ctrl); rate_control_put(sta->rate_ctrl);
if (sta->key)
ieee80211_debugfs_key_sta_del(sta->key, sta);
kfree(sta); kfree(sta);
} }
...@@ -173,9 +177,42 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, ...@@ -173,9 +177,42 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
local->mdev->name, MAC_ARG(addr)); local->mdev->name, MAC_ARG(addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_DEBUGFS
if (!in_interrupt()) {
sta->debugfs_registered = 1;
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
} else {
/* debugfs entry adding might sleep, so schedule process
* context task for adding entry for STAs that do not yet
* have one. */
queue_work(local->hw.workqueue, &local->sta_debugfs_add);
}
#endif
return sta; return sta;
} }
static void finish_sta_info_free(struct ieee80211_local *local,
struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n",
local->mdev->name, MAC_ARG(sta->addr));
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
if (sta->key) {
ieee80211_debugfs_key_remove(sta->key);
ieee80211_key_free(sta->key);
sta->key = NULL;
}
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
sta_info_put(sta);
}
static void sta_info_remove(struct sta_info *sta) static void sta_info_remove(struct sta_info *sta)
{ {
struct ieee80211_local *local = sta->local; struct ieee80211_local *local = sta->local;
...@@ -239,17 +276,13 @@ void sta_info_free(struct sta_info *sta, int locked) ...@@ -239,17 +276,13 @@ void sta_info_free(struct sta_info *sta, int locked)
sta->key_idx_compression = HW_KEY_IDX_INVALID; sta->key_idx_compression = HW_KEY_IDX_INVALID;
} }
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG #ifdef CONFIG_MAC80211_DEBUGFS
printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n", if (in_atomic()) {
local->mdev->name, MAC_ARG(sta->addr)); list_add(&sta->list, &local->deleted_sta_list);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ queue_work(local->hw.workqueue, &local->sta_debugfs_add);
} else
if (sta->key) { #endif
ieee80211_key_free(sta->key); finish_sta_info_free(local, sta);
sta->key = NULL;
}
sta_info_put(sta);
} }
...@@ -322,6 +355,50 @@ static void sta_info_cleanup(unsigned long data) ...@@ -322,6 +355,50 @@ static void sta_info_cleanup(unsigned long data)
add_timer(&local->sta_cleanup); add_timer(&local->sta_cleanup);
} }
#ifdef CONFIG_MAC80211_DEBUGFS
static void sta_info_debugfs_add_task(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, sta_debugfs_add);
struct sta_info *sta, *tmp;
while (1) {
spin_lock_bh(&local->sta_lock);
if (!list_empty(&local->deleted_sta_list)) {
sta = list_entry(local->deleted_sta_list.next,
struct sta_info, list);
list_del(local->deleted_sta_list.next);
} else
sta = NULL;
spin_unlock_bh(&local->sta_lock);
if (!sta)
break;
finish_sta_info_free(local, sta);
}
while (1) {
sta = NULL;
spin_lock_bh(&local->sta_lock);
list_for_each_entry(tmp, &local->sta_list, list) {
if (!tmp->debugfs_registered) {
sta = tmp;
__sta_info_get(sta);
break;
}
}
spin_unlock_bh(&local->sta_lock);
if (!sta)
break;
sta->debugfs_registered = 1;
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
sta_info_put(sta);
}
}
#endif
void sta_info_init(struct ieee80211_local *local) void sta_info_init(struct ieee80211_local *local)
{ {
spin_lock_init(&local->sta_lock); spin_lock_init(&local->sta_lock);
...@@ -332,6 +409,10 @@ void sta_info_init(struct ieee80211_local *local) ...@@ -332,6 +409,10 @@ void sta_info_init(struct ieee80211_local *local)
local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
local->sta_cleanup.data = (unsigned long) local; local->sta_cleanup.data = (unsigned long) local;
local->sta_cleanup.function = sta_info_cleanup; local->sta_cleanup.function = sta_info_cleanup;
#ifdef CONFIG_MAC80211_DEBUGFS
INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task);
#endif
} }
int sta_info_start(struct ieee80211_local *local) int sta_info_start(struct ieee80211_local *local)
...@@ -347,7 +428,10 @@ void sta_info_stop(struct ieee80211_local *local) ...@@ -347,7 +428,10 @@ void sta_info_stop(struct ieee80211_local *local)
del_timer(&local->sta_cleanup); del_timer(&local->sta_cleanup);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
/* We don't need locking at this point. */ /* sta_info_free must be called with 0 as the last
* parameter to ensure all debugfs sta entries are
* unregistered. We don't need locking at this
* point. */
sta_info_free(sta, 0); sta_info_free(sta, 0);
} }
} }
......
...@@ -98,6 +98,9 @@ struct sta_info { ...@@ -98,6 +98,9 @@ struct sta_info {
* filtering; used only if sta->key is not * filtering; used only if sta->key is not
* set */ * set */
#ifdef CONFIG_MAC80211_DEBUGFS
int debugfs_registered;
#endif
int assoc_ap; /* whether this is an AP that we are int assoc_ap; /* whether this is an AP that we are
* associated with as a client */ * associated with as a client */
...@@ -109,6 +112,22 @@ struct sta_info { ...@@ -109,6 +112,22 @@ struct sta_info {
int vlan_id; int vlan_id;
u16 listen_interval; u16 listen_interval;
#ifdef CONFIG_MAC80211_DEBUGFS
struct sta_info_debugfsdentries {
struct dentry *dir;
struct dentry *flags;
struct dentry *num_ps_buf_frames;
struct dentry *last_ack_rssi;
struct dentry *last_ack_ms;
struct dentry *inactive_ms;
struct dentry *last_seq_ctrl;
#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
struct dentry *wme_rx_queue;
struct dentry *wme_tx_queue;
#endif
} debugfs;
#endif
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment