Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller 2009-05-25 00:38:24 -07:00
commit 45ea4ea2af
124 changed files with 13213 additions and 1745 deletions

View file

@ -10,6 +10,14 @@ config CFG80211_REG_DEBUG
If unsure, say N.
config CFG80211_DEBUGFS
bool "cfg80211 DebugFS entries"
depends on CFG80211 && DEBUG_FS
---help---
You can enable this if you want to debugfs entries for cfg80211.
If unsure, say N.
config WIRELESS_OLD_REGULATORY
bool "Old wireless static regulatory definitions"
default n

View file

@ -6,6 +6,7 @@ obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
ccflags-y += -D__CHECK_ENDIAN__

View file

@ -17,6 +17,7 @@
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
#include "debugfs.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
@ -228,7 +229,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
/* exported functions */
struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
{
static int wiphy_counter;
@ -375,6 +376,8 @@ int wiphy_register(struct wiphy *wiphy)
nl80211_send_reg_change_event(&request);
}
cfg80211_debugfs_drv_add(drv);
res = 0;
out_unlock:
mutex_unlock(&cfg80211_mutex);
@ -405,6 +408,8 @@ void wiphy_unregister(struct wiphy *wiphy)
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
cfg80211_debugfs_drv_del(drv);
/* If this device got a regulatory hint tell core its
* free to listen now to a new shiny device regulatory hint */
reg_device_remove(wiphy);

View file

@ -10,12 +10,13 @@
#include <linux/netdevice.h>
#include <linux/kref.h>
#include <linux/rbtree.h>
#include <linux/debugfs.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "reg.h"
struct cfg80211_registered_device {
struct cfg80211_ops *ops;
const struct cfg80211_ops *ops;
struct list_head list;
/* we hold this mutex during any call so that
* we cannot do multiple calls at once, and also
@ -50,6 +51,17 @@ struct cfg80211_registered_device {
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
unsigned long suspend_at;
#ifdef CONFIG_CFG80211_DEBUGFS
/* Debugfs entries */
struct wiphy_debugfsdentries {
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
struct dentry *long_retry_limit;
struct dentry *ht40allow_map;
} debugfs;
#endif
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));

131
net/wireless/debugfs.c Normal file
View file

@ -0,0 +1,131 @@
/*
* cfg80211 debugfs
*
* Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* 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 "core.h"
#include "debugfs.h"
static int cfg80211_open_file_generic(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wiphy *wiphy= file->private_data; \
char buf[buflen]; \
int res; \
\
res = scnprintf(buf, buflen, fmt "\n", ##value); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = cfg80211_open_file_generic, \
};
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
wiphy->rts_threshold)
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
wiphy->frag_threshold);
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
wiphy->retry_short)
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
wiphy->retry_long);
static int ht_print_chan(struct ieee80211_channel *chan,
char *buf, int buf_size, int offset)
{
if (WARN_ON(offset > buf_size))
return 0;
if (chan->flags & IEEE80211_CHAN_DISABLED)
return snprintf(buf + offset,
buf_size - offset,
"%d Disabled\n",
chan->center_freq);
return snprintf(buf + offset,
buf_size - offset,
"%d HT40 %c%c\n",
chan->center_freq,
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-',
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+');
}
static ssize_t ht40allow_map_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wiphy *wiphy = file->private_data;
char *buf;
unsigned int offset = 0, buf_size = PAGE_SIZE, i, r;
enum ieee80211_band band;
struct ieee80211_supported_band *sband;
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&cfg80211_mutex);
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++)
offset += ht_print_chan(&sband->channels[i],
buf, buf_size, offset);
}
mutex_unlock(&cfg80211_mutex);
r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
kfree(buf);
return r;
}
static const struct file_operations ht40allow_map_ops = {
.read = ht40allow_map_read,
.open = cfg80211_open_file_generic,
};
#define DEBUGFS_ADD(name) \
drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \
&drv->wiphy, &name## _ops);
#define DEBUGFS_DEL(name) \
debugfs_remove(drv->debugfs.name); \
drv->debugfs.name = NULL;
void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv)
{
struct dentry *phyd = drv->wiphy.debugfsdir;
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
DEBUGFS_ADD(long_retry_limit);
DEBUGFS_ADD(ht40allow_map);
}
void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv)
{
DEBUGFS_DEL(rts_threshold);
DEBUGFS_DEL(fragmentation_threshold);
DEBUGFS_DEL(short_retry_limit);
DEBUGFS_DEL(long_retry_limit);
DEBUGFS_DEL(ht40allow_map);
}

14
net/wireless/debugfs.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef __CFG80211_DEBUGFS_H
#define __CFG80211_DEBUGFS_H
#ifdef CONFIG_CFG80211_DEBUGFS
void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv);
void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv);
#else
static inline
void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {}
static inline
void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {}
#endif
#endif /* __CFG80211_DEBUGFS_H */

View file

@ -77,6 +77,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
@ -492,7 +493,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
struct ieee80211_channel *chan;
struct ieee80211_sta_ht_cap *ht_cap;
u32 freq, sec_freq;
u32 freq;
if (!rdev->ops->set_channel) {
result = -EOPNOTSUPP;
@ -518,33 +519,28 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
goto bad_res;
if (channel_type == NL80211_CHAN_HT40MINUS)
sec_freq = freq - 20;
else if (channel_type == NL80211_CHAN_HT40PLUS)
sec_freq = freq + 20;
else
sec_freq = 0;
if (channel_type == NL80211_CHAN_HT40MINUS &&
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
goto bad_res;
else if (channel_type == NL80211_CHAN_HT40PLUS &&
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
goto bad_res;
/*
* At this point we know if that if HT40 was requested
* we are allowed to use it and the extension channel
* exists.
*/
ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
/* no HT capabilities */
if (channel_type != NL80211_CHAN_NO_HT &&
!ht_cap->ht_supported)
goto bad_res;
if (sec_freq) {
struct ieee80211_channel *schan;
/* no 40 MHz capabilities */
/* no HT capabilities or intolerant */
if (channel_type != NL80211_CHAN_NO_HT) {
if (!ht_cap->ht_supported)
goto bad_res;
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
(ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
goto bad_res;
schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
/* Secondary channel not allowed */
if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
goto bad_res;
}
result = rdev->ops->set_channel(&rdev->wiphy, chan,
@ -2571,18 +2567,24 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
rem_reg_rules) {
num_rules++;
if (num_rules > NL80211_MAX_SUPP_REG_RULES)
goto bad_reg;
return -EINVAL;
}
if (!reg_is_valid_request(alpha2))
return -EINVAL;
mutex_lock(&cfg80211_mutex);
if (!reg_is_valid_request(alpha2)) {
r = -EINVAL;
goto bad_reg;
}
size_of_regd = sizeof(struct ieee80211_regdomain) +
(num_rules * sizeof(struct ieee80211_reg_rule));
rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!rd)
return -ENOMEM;
if (!rd) {
r = -ENOMEM;
goto bad_reg;
}
rd->n_reg_rules = num_rules;
rd->alpha2[0] = alpha2[0];
@ -2599,20 +2601,24 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
rule_idx++;
if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
r = -EINVAL;
goto bad_reg;
}
}
BUG_ON(rule_idx != num_rules);
mutex_lock(&cfg80211_mutex);
r = set_regdom(rd);
mutex_unlock(&cfg80211_mutex);
return r;
bad_reg:
mutex_unlock(&cfg80211_mutex);
kfree(rd);
return -EINVAL;
return r;
}
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)

View file

@ -48,12 +48,6 @@ static struct regulatory_request *last_request;
/* To trigger userspace events */
static struct platform_device *reg_pdev;
/* Keep the ordering from large to small */
static u32 supported_bandwidths[] = {
MHZ_TO_KHZ(40),
MHZ_TO_KHZ(20),
};
/*
* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
@ -388,6 +382,8 @@ static int call_crda(const char *alpha2)
/* Used by nl80211 before kmalloc'ing our regulatory domain */
bool reg_is_valid_request(const char *alpha2)
{
assert_cfg80211_lock();
if (!last_request)
return false;
@ -435,19 +431,20 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
return true;
}
/* Returns value in KHz */
static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
u32 freq)
static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
u32 center_freq_khz,
u32 bw_khz)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) {
u32 start_freq_khz = freq - supported_bandwidths[i]/2;
u32 end_freq_khz = freq + supported_bandwidths[i]/2;
if (start_freq_khz >= freq_range->start_freq_khz &&
end_freq_khz <= freq_range->end_freq_khz)
return supported_bandwidths[i];
}
return 0;
u32 start_freq_khz, end_freq_khz;
start_freq_khz = center_freq_khz - (bw_khz/2);
end_freq_khz = center_freq_khz + (bw_khz/2);
if (start_freq_khz >= freq_range->start_freq_khz &&
end_freq_khz <= freq_range->end_freq_khz)
return true;
return false;
}
/**
@ -847,14 +844,17 @@ static u32 map_regdom_flags(u32 rd_flags)
static int freq_reg_info_regd(struct wiphy *wiphy,
u32 center_freq,
u32 *bandwidth,
u32 desired_bw_khz,
const struct ieee80211_reg_rule **reg_rule,
const struct ieee80211_regdomain *custom_regd)
{
int i;
bool band_rule_found = false;
const struct ieee80211_regdomain *regd;
u32 max_bandwidth = 0;
bool bw_fits = false;
if (!desired_bw_khz)
desired_bw_khz = MHZ_TO_KHZ(20);
regd = custom_regd ? custom_regd : cfg80211_regdomain;
@ -887,38 +887,54 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq);
max_bandwidth = freq_max_bandwidth(fr, center_freq);
bw_fits = reg_does_bw_fit(fr,
center_freq,
desired_bw_khz);
if (max_bandwidth && *bandwidth <= max_bandwidth) {
if (band_rule_found && bw_fits) {
*reg_rule = rr;
*bandwidth = max_bandwidth;
break;
return 0;
}
}
if (!band_rule_found)
return -ERANGE;
return !max_bandwidth;
return -EINVAL;
}
EXPORT_SYMBOL(freq_reg_info);
int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
const struct ieee80211_reg_rule **reg_rule)
int freq_reg_info(struct wiphy *wiphy,
u32 center_freq,
u32 desired_bw_khz,
const struct ieee80211_reg_rule **reg_rule)
{
assert_cfg80211_lock();
return freq_reg_info_regd(wiphy, center_freq,
bandwidth, reg_rule, NULL);
return freq_reg_info_regd(wiphy,
center_freq,
desired_bw_khz,
reg_rule,
NULL);
}
/*
* Note that right now we assume the desired channel bandwidth
* is always 20 MHz for each individual channel (HT40 uses 20 MHz
* per channel, the primary and the extension channel). To support
* smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
* new ieee80211_channel.target_bw and re run the regulatory check
* on the wiphy with the target_bw specified. Then we can simply use
* that below for the desired_bw_khz below.
*/
static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
unsigned int chan_idx)
{
int r;
u32 flags;
u32 max_bandwidth = 0;
u32 flags, bw_flags = 0;
u32 desired_bw_khz = MHZ_TO_KHZ(20);
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct wiphy *request_wiphy = NULL;
@ -933,8 +949,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
flags = chan->orig_flags;
r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq),
&max_bandwidth, &reg_rule);
r = freq_reg_info(wiphy,
MHZ_TO_KHZ(chan->center_freq),
desired_bw_khz,
&reg_rule);
if (r) {
/*
@ -977,6 +995,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
}
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
@ -987,19 +1009,19 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
* settings
*/
chan->flags = chan->orig_flags =
map_regdom_flags(reg_rule->flags);
map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = chan->orig_mag =
(int) MBI_TO_DBI(power_rule->max_antenna_gain);
chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
chan->max_power = chan->orig_mpwr =
(int) MBM_TO_DBM(power_rule->max_eirp);
return;
}
chan->flags = flags | map_regdom_flags(reg_rule->flags);
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
chan->max_antenna_gain = min(chan->orig_mag,
(int) MBI_TO_DBI(power_rule->max_antenna_gain));
chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
if (chan->orig_mpwr)
chan->max_power = min(chan->orig_mpwr,
(int) MBM_TO_DBM(power_rule->max_eirp));
@ -1156,6 +1178,93 @@ static void reg_process_beacons(struct wiphy *wiphy)
wiphy_update_beacon_reg(wiphy);
}
static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
{
if (!chan)
return true;
if (chan->flags & IEEE80211_CHAN_DISABLED)
return true;
/* This would happen when regulatory rules disallow HT40 completely */
if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
return true;
return false;
}
static void reg_process_ht_flags_channel(struct wiphy *wiphy,
enum ieee80211_band band,
unsigned int chan_idx)
{
struct ieee80211_supported_band *sband;
struct ieee80211_channel *channel;
struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
unsigned int i;
assert_cfg80211_lock();
sband = wiphy->bands[band];
BUG_ON(chan_idx >= sband->n_channels);
channel = &sband->channels[chan_idx];
if (is_ht40_not_allowed(channel)) {
channel->flags |= IEEE80211_CHAN_NO_HT40;
return;
}
/*
* We need to ensure the extension channels exist to
* be able to use HT40- or HT40+, this finds them (or not)
*/
for (i = 0; i < sband->n_channels; i++) {
struct ieee80211_channel *c = &sband->channels[i];
if (c->center_freq == (channel->center_freq - 20))
channel_before = c;
if (c->center_freq == (channel->center_freq + 20))
channel_after = c;
}
/*
* Please note that this assumes target bandwidth is 20 MHz,
* if that ever changes we also need to change the below logic
* to include that as well.
*/
if (is_ht40_not_allowed(channel_before))
channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
else
channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
if (is_ht40_not_allowed(channel_after))
channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
else
channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
}
static void reg_process_ht_flags_band(struct wiphy *wiphy,
enum ieee80211_band band)
{
unsigned int i;
struct ieee80211_supported_band *sband;
BUG_ON(!wiphy->bands[band]);
sband = wiphy->bands[band];
for (i = 0; i < sband->n_channels; i++)
reg_process_ht_flags_channel(wiphy, band, i);
}
static void reg_process_ht_flags(struct wiphy *wiphy)
{
enum ieee80211_band band;
if (!wiphy)
return;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (wiphy->bands[band])
reg_process_ht_flags_band(wiphy, band);
}
}
void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
@ -1169,6 +1278,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy,
}
out:
reg_process_beacons(wiphy);
reg_process_ht_flags(wiphy);
if (wiphy->reg_notifier)
wiphy->reg_notifier(wiphy, last_request);
}
@ -1179,9 +1289,11 @@ static void handle_channel_custom(struct wiphy *wiphy,
const struct ieee80211_regdomain *regd)
{
int r;
u32 max_bandwidth = 0;
u32 desired_bw_khz = MHZ_TO_KHZ(20);
u32 bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
@ -1191,8 +1303,11 @@ static void handle_channel_custom(struct wiphy *wiphy,
BUG_ON(chan_idx >= sband->n_channels);
chan = &sband->channels[chan_idx];
r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
&max_bandwidth, &reg_rule, regd);
r = freq_reg_info_regd(wiphy,
MHZ_TO_KHZ(chan->center_freq),
desired_bw_khz,
&reg_rule,
regd);
if (r) {
chan->flags = IEEE80211_CHAN_DISABLED;
@ -1200,10 +1315,14 @@ static void handle_channel_custom(struct wiphy *wiphy,
}
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
chan->flags |= map_regdom_flags(reg_rule->flags);
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
chan->max_bandwidth = KHZ_TO_MHZ(desired_bw_khz);
chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
}
@ -1225,13 +1344,22 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
const struct ieee80211_regdomain *regd)
{
enum ieee80211_band band;
unsigned int bands_set = 0;
mutex_lock(&cfg80211_mutex);
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (wiphy->bands[band])
handle_band_custom(wiphy, band, regd);
if (!wiphy->bands[band])
continue;
handle_band_custom(wiphy, band, regd);
bands_set++;
}
mutex_unlock(&cfg80211_mutex);
/*
* no point in calling this if it won't have any effect
* on your device's supportd bands.
*/
WARN_ON(!bands_set);
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);

View file

@ -4,7 +4,9 @@
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/bitops.h>
#include <linux/etherdevice.h>
#include <net/cfg80211.h>
#include <net/ip.h>
#include "core.h"
struct ieee80211_rate *
@ -181,5 +183,323 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
return -EINVAL;
}
if (params->seq) {
switch (params->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
/* These ciphers do not use key sequence */
return -EINVAL;
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_AES_CMAC:
if (params->seq_len != 6)
return -EINVAL;
break;
}
}
return 0;
}
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
const unsigned char rfc1042_header[] __aligned(2) =
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
EXPORT_SYMBOL(rfc1042_header);
/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
const unsigned char bridge_tunnel_header[] __aligned(2) =
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
EXPORT_SYMBOL(bridge_tunnel_header);
unsigned int ieee80211_hdrlen(__le16 fc)
{
unsigned int hdrlen = 24;
if (ieee80211_is_data(fc)) {
if (ieee80211_has_a4(fc))
hdrlen = 30;
if (ieee80211_is_data_qos(fc))
hdrlen += IEEE80211_QOS_CTL_LEN;
goto out;
}
if (ieee80211_is_ctl(fc)) {
/*
* ACK and CTS are 10 bytes, all others 16. To see how
* to get this condition consider
* subtype mask: 0b0000000011110000 (0x00F0)
* ACK subtype: 0b0000000011010000 (0x00D0)
* CTS subtype: 0b0000000011000000 (0x00C0)
* bits that matter: ^^^ (0x00E0)
* value of those: 0b0000000011000000 (0x00C0)
*/
if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
hdrlen = 10;
else
hdrlen = 16;
}
out:
return hdrlen;
}
EXPORT_SYMBOL(ieee80211_hdrlen);
unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
{
const struct ieee80211_hdr *hdr =
(const struct ieee80211_hdr *)skb->data;
unsigned int hdrlen;
if (unlikely(skb->len < 10))
return 0;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (unlikely(hdrlen > skb->len))
return 0;
return hdrlen;
}
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
{
int ae = meshhdr->flags & MESH_FLAGS_AE;
/* 7.1.3.5a.2 */
switch (ae) {
case 0:
return 6;
case 1:
return 12;
case 2:
return 18;
case 3:
return 24;
default:
return 6;
}
}
int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
enum nl80211_iftype iftype)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 hdrlen, ethertype;
u8 *payload;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN] __aligned(2);
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return -1;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
* IEEE 802.11 address fields:
* ToDS FromDS Addr1 Addr2 Addr3 Addr4
* 0 0 DA SA BSSID n/a
* 0 1 DA BSSID SA n/a
* 1 0 BSSID SA DA n/a
* 1 1 RA TA DA SA
*/
memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
switch (hdr->frame_control &
cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
case cpu_to_le16(IEEE80211_FCTL_TODS):
if (unlikely(iftype != NL80211_IFTYPE_AP &&
iftype != NL80211_IFTYPE_AP_VLAN))
return -1;
break;
case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
if (unlikely(iftype != NL80211_IFTYPE_WDS &&
iftype != NL80211_IFTYPE_MESH_POINT))
return -1;
if (iftype == NL80211_IFTYPE_MESH_POINT) {
struct ieee80211s_hdr *meshdr =
(struct ieee80211s_hdr *) (skb->data + hdrlen);
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
memcpy(dst, meshdr->eaddr1, ETH_ALEN);
memcpy(src, meshdr->eaddr2, ETH_ALEN);
}
}
break;
case cpu_to_le16(IEEE80211_FCTL_FROMDS):
if (iftype != NL80211_IFTYPE_STATION ||
(is_multicast_ether_addr(dst) &&
!compare_ether_addr(src, addr)))
return -1;
break;
case cpu_to_le16(0):
if (iftype != NL80211_IFTYPE_ADHOC)
return -1;
break;
}
if (unlikely(skb->len - hdrlen < 8))
return -1;
payload = skb->data + hdrlen;
ethertype = (payload[6] << 8) | payload[7];
if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
/* remove RFC1042 or Bridge-Tunnel encapsulation and
* replace EtherType */
skb_pull(skb, hdrlen + 6);
memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
} else {
struct ethhdr *ehdr;
__be16 len;
skb_pull(skb, hdrlen);
len = htons(skb->len);
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
memcpy(ehdr->h_dest, dst, ETH_ALEN);
memcpy(ehdr->h_source, src, ETH_ALEN);
ehdr->h_proto = len;
}
return 0;
}
EXPORT_SYMBOL(ieee80211_data_to_8023);
int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
enum nl80211_iftype iftype, u8 *bssid, bool qos)
{
struct ieee80211_hdr hdr;
u16 hdrlen, ethertype;
__le16 fc;
const u8 *encaps_data;
int encaps_len, skip_header_bytes;
int nh_pos, h_pos;
int head_need;
if (unlikely(skb->len < ETH_HLEN))
return -EINVAL;
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
/* convert Ethernet header to proper 802.11 header (based on
* operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 24;
break;
case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
memcpy(hdr.addr1, bssid, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, bssid, ETH_ALEN);
hdrlen = 24;
break;
default:
return -EOPNOTSUPP;
}
if (qos) {
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2;
}
hdr.frame_control = fc;
hdr.duration_id = 0;
hdr.seq_ctrl = 0;
skip_header_bytes = ETH_HLEN;
if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
encaps_data = bridge_tunnel_header;
encaps_len = sizeof(bridge_tunnel_header);
skip_header_bytes -= 2;
} else if (ethertype > 0x600) {
encaps_data = rfc1042_header;
encaps_len = sizeof(rfc1042_header);
skip_header_bytes -= 2;
} else {
encaps_data = NULL;
encaps_len = 0;
}
skb_pull(skb, skip_header_bytes);
nh_pos -= skip_header_bytes;
h_pos -= skip_header_bytes;
head_need = hdrlen + encaps_len - skb_headroom(skb);
if (head_need > 0 || skb_cloned(skb)) {
head_need = max(head_need, 0);
if (head_need)
skb_orphan(skb);
if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) {
printk(KERN_ERR "failed to reallocate Tx buffer\n");
return -ENOMEM;
}
skb->truesize += head_need;
}
if (encaps_data) {
memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
nh_pos += encaps_len;
h_pos += encaps_len;
}
memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
nh_pos += hdrlen;
h_pos += hdrlen;
/* Update skb pointers to various headers since this modified frame
* is going to go through Linux networking code that may potentially
* need things like pointer to IP header. */
skb_set_mac_header(skb, 0);
skb_set_network_header(skb, nh_pos);
skb_set_transport_header(skb, h_pos);
return 0;
}
EXPORT_SYMBOL(ieee80211_data_from_8023);
/* Given a data frame determine the 802.1p/1d tag to use. */
unsigned int cfg80211_classify8021d(struct sk_buff *skb)
{
unsigned int dscp;
/* skb->priority values from 256->263 are magic values to
* directly indicate a specific 802.1d priority. This is used
* to allow 802.1d priority to be passed directly in from VLAN
* tags, etc.
*/
if (skb->priority >= 256 && skb->priority <= 263)
return skb->priority - 256;
switch (skb->protocol) {
case htons(ETH_P_IP):
dscp = ip_hdr(skb)->tos & 0xfc;
break;
default:
return 0;
}
return dscp >> 5;
}
EXPORT_SYMBOL(cfg80211_classify8021d);

View file

@ -504,6 +504,13 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
else if (idx == wdev->wext.default_mgmt_key)
wdev->wext.default_mgmt_key = -1;
}
/*
* Applications using wireless extensions expect to be
* able to delete keys that don't exist, so allow that.
*/
if (err == -ENOENT)
return 0;
return err;
} else {
if (addr)

View file

@ -636,8 +636,10 @@ static void wireless_seq_printf_stats(struct seq_file *seq,
/*
* Print info for /proc/net/wireless (print all entries)
*/
static int wireless_seq_show(struct seq_file *seq, void *v)
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"
@ -651,21 +653,41 @@ static int wireless_seq_show(struct seq_file *seq, void *v)
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();
return dev_seq_start(seq, pos);
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)
{
dev_seq_stop(seq, v);
rtnl_unlock();
}
static const struct seq_operations wireless_seq_ops = {
.start = wireless_dev_seq_start,
.next = dev_seq_next,
.next = wireless_dev_seq_next,
.stop = wireless_dev_seq_stop,
.show = wireless_seq_show,
.show = wireless_dev_seq_show,
};
static int seq_open_wireless(struct inode *inode, struct file *file)