Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Conflicts: drivers/net/wireless/iwlwifi/iwl-core.h
This commit is contained in:
@@ -402,6 +402,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
||||
rdev->wiphy.retry_long = 4;
|
||||
rdev->wiphy.frag_threshold = (u32) -1;
|
||||
rdev->wiphy.rts_threshold = (u32) -1;
|
||||
rdev->wiphy.coverage_class = 0;
|
||||
|
||||
return &rdev->wiphy;
|
||||
}
|
||||
|
@@ -111,7 +111,8 @@ struct cfg80211_internal_bss {
|
||||
unsigned long ts;
|
||||
struct kref ref;
|
||||
atomic_t hold;
|
||||
bool ies_allocated;
|
||||
bool beacon_ies_allocated;
|
||||
bool proberesp_ies_allocated;
|
||||
|
||||
/* must be last because of priv member */
|
||||
struct cfg80211_bss pub;
|
||||
|
@@ -69,6 +69,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
|
||||
[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
|
||||
|
||||
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
|
||||
@@ -143,6 +144,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
|
||||
.len = WLAN_PMKID_LEN },
|
||||
[NL80211_ATTR_DURATION] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
|
||||
[NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
/* policy for the attributes */
|
||||
@@ -444,6 +446,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
||||
dev->wiphy.frag_threshold);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
|
||||
dev->wiphy.rts_threshold);
|
||||
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
|
||||
dev->wiphy.coverage_class);
|
||||
|
||||
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
|
||||
dev->wiphy.max_scan_ssids);
|
||||
@@ -572,6 +576,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
||||
CMD(del_pmksa, DEL_PMKSA);
|
||||
CMD(flush_pmksa, FLUSH_PMKSA);
|
||||
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
||||
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
||||
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
||||
i++;
|
||||
NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
|
||||
@@ -684,6 +689,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
u32 changed;
|
||||
u8 retry_short = 0, retry_long = 0;
|
||||
u32 frag_threshold = 0, rts_threshold = 0;
|
||||
u8 coverage_class = 0;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
@@ -806,9 +812,16 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
changed |= WIPHY_PARAM_RTS_THRESHOLD;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
|
||||
coverage_class = nla_get_u8(
|
||||
info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
|
||||
changed |= WIPHY_PARAM_COVERAGE_CLASS;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
u8 old_retry_short, old_retry_long;
|
||||
u32 old_frag_threshold, old_rts_threshold;
|
||||
u8 old_coverage_class;
|
||||
|
||||
if (!rdev->ops->set_wiphy_params) {
|
||||
result = -EOPNOTSUPP;
|
||||
@@ -819,6 +832,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
old_retry_long = rdev->wiphy.retry_long;
|
||||
old_frag_threshold = rdev->wiphy.frag_threshold;
|
||||
old_rts_threshold = rdev->wiphy.rts_threshold;
|
||||
old_coverage_class = rdev->wiphy.coverage_class;
|
||||
|
||||
if (changed & WIPHY_PARAM_RETRY_SHORT)
|
||||
rdev->wiphy.retry_short = retry_short;
|
||||
@@ -828,6 +842,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
rdev->wiphy.frag_threshold = frag_threshold;
|
||||
if (changed & WIPHY_PARAM_RTS_THRESHOLD)
|
||||
rdev->wiphy.rts_threshold = rts_threshold;
|
||||
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
|
||||
rdev->wiphy.coverage_class = coverage_class;
|
||||
|
||||
result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
|
||||
if (result) {
|
||||
@@ -835,6 +851,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
rdev->wiphy.retry_long = old_retry_long;
|
||||
rdev->wiphy.frag_threshold = old_frag_threshold;
|
||||
rdev->wiphy.rts_threshold = old_rts_threshold;
|
||||
rdev->wiphy.coverage_class = old_coverage_class;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3146,6 +3163,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
||||
NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
|
||||
res->len_information_elements,
|
||||
res->information_elements);
|
||||
if (res->beacon_ies && res->len_beacon_ies &&
|
||||
res->beacon_ies != res->information_elements)
|
||||
NLA_PUT(msg, NL80211_BSS_BEACON_IES,
|
||||
res->len_beacon_ies, res->beacon_ies);
|
||||
if (res->tsf)
|
||||
NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
|
||||
if (res->beacon_interval)
|
||||
@@ -4423,6 +4444,109 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
|
||||
return err;
|
||||
}
|
||||
|
||||
static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
|
||||
u8 *rates, u8 rates_len)
|
||||
{
|
||||
u8 i;
|
||||
u32 mask = 0;
|
||||
|
||||
for (i = 0; i < rates_len; i++) {
|
||||
int rate = (rates[i] & 0x7f) * 5;
|
||||
int ridx;
|
||||
for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
|
||||
struct ieee80211_rate *srate =
|
||||
&sband->bitrates[ridx];
|
||||
if (rate == srate->bitrate) {
|
||||
mask |= 1 << ridx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ridx == sband->n_bitrates)
|
||||
return 0; /* rate not found */
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static struct nla_policy
|
||||
nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] __read_mostly = {
|
||||
[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
|
||||
.len = NL80211_MAX_SUPP_RATES },
|
||||
};
|
||||
|
||||
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct nlattr *tb[NL80211_TXRATE_MAX + 1];
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct cfg80211_bitrate_mask mask;
|
||||
int err, rem, i;
|
||||
struct net_device *dev;
|
||||
struct nlattr *tx_rates;
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
||||
if (err)
|
||||
goto unlock_rtnl;
|
||||
|
||||
if (!rdev->ops->set_bitrate_mask) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
memset(&mask, 0, sizeof(mask));
|
||||
/* Default to all rates enabled */
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
sband = rdev->wiphy.bands[i];
|
||||
mask.control[i].legacy =
|
||||
sband ? (1 << sband->n_bitrates) - 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The nested attribute uses enum nl80211_band as the index. This maps
|
||||
* directly to the enum ieee80211_band values used in cfg80211.
|
||||
*/
|
||||
nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
|
||||
{
|
||||
enum ieee80211_band band = nla_type(tx_rates);
|
||||
if (band < 0 || band >= IEEE80211_NUM_BANDS) {
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
sband = rdev->wiphy.bands[band];
|
||||
if (sband == NULL) {
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
|
||||
nla_len(tx_rates), nl80211_txattr_policy);
|
||||
if (tb[NL80211_TXRATE_LEGACY]) {
|
||||
mask.control[band].legacy = rateset_to_mask(
|
||||
sband,
|
||||
nla_data(tb[NL80211_TXRATE_LEGACY]),
|
||||
nla_len(tb[NL80211_TXRATE_LEGACY]));
|
||||
if (mask.control[band].legacy == 0) {
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
|
||||
|
||||
unlock:
|
||||
dev_put(dev);
|
||||
cfg80211_unlock_rdev(rdev);
|
||||
unlock_rtnl:
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct genl_ops nl80211_ops[] = {
|
||||
{
|
||||
.cmd = NL80211_CMD_GET_WIPHY,
|
||||
@@ -4697,6 +4821,12 @@ static struct genl_ops nl80211_ops[] = {
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
|
||||
.doit = nl80211_set_tx_bitrate_mask,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
||||
|
@@ -43,6 +43,15 @@
|
||||
#include "regdb.h"
|
||||
#include "nl80211.h"
|
||||
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
#define REG_DBG_PRINT(format, args...) \
|
||||
do { \
|
||||
printk(KERN_DEBUG format , ## args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define REG_DBG_PRINT(args...)
|
||||
#endif
|
||||
|
||||
/* Receipt of information from last regulatory request */
|
||||
static struct regulatory_request *last_request;
|
||||
|
||||
@@ -475,6 +484,205 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
|
||||
#undef ONE_GHZ_IN_KHZ
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a work around for sanity checking ieee80211_channel_to_frequency()'s
|
||||
* work. ieee80211_channel_to_frequency() can for example currently provide a
|
||||
* 2 GHz channel when in fact a 5 GHz channel was desired. An example would be
|
||||
* an AP providing channel 8 on a country IE triplet when it sent this on the
|
||||
* 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz
|
||||
* channel.
|
||||
*
|
||||
* This can be removed once ieee80211_channel_to_frequency() takes in a band.
|
||||
*/
|
||||
static bool chan_in_band(int chan, enum ieee80211_band band)
|
||||
{
|
||||
int center_freq = ieee80211_channel_to_frequency(chan);
|
||||
|
||||
switch (band) {
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
if (center_freq <= 2484)
|
||||
return true;
|
||||
return false;
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
if (center_freq >= 5005)
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some APs may send a country IE triplet for each channel they
|
||||
* support and while this is completely overkill and silly we still
|
||||
* need to support it. We avoid making a single rule for each channel
|
||||
* though and to help us with this we use this helper to find the
|
||||
* actual subband end channel. These type of country IE triplet
|
||||
* scenerios are handled then, all yielding two regulaotry rules from
|
||||
* parsing a country IE:
|
||||
*
|
||||
* [1]
|
||||
* [2]
|
||||
* [36]
|
||||
* [40]
|
||||
*
|
||||
* [1]
|
||||
* [2-4]
|
||||
* [5-12]
|
||||
* [36]
|
||||
* [40-44]
|
||||
*
|
||||
* [1-4]
|
||||
* [5-7]
|
||||
* [36-44]
|
||||
* [48-64]
|
||||
*
|
||||
* [36-36]
|
||||
* [40-40]
|
||||
* [44-44]
|
||||
* [48-48]
|
||||
* [52-52]
|
||||
* [56-56]
|
||||
* [60-60]
|
||||
* [64-64]
|
||||
* [100-100]
|
||||
* [104-104]
|
||||
* [108-108]
|
||||
* [112-112]
|
||||
* [116-116]
|
||||
* [120-120]
|
||||
* [124-124]
|
||||
* [128-128]
|
||||
* [132-132]
|
||||
* [136-136]
|
||||
* [140-140]
|
||||
*
|
||||
* Returns 0 if the IE has been found to be invalid in the middle
|
||||
* somewhere.
|
||||
*/
|
||||
static int max_subband_chan(enum ieee80211_band band,
|
||||
int orig_cur_chan,
|
||||
int orig_end_channel,
|
||||
s8 orig_max_power,
|
||||
u8 **country_ie,
|
||||
u8 *country_ie_len)
|
||||
{
|
||||
u8 *triplets_start = *country_ie;
|
||||
u8 len_at_triplet = *country_ie_len;
|
||||
int end_subband_chan = orig_end_channel;
|
||||
|
||||
/*
|
||||
* We'll deal with padding for the caller unless
|
||||
* its not immediate and we don't process any channels
|
||||
*/
|
||||
if (*country_ie_len == 1) {
|
||||
*country_ie += 1;
|
||||
*country_ie_len -= 1;
|
||||
return orig_end_channel;
|
||||
}
|
||||
|
||||
/* Move to the next triplet and then start search */
|
||||
*country_ie += 3;
|
||||
*country_ie_len -= 3;
|
||||
|
||||
if (!chan_in_band(orig_cur_chan, band))
|
||||
return 0;
|
||||
|
||||
while (*country_ie_len >= 3) {
|
||||
int end_channel = 0;
|
||||
struct ieee80211_country_ie_triplet *triplet =
|
||||
(struct ieee80211_country_ie_triplet *) *country_ie;
|
||||
int cur_channel = 0, next_expected_chan;
|
||||
|
||||
/* means last triplet is completely unrelated to this one */
|
||||
if (triplet->ext.reg_extension_id >=
|
||||
IEEE80211_COUNTRY_EXTENSION_ID) {
|
||||
*country_ie -= 3;
|
||||
*country_ie_len += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (triplet->chans.first_channel == 0) {
|
||||
*country_ie += 1;
|
||||
*country_ie_len -= 1;
|
||||
if (*country_ie_len != 0)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (triplet->chans.num_channels == 0)
|
||||
return 0;
|
||||
|
||||
/* Monitonically increasing channel order */
|
||||
if (triplet->chans.first_channel <= end_subband_chan)
|
||||
return 0;
|
||||
|
||||
if (!chan_in_band(triplet->chans.first_channel, band))
|
||||
return 0;
|
||||
|
||||
/* 2 GHz */
|
||||
if (triplet->chans.first_channel <= 14) {
|
||||
end_channel = triplet->chans.first_channel +
|
||||
triplet->chans.num_channels - 1;
|
||||
}
|
||||
else {
|
||||
end_channel = triplet->chans.first_channel +
|
||||
(4 * (triplet->chans.num_channels - 1));
|
||||
}
|
||||
|
||||
if (!chan_in_band(end_channel, band))
|
||||
return 0;
|
||||
|
||||
if (orig_max_power != triplet->chans.max_power) {
|
||||
*country_ie -= 3;
|
||||
*country_ie_len += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
cur_channel = triplet->chans.first_channel;
|
||||
|
||||
/* The key is finding the right next expected channel */
|
||||
if (band == IEEE80211_BAND_2GHZ)
|
||||
next_expected_chan = end_subband_chan + 1;
|
||||
else
|
||||
next_expected_chan = end_subband_chan + 4;
|
||||
|
||||
if (cur_channel != next_expected_chan) {
|
||||
*country_ie -= 3;
|
||||
*country_ie_len += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
end_subband_chan = end_channel;
|
||||
|
||||
/* Move to the next one */
|
||||
*country_ie += 3;
|
||||
*country_ie_len -= 3;
|
||||
|
||||
/*
|
||||
* Padding needs to be dealt with if we processed
|
||||
* some channels.
|
||||
*/
|
||||
if (*country_ie_len == 1) {
|
||||
*country_ie += 1;
|
||||
*country_ie_len -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If seen, the IE is invalid */
|
||||
if (*country_ie_len == 2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (end_subband_chan == orig_end_channel) {
|
||||
*country_ie = triplets_start;
|
||||
*country_ie_len = len_at_triplet;
|
||||
return orig_end_channel;
|
||||
}
|
||||
|
||||
return end_subband_chan;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a country IE to a regulatory domain. A regulatory domain
|
||||
* structure has a lot of information which the IE doesn't yet have,
|
||||
@@ -482,6 +690,7 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
|
||||
* with our userspace regulatory agent to get lower bounds.
|
||||
*/
|
||||
static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
enum ieee80211_band band,
|
||||
u8 *country_ie,
|
||||
u8 country_ie_len,
|
||||
u32 *checksum)
|
||||
@@ -543,10 +752,29 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* APs can add padding to make length divisible
|
||||
* by two, required by the spec.
|
||||
*/
|
||||
if (triplet->chans.first_channel == 0) {
|
||||
country_ie++;
|
||||
country_ie_len--;
|
||||
/* This is expected to be at the very end only */
|
||||
if (country_ie_len != 0)
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (triplet->chans.num_channels == 0)
|
||||
return NULL;
|
||||
|
||||
if (!chan_in_band(triplet->chans.first_channel, band))
|
||||
return NULL;
|
||||
|
||||
/* 2 GHz */
|
||||
if (triplet->chans.first_channel <= 14)
|
||||
if (band == IEEE80211_BAND_2GHZ)
|
||||
end_channel = triplet->chans.first_channel +
|
||||
triplet->chans.num_channels;
|
||||
triplet->chans.num_channels - 1;
|
||||
else
|
||||
/*
|
||||
* 5 GHz -- For example in country IEs if the first
|
||||
@@ -561,6 +789,24 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
(4 * (triplet->chans.num_channels - 1));
|
||||
|
||||
cur_channel = triplet->chans.first_channel;
|
||||
|
||||
/*
|
||||
* Enhancement for APs that send a triplet for every channel
|
||||
* or for whatever reason sends triplets with multiple channels
|
||||
* separated when in fact they should be together.
|
||||
*/
|
||||
end_channel = max_subband_chan(band,
|
||||
cur_channel,
|
||||
end_channel,
|
||||
triplet->chans.max_power,
|
||||
&country_ie,
|
||||
&country_ie_len);
|
||||
if (!end_channel)
|
||||
return NULL;
|
||||
|
||||
if (!chan_in_band(end_channel, band))
|
||||
return NULL;
|
||||
|
||||
cur_sub_max_channel = end_channel;
|
||||
|
||||
/* Basic sanity check */
|
||||
@@ -591,10 +837,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
|
||||
last_sub_max_channel = cur_sub_max_channel;
|
||||
|
||||
country_ie += 3;
|
||||
country_ie_len -= 3;
|
||||
num_rules++;
|
||||
|
||||
if (country_ie_len >= 3) {
|
||||
country_ie += 3;
|
||||
country_ie_len -= 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: this is not a IEEE requirement but
|
||||
* simply a memory requirement
|
||||
@@ -637,6 +886,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (triplet->chans.first_channel == 0) {
|
||||
country_ie++;
|
||||
country_ie_len--;
|
||||
break;
|
||||
}
|
||||
|
||||
reg_rule = &rd->reg_rules[i];
|
||||
freq_range = ®_rule->freq_range;
|
||||
power_rule = ®_rule->power_rule;
|
||||
@@ -644,13 +899,20 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
reg_rule->flags = flags;
|
||||
|
||||
/* 2 GHz */
|
||||
if (triplet->chans.first_channel <= 14)
|
||||
if (band == IEEE80211_BAND_2GHZ)
|
||||
end_channel = triplet->chans.first_channel +
|
||||
triplet->chans.num_channels;
|
||||
triplet->chans.num_channels -1;
|
||||
else
|
||||
end_channel = triplet->chans.first_channel +
|
||||
(4 * (triplet->chans.num_channels - 1));
|
||||
|
||||
end_channel = max_subband_chan(band,
|
||||
triplet->chans.first_channel,
|
||||
end_channel,
|
||||
triplet->chans.max_power,
|
||||
&country_ie,
|
||||
&country_ie_len);
|
||||
|
||||
/*
|
||||
* The +10 is since the regulatory domain expects
|
||||
* the actual band edge, not the center of freq for
|
||||
@@ -671,12 +933,15 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
||||
*/
|
||||
freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
|
||||
power_rule->max_antenna_gain = DBI_TO_MBI(100);
|
||||
power_rule->max_eirp = DBM_TO_MBM(100);
|
||||
power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power);
|
||||
|
||||
country_ie += 3;
|
||||
country_ie_len -= 3;
|
||||
i++;
|
||||
|
||||
if (country_ie_len >= 3) {
|
||||
country_ie += 3;
|
||||
country_ie_len -= 3;
|
||||
}
|
||||
|
||||
BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
|
||||
}
|
||||
|
||||
@@ -972,25 +1237,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
|
||||
if (r == -ERANGE &&
|
||||
last_request->initiator ==
|
||||
NL80211_REGDOM_SET_BY_COUNTRY_IE) {
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz "
|
||||
REG_DBG_PRINT("cfg80211: Leaving channel %d MHz "
|
||||
"intact on %s - no rule found in band on "
|
||||
"Country IE\n",
|
||||
chan->center_freq, wiphy_name(wiphy));
|
||||
#endif
|
||||
chan->center_freq, wiphy_name(wiphy));
|
||||
} else {
|
||||
/*
|
||||
* In this case we know the country IE has at least one reg rule
|
||||
* for the band so we respect its band definitions
|
||||
*/
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
if (last_request->initiator ==
|
||||
NL80211_REGDOM_SET_BY_COUNTRY_IE)
|
||||
printk(KERN_DEBUG "cfg80211: Disabling "
|
||||
REG_DBG_PRINT("cfg80211: Disabling "
|
||||
"channel %d MHz on %s due to "
|
||||
"Country IE\n",
|
||||
chan->center_freq, wiphy_name(wiphy));
|
||||
#endif
|
||||
flags |= IEEE80211_CHAN_DISABLED;
|
||||
chan->flags = flags;
|
||||
}
|
||||
@@ -1685,7 +1946,7 @@ int regulatory_hint_user(const char *alpha2)
|
||||
request->wiphy_idx = WIPHY_IDX_STALE;
|
||||
request->alpha2[0] = alpha2[0];
|
||||
request->alpha2[1] = alpha2[1];
|
||||
request->initiator = NL80211_REGDOM_SET_BY_USER,
|
||||
request->initiator = NL80211_REGDOM_SET_BY_USER;
|
||||
|
||||
queue_regulatory_request(request);
|
||||
|
||||
@@ -1753,8 +2014,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
|
||||
* therefore cannot iterate over the rdev list here.
|
||||
*/
|
||||
void regulatory_hint_11d(struct wiphy *wiphy,
|
||||
u8 *country_ie,
|
||||
u8 country_ie_len)
|
||||
enum ieee80211_band band,
|
||||
u8 *country_ie,
|
||||
u8 country_ie_len)
|
||||
{
|
||||
struct ieee80211_regdomain *rd = NULL;
|
||||
char alpha2[2];
|
||||
@@ -1800,9 +2062,11 @@ void regulatory_hint_11d(struct wiphy *wiphy,
|
||||
wiphy_idx_valid(last_request->wiphy_idx)))
|
||||
goto out;
|
||||
|
||||
rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
|
||||
if (!rd)
|
||||
rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum);
|
||||
if (!rd) {
|
||||
REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will not happen right now but we leave it here for the
|
||||
@@ -1870,13 +2134,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
||||
if (!reg_beacon)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
printk(KERN_DEBUG "cfg80211: Found new beacon on "
|
||||
"frequency: %d MHz (Ch %d) on %s\n",
|
||||
beacon_chan->center_freq,
|
||||
ieee80211_frequency_to_channel(beacon_chan->center_freq),
|
||||
wiphy_name(wiphy));
|
||||
#endif
|
||||
REG_DBG_PRINT("cfg80211: Found new beacon on "
|
||||
"frequency: %d MHz (Ch %d) on %s\n",
|
||||
beacon_chan->center_freq,
|
||||
ieee80211_frequency_to_channel(beacon_chan->center_freq),
|
||||
wiphy_name(wiphy));
|
||||
|
||||
memcpy(®_beacon->chan, beacon_chan,
|
||||
sizeof(struct ieee80211_channel));
|
||||
|
||||
|
@@ -41,14 +41,25 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
||||
* regulatory_hint_11d - hints a country IE as a regulatory domain
|
||||
* @wiphy: the wireless device giving the hint (used only for reporting
|
||||
* conflicts)
|
||||
* @band: the band on which the country IE was received on. This determines
|
||||
* the band we'll process the country IE channel triplets for.
|
||||
* @country_ie: pointer to the country IE
|
||||
* @country_ie_len: length of the country IE
|
||||
*
|
||||
* We will intersect the rd with the what CRDA tells us should apply
|
||||
* for the alpha2 this country IE belongs to, this prevents APs from
|
||||
* sending us incorrect or outdated information against a country.
|
||||
*
|
||||
* The AP is expected to provide Country IE channel triplets for the
|
||||
* band it is on. It is technically possible for APs to send channel
|
||||
* country IE triplets even for channels outside of the band they are
|
||||
* in but for that they would have to use the regulatory extension
|
||||
* in combination with a triplet but this behaviour is currently
|
||||
* not observed. For this reason if a triplet is seen with channel
|
||||
* information for a band the BSS is not present in it will be ignored.
|
||||
*/
|
||||
void regulatory_hint_11d(struct wiphy *wiphy,
|
||||
enum ieee80211_band band,
|
||||
u8 *country_ie,
|
||||
u8 country_ie_len);
|
||||
|
||||
|
@@ -100,8 +100,10 @@ static void bss_release(struct kref *ref)
|
||||
if (bss->pub.free_priv)
|
||||
bss->pub.free_priv(&bss->pub);
|
||||
|
||||
if (bss->ies_allocated)
|
||||
kfree(bss->pub.information_elements);
|
||||
if (bss->beacon_ies_allocated)
|
||||
kfree(bss->pub.beacon_ies);
|
||||
if (bss->proberesp_ies_allocated)
|
||||
kfree(bss->pub.proberesp_ies);
|
||||
|
||||
BUG_ON(atomic_read(&bss->hold));
|
||||
|
||||
@@ -375,8 +377,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
|
||||
|
||||
static struct cfg80211_internal_bss *
|
||||
cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *res,
|
||||
bool overwrite)
|
||||
struct cfg80211_internal_bss *res)
|
||||
{
|
||||
struct cfg80211_internal_bss *found = NULL;
|
||||
const u8 *meshid, *meshcfg;
|
||||
@@ -418,28 +419,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
||||
found->pub.capability = res->pub.capability;
|
||||
found->ts = res->ts;
|
||||
|
||||
/* overwrite IEs */
|
||||
if (overwrite) {
|
||||
/* Update IEs */
|
||||
if (res->pub.proberesp_ies) {
|
||||
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
|
||||
size_t ielen = res->pub.len_information_elements;
|
||||
size_t ielen = res->pub.len_proberesp_ies;
|
||||
|
||||
if (!found->ies_allocated && ksize(found) >= used + ielen) {
|
||||
memcpy(found->pub.information_elements,
|
||||
res->pub.information_elements, ielen);
|
||||
found->pub.len_information_elements = ielen;
|
||||
if (found->pub.proberesp_ies &&
|
||||
!found->proberesp_ies_allocated &&
|
||||
ksize(found) >= used + ielen) {
|
||||
memcpy(found->pub.proberesp_ies,
|
||||
res->pub.proberesp_ies, ielen);
|
||||
found->pub.len_proberesp_ies = ielen;
|
||||
} else {
|
||||
u8 *ies = found->pub.information_elements;
|
||||
u8 *ies = found->pub.proberesp_ies;
|
||||
|
||||
if (found->ies_allocated)
|
||||
if (found->proberesp_ies_allocated)
|
||||
ies = krealloc(ies, ielen, GFP_ATOMIC);
|
||||
else
|
||||
ies = kmalloc(ielen, GFP_ATOMIC);
|
||||
|
||||
if (ies) {
|
||||
memcpy(ies, res->pub.information_elements, ielen);
|
||||
found->ies_allocated = true;
|
||||
found->pub.information_elements = ies;
|
||||
found->pub.len_information_elements = ielen;
|
||||
memcpy(ies, res->pub.proberesp_ies,
|
||||
ielen);
|
||||
found->proberesp_ies_allocated = true;
|
||||
found->pub.proberesp_ies = ies;
|
||||
found->pub.len_proberesp_ies = ielen;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override possible earlier Beacon frame IEs */
|
||||
found->pub.information_elements =
|
||||
found->pub.proberesp_ies;
|
||||
found->pub.len_information_elements =
|
||||
found->pub.len_proberesp_ies;
|
||||
}
|
||||
if (res->pub.beacon_ies) {
|
||||
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
|
||||
size_t ielen = res->pub.len_beacon_ies;
|
||||
|
||||
if (found->pub.beacon_ies &&
|
||||
!found->beacon_ies_allocated &&
|
||||
ksize(found) >= used + ielen) {
|
||||
memcpy(found->pub.beacon_ies,
|
||||
res->pub.beacon_ies, ielen);
|
||||
found->pub.len_beacon_ies = ielen;
|
||||
} else {
|
||||
u8 *ies = found->pub.beacon_ies;
|
||||
|
||||
if (found->beacon_ies_allocated)
|
||||
ies = krealloc(ies, ielen, GFP_ATOMIC);
|
||||
else
|
||||
ies = kmalloc(ielen, GFP_ATOMIC);
|
||||
|
||||
if (ies) {
|
||||
memcpy(ies, res->pub.beacon_ies,
|
||||
ielen);
|
||||
found->beacon_ies_allocated = true;
|
||||
found->pub.beacon_ies = ies;
|
||||
found->pub.len_beacon_ies = ielen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,14 +526,26 @@ cfg80211_inform_bss(struct wiphy *wiphy,
|
||||
res->pub.tsf = timestamp;
|
||||
res->pub.beacon_interval = beacon_interval;
|
||||
res->pub.capability = capability;
|
||||
/* point to after the private area */
|
||||
res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.information_elements, ie, ielen);
|
||||
res->pub.len_information_elements = ielen;
|
||||
/*
|
||||
* Since we do not know here whether the IEs are from a Beacon or Probe
|
||||
* Response frame, we need to pick one of the options and only use it
|
||||
* with the driver that does not provide the full Beacon/Probe Response
|
||||
* frame. Use Beacon frame pointer to avoid indicating that this should
|
||||
* override the information_elements pointer should we have received an
|
||||
* earlier indication of Probe Response data.
|
||||
*
|
||||
* The initial buffer for the IEs is allocated with the BSS entry and
|
||||
* is located after the private area.
|
||||
*/
|
||||
res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.beacon_ies, ie, ielen);
|
||||
res->pub.len_beacon_ies = ielen;
|
||||
res->pub.information_elements = res->pub.beacon_ies;
|
||||
res->pub.len_information_elements = res->pub.len_beacon_ies;
|
||||
|
||||
kref_init(&res->ref);
|
||||
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0);
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
@@ -517,7 +566,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
||||
struct cfg80211_internal_bss *res;
|
||||
size_t ielen = len - offsetof(struct ieee80211_mgmt,
|
||||
u.probe_resp.variable);
|
||||
bool overwrite;
|
||||
size_t privsz = wiphy->bss_priv_size;
|
||||
|
||||
if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
|
||||
@@ -538,16 +586,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
||||
res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
|
||||
res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
|
||||
res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
|
||||
/* point to after the private area */
|
||||
res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
|
||||
res->pub.len_information_elements = ielen;
|
||||
/*
|
||||
* The initial buffer for the IEs is allocated with the BSS entry and
|
||||
* is located after the private area.
|
||||
*/
|
||||
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
|
||||
res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
|
||||
ielen);
|
||||
res->pub.len_proberesp_ies = ielen;
|
||||
res->pub.information_elements = res->pub.proberesp_ies;
|
||||
res->pub.len_information_elements = res->pub.len_proberesp_ies;
|
||||
} else {
|
||||
res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
|
||||
res->pub.len_beacon_ies = ielen;
|
||||
res->pub.information_elements = res->pub.beacon_ies;
|
||||
res->pub.len_information_elements = res->pub.len_beacon_ies;
|
||||
}
|
||||
|
||||
kref_init(&res->ref);
|
||||
|
||||
overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
|
||||
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
|
@@ -454,6 +454,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
||||
* - and country_ie[1] which is the IE length
|
||||
*/
|
||||
regulatory_hint_11d(wdev->wiphy,
|
||||
bss->channel->band,
|
||||
country_ie + 2,
|
||||
country_ie[1]);
|
||||
}
|
||||
|
@@ -1204,21 +1204,47 @@ int cfg80211_wext_siwrate(struct net_device *dev,
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
|
||||
struct cfg80211_bitrate_mask mask;
|
||||
u32 fixed, maxrate;
|
||||
struct ieee80211_supported_band *sband;
|
||||
int band, ridx;
|
||||
bool match = false;
|
||||
|
||||
if (!rdev->ops->set_bitrate_mask)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mask.fixed = 0;
|
||||
mask.maxrate = 0;
|
||||
memset(&mask, 0, sizeof(mask));
|
||||
fixed = 0;
|
||||
maxrate = 0;
|
||||
|
||||
if (rate->value < 0) {
|
||||
/* nothing */
|
||||
} else if (rate->fixed) {
|
||||
mask.fixed = rate->value / 1000; /* kbps */
|
||||
fixed = rate->value / 100000;
|
||||
} else {
|
||||
mask.maxrate = rate->value / 1000; /* kbps */
|
||||
maxrate = rate->value / 100000;
|
||||
}
|
||||
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
sband = wdev->wiphy->bands[band];
|
||||
if (sband == NULL)
|
||||
continue;
|
||||
for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
|
||||
struct ieee80211_rate *srate = &sband->bitrates[ridx];
|
||||
if (fixed == srate->bitrate) {
|
||||
mask.control[band].legacy = 1 << ridx;
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
if (srate->bitrate <= maxrate) {
|
||||
mask.control[band].legacy |= 1 << ridx;
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate);
|
||||
|
Reference in New Issue
Block a user