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:
David S. Miller
2010-01-19 11:43:42 -08:00
92 changed files with 7263 additions and 2760 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 = {

View File

@@ -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 = &reg_rule->freq_range;
power_rule = &reg_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(&reg_beacon->chan, beacon_chan,
sizeof(struct ieee80211_channel));

View File

@@ -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);

View File

@@ -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;

View File

@@ -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]);
}

View File

@@ -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);