summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/chan.c27
-rw-r--r--net/mac80211/debugfs.c81
-rw-r--r--net/mac80211/debugfs_netdev.c71
-rw-r--r--net/mac80211/driver-ops.h35
-rw-r--r--net/mac80211/driver-trace.h45
-rw-r--r--net/mac80211/ieee80211_i.h34
-rw-r--r--net/mac80211/main.c2
-rw-r--r--net/mac80211/mlme.c382
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c16
-rw-r--r--net/mac80211/rx.c21
-rw-r--r--net/mac80211/tx.c10
-rw-r--r--net/mac80211/wep.c21
-rw-r--r--net/mac80211/wep.h1
-rw-r--r--net/mac80211/wpa.c22
-rw-r--r--net/wireless/nl80211.c8
-rw-r--r--net/wireless/scan.c7
-rw-r--r--net/wireless/wext-sme.c3
17 files changed, 355 insertions, 431 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index d1f7abddb182..e00ce8c3e28e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -3,6 +3,7 @@
*/
#include <linux/nl80211.h>
+#include <net/cfg80211.h>
#include "ieee80211_i.h"
static enum ieee80211_chan_mode
@@ -134,3 +135,29 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
return result;
}
+
+/*
+ * ieee80211_get_tx_channel_type returns the channel type we should
+ * use for packet transmission, given the channel capability and
+ * whatever regulatory flags we have been given.
+ */
+enum nl80211_channel_type ieee80211_get_tx_channel_type(
+ struct ieee80211_local *local,
+ enum nl80211_channel_type channel_type)
+{
+ switch (channel_type) {
+ case NL80211_CHAN_HT40PLUS:
+ if (local->hw.conf.channel->flags &
+ IEEE80211_CHAN_NO_HT40PLUS)
+ return NL80211_CHAN_HT20;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ if (local->hw.conf.channel->flags &
+ IEEE80211_CHAN_NO_HT40MINUS)
+ return NL80211_CHAN_HT20;
+ break;
+ default:
+ break;
+ }
+ return channel_type;
+}
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 483e96ed95c1..cc5b7a6e7e0b 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -97,85 +97,6 @@ static const struct file_operations reset_ops = {
.llseek = noop_llseek,
};
-static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n",
- local->uapsd_queues);
-}
-
-static ssize_t uapsd_queues_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- u8 val;
- int ret;
-
- ret = kstrtou8_from_user(user_buf, count, 0, &val);
- if (ret)
- return ret;
-
- if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
- return -ERANGE;
-
- local->uapsd_queues = val;
-
- return count;
-}
-
-static const struct file_operations uapsd_queues_ops = {
- .read = uapsd_queues_read,
- .write = uapsd_queues_write,
- .open = mac80211_open_file_generic,
- .llseek = default_llseek,
-};
-
-static ssize_t uapsd_max_sp_len_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
-
- return mac80211_format_buffer(user_buf, count, ppos, "0x%x\n",
- local->uapsd_max_sp_len);
-}
-
-static ssize_t uapsd_max_sp_len_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ieee80211_local *local = file->private_data;
- unsigned long val;
- char buf[10];
- size_t len;
- int ret;
-
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
-
- ret = kstrtoul(buf, 0, &val);
-
- if (ret)
- return -EINVAL;
-
- if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
- return -ERANGE;
-
- local->uapsd_max_sp_len = val;
-
- return count;
-}
-
-static const struct file_operations uapsd_max_sp_len_ops = {
- .read = uapsd_max_sp_len_read,
- .write = uapsd_max_sp_len_write,
- .open = mac80211_open_file_generic,
- .llseek = default_llseek,
-};
-
static ssize_t channel_type_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -362,8 +283,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(wep_iv);
DEBUGFS_ADD(queues);
DEBUGFS_ADD_MODE(reset, 0200);
- DEBUGFS_ADD(uapsd_queues);
- DEBUGFS_ADD(uapsd_max_sp_len);
DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(hwflags);
DEBUGFS_ADD(user_power);
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index f6de8a65f402..a32eeda04aa3 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -49,16 +49,15 @@ static ssize_t ieee80211_if_write(
size_t count, loff_t *ppos,
ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int))
{
- u8 *buf;
+ char buf[64];
ssize_t ret;
- buf = kmalloc(count, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ if (count >= sizeof(buf))
+ return -E2BIG;
- ret = -EFAULT;
if (copy_from_user(buf, userbuf, count))
- goto freebuf;
+ return -EFAULT;
+ buf[count] = '\0';
ret = -ENODEV;
rtnl_lock();
@@ -66,8 +65,6 @@ static ssize_t ieee80211_if_write(
ret = (*write)(sdata, buf, count);
rtnl_unlock();
-freebuf:
- kfree(buf);
return ret;
}
@@ -340,6 +337,62 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
__IEEE80211_IF_FILE_W(tkip_mic_test);
+static ssize_t ieee80211_if_fmt_uapsd_queues(
+ const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
+{
+ const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_queues);
+}
+
+static ssize_t ieee80211_if_parse_uapsd_queues(
+ struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u8 val;
+ int ret;
+
+ ret = kstrtou8(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+ return -ERANGE;
+
+ ifmgd->uapsd_queues = val;
+
+ return buflen;
+}
+__IEEE80211_IF_FILE_W(uapsd_queues);
+
+static ssize_t ieee80211_if_fmt_uapsd_max_sp_len(
+ const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
+{
+ const struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ return snprintf(buf, buflen, "0x%x\n", ifmgd->uapsd_max_sp_len);
+}
+
+static ssize_t ieee80211_if_parse_uapsd_max_sp_len(
+ struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return -EINVAL;
+
+ if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+ return -ERANGE;
+
+ ifmgd->uapsd_max_sp_len = val;
+
+ return buflen;
+}
+__IEEE80211_IF_FILE_W(uapsd_max_sp_len);
+
/* AP attributes */
IEEE80211_IF_FILE(num_sta_authorized, u.ap.num_sta_authorized, ATOMIC);
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
@@ -472,6 +525,8 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(ave_beacon);
DEBUGFS_ADD_MODE(smps, 0600);
DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
+ DEBUGFS_ADD_MODE(uapsd_queues, 0600);
+ DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
}
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 70dfb6415c20..af4691fed645 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -168,41 +168,6 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
trace_drv_return_void(local);
}
-static inline int drv_tx_sync(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- const u8 *bssid,
- enum ieee80211_tx_sync_type type)
-{
- int ret = 0;
-
- might_sleep();
-
- check_sdata_in_driver(sdata);
-
- trace_drv_tx_sync(local, sdata, bssid, type);
- if (local->ops->tx_sync)
- ret = local->ops->tx_sync(&local->hw, &sdata->vif,
- bssid, type);
- trace_drv_return_int(local, ret);
- return ret;
-}
-
-static inline void drv_finish_tx_sync(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- const u8 *bssid,
- enum ieee80211_tx_sync_type type)
-{
- might_sleep();
-
- check_sdata_in_driver(sdata);
-
- trace_drv_finish_tx_sync(local, sdata, bssid, type);
- if (local->ops->finish_tx_sync)
- local->ops->finish_tx_sync(&local->hw, &sdata->vif,
- bssid, type);
- trace_drv_return_void(local);
-}
-
static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
struct netdev_hw_addr_list *mc_list)
{
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 384e2f08c187..21d6f5290a1c 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -296,7 +296,7 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->dtimper = info->dtim_period;
__entry->bcnint = info->beacon_int;
__entry->assoc_cap = info->assoc_capability;
- __entry->timestamp = info->timestamp;
+ __entry->timestamp = info->last_tsf;
__entry->basic_rates = info->basic_rates;
__entry->enable_beacon = info->enable_beacon;
__entry->ht_operation_mode = info->ht_operation_mode;
@@ -308,49 +308,6 @@ TRACE_EVENT(drv_bss_info_changed,
)
);
-DECLARE_EVENT_CLASS(tx_sync_evt,
- TP_PROTO(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- const u8 *bssid,
- enum ieee80211_tx_sync_type type),
- TP_ARGS(local, sdata, bssid, type),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- VIF_ENTRY
- __array(char, bssid, ETH_ALEN)
- __field(u32, sync_type)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- VIF_ASSIGN;
- memcpy(__entry->bssid, bssid, ETH_ALEN);
- __entry->sync_type = type;
- ),
-
- TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " bssid:%pM type:%d",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->bssid, __entry->sync_type
- )
-);
-
-DEFINE_EVENT(tx_sync_evt, drv_tx_sync,
- TP_PROTO(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- const u8 *bssid,
- enum ieee80211_tx_sync_type type),
- TP_ARGS(local, sdata, bssid, type)
-);
-
-DEFINE_EVENT(tx_sync_evt, drv_finish_tx_sync,
- TP_PROTO(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- const u8 *bssid,
- enum ieee80211_tx_sync_type type),
- TP_ARGS(local, sdata, bssid, type)
-);
-
TRACE_EVENT(drv_prepare_multicast,
TP_PROTO(struct ieee80211_local *local, int mc_count),
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 796b13bfc953..d9798a307f20 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -388,7 +388,6 @@ struct ieee80211_mgd_auth_data {
u8 key[WLAN_KEY_LEN_WEP104];
u8 key_len, key_idx;
- bool synced;
bool done;
size_t ie_len;
@@ -408,7 +407,7 @@ struct ieee80211_mgd_assoc_data {
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len;
u8 supp_rates_len;
- bool wmm_used, uapsd_used;
+ bool wmm, uapsd;
bool have_beacon;
bool sent_assoc;
bool synced;
@@ -460,6 +459,20 @@ struct ieee80211_if_managed {
IEEE80211_MFP_REQUIRED
} mfp; /* management frame protection */
+ /*
+ * Bitmask of enabled u-apsd queues,
+ * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association
+ * to take effect.
+ */
+ unsigned int uapsd_queues;
+
+ /*
+ * Maximum number of buffered frames AP can deliver during a
+ * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar.
+ * Needs a new association to take effect.
+ */
+ unsigned int uapsd_max_sp_len;
+
int wmm_last_param_set;
u8 use_4addr;
@@ -1018,20 +1031,6 @@ struct ieee80211_local {
*/
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
- /*
- * Bitmask of enabled u-apsd queues,
- * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association
- * to take effect.
- */
- unsigned int uapsd_queues;
-
- /*
- * Maximum number of buffered frames AP can deliver during a
- * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar.
- * Needs a new association to take effect.
- */
- unsigned int uapsd_max_sp_len;
-
bool pspolling;
bool offchannel_ps_enabled;
/*
@@ -1503,6 +1502,9 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
enum nl80211_channel_type chantype);
enum nl80211_channel_type
ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info);
+enum nl80211_channel_type ieee80211_get_tx_channel_type(
+ struct ieee80211_local *local,
+ enum nl80211_channel_type channel_type);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 36fa8051296c..b581a24fa15c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -595,8 +595,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
local->user_power_level = -1;
- local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
- local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c08924aeac00..576fb25456dd 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -189,40 +189,35 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
u16 ht_opmode;
bool enable_ht = true;
enum nl80211_channel_type prev_chantype;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT;
+ enum nl80211_channel_type tx_channel_type;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
prev_chantype = sdata->vif.bss_conf.channel_type;
- /* HT is not supported */
- if (!sband->ht_cap.ht_supported)
- enable_ht = false;
- if (enable_ht) {
- hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan,
- sband->band);
- /* check that channel matches the right operating channel */
- if (local->hw.conf.channel->center_freq != hti_cfreq) {
- /* Some APs mess this up, evidently.
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel, for instance.
- */
- printk(KERN_DEBUG
- "%s: Wrong control channel in association"
- " response: configured center-freq: %d"
- " hti-cfreq: %d hti->control_chan: %d"
- " band: %d. Disabling HT.\n",
- sdata->name,
- local->hw.conf.channel->center_freq,
- hti_cfreq, hti->control_chan,
- sband->band);
- enable_ht = false;
- }
+ hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan,
+ sband->band);
+ /* check that channel matches the right operating channel */
+ if (local->hw.conf.channel->center_freq != hti_cfreq) {
+ /* Some APs mess this up, evidently.
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel, for instance.
+ */
+ printk(KERN_DEBUG
+ "%s: Wrong control channel in association"
+ " response: configured center-freq: %d"
+ " hti-cfreq: %d hti->control_chan: %d"
+ " band: %d. Disabling HT.\n",
+ sdata->name,
+ local->hw.conf.channel->center_freq,
+ hti_cfreq, hti->control_chan,
+ sband->band);
+ enable_ht = false;
}
if (enable_ht) {
- channel_type = NL80211_CHAN_HT20;
+ rx_channel_type = NL80211_CHAN_HT20;
if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
!ieee80111_cfg_override_disables_ht40(sdata) &&
@@ -230,29 +225,28 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
(hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (!(local->hw.conf.channel->flags &
- IEEE80211_CHAN_NO_HT40PLUS))
- channel_type = NL80211_CHAN_HT40PLUS;
+ rx_channel_type = NL80211_CHAN_HT40PLUS;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (!(local->hw.conf.channel->flags &
- IEEE80211_CHAN_NO_HT40MINUS))
- channel_type = NL80211_CHAN_HT40MINUS;
+ rx_channel_type = NL80211_CHAN_HT40MINUS;
break;
}
}
}
+ tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type);
+
if (local->tmp_channel)
- local->tmp_channel_type = channel_type;
+ local->tmp_channel_type = rx_channel_type;
- if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+ if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) {
/* can only fail due to HT40+/- mismatch */
- channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
+ rx_channel_type = NL80211_CHAN_HT20;
+ WARN_ON(!ieee80211_set_channel_type(local, sdata,
+ rx_channel_type));
}
- if (beacon_htcap_ie && (prev_chantype != channel_type)) {
+ if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) {
/*
* Whenever the AP announces the HT mode change that can be
* 40MHz intolerant or etc., it would be safer to stop tx
@@ -270,13 +264,13 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
/* channel_type change automatically detected */
ieee80211_hw_config(local, 0);
- if (prev_chantype != channel_type) {
+ if (prev_chantype != tx_channel_type) {
rcu_read_lock();
sta = sta_info_get(sdata, bssid);
if (sta)
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_HT_CHANGED,
- channel_type);
+ tx_channel_type);
rcu_read_unlock();
if (beacon_htcap_ie)
@@ -289,7 +283,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
/* if bss configuration changed store the new one */
if (sdata->ht_opmode_valid != enable_ht ||
sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
- prev_chantype != channel_type) {
+ prev_chantype != rx_channel_type) {
changed |= BSS_CHANGED_HT;
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
sdata->ht_opmode_valid = enable_ht;
@@ -335,9 +329,6 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
- if (!sband->ht_cap.ht_supported)
- return;
-
if (!ht_info_ie)
return;
@@ -405,7 +396,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
u16 capab;
struct ieee80211_supported_band *sband;
u32 rates = 0;
- struct ieee80211_bss *bss = (void *)assoc_data->bss->priv;
lockdep_assert_held(&ifmgd->mtx);
@@ -566,8 +556,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N) &&
- bss->wmm_used && local->hw.queues >= 4)
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie,
sband, local->oper_channel, ifmgd->ap_smps);
@@ -581,10 +570,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
- if (assoc_data->wmm_used && local->hw.queues >= 4) {
- if (assoc_data->uapsd_used) {
- qos_info = local->uapsd_queues;
- qos_info |= (local->uapsd_max_sp_len <<
+ if (assoc_data->wmm) {
+ if (assoc_data->uapsd) {
+ qos_info = ifmgd->uapsd_queues;
+ qos_info |= (ifmgd->uapsd_max_sp_len <<
IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
} else {
qos_info = 0;
@@ -1203,7 +1192,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
return;
if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
- uapsd_queues = local->uapsd_queues;
+ uapsd_queues = ifmgd->uapsd_queues;
count = wmm_param[6] & 0x0f;
if (count == ifmgd->wmm_last_param_set)
@@ -1329,7 +1318,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_info_changed |= BSS_CHANGED_ASSOC;
/* set timing information */
bss_conf->beacon_int = cbss->beacon_interval;
- bss_conf->timestamp = cbss->tsf;
+ bss_conf->last_tsf = cbss->tsf;
bss_info_changed |= BSS_CHANGED_BEACON_INT;
bss_info_changed |= ieee80211_handle_bss_capability(sdata,
@@ -1355,15 +1344,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_conf->dtim_period = 0;
bss_conf->assoc = 1;
- /*
- * For now just always ask the driver to update the basic rateset
- * when we have associated, we aren't checking whether it actually
- * changed or not.
- */
- bss_info_changed |= BSS_CHANGED_BASIC_RATES;
-
- /* And the BSSID changed - we're associated now */
- bss_info_changed |= BSS_CHANGED_BSSID;
/* Tell the driver to monitor connection quality (if supported) */
if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI &&
@@ -1394,7 +1374,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- u32 changed = 0, config_changed = 0;
+ u32 changed = 0;
u8 bssid[ETH_ALEN];
ASSERT_MGD_MTX(ifmgd);
@@ -1454,9 +1434,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
- /* channel(_type) changes are handled by ieee80211_hw_config */
- WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
-
/* on the next assoc, re-program HT parameters */
sdata->ht_opmode_valid = false;
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
@@ -1469,12 +1446,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
- config_changed |= IEEE80211_CONF_CHANGE_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
local->ps_sdata = NULL;
- ieee80211_hw_config(local, config_changed);
-
/* Disable ARP filtering */
if (sdata->vif.bss_conf.arp_filter_enabled) {
sdata->vif.bss_conf.arp_filter_enabled = false;
@@ -1488,6 +1463,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
+ /* channel(_type) changes are handled by ieee80211_hw_config */
+ WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
+ ieee80211_hw_config(local, 0);
+
/* disassociated - set to defaults now */
ieee80211_set_wmm_default(sdata, false);
@@ -1770,11 +1749,6 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
lockdep_assert_held(&sdata->u.mgd.mtx);
- if (auth_data->synced)
- drv_finish_tx_sync(sdata->local, sdata,
- auth_data->bss->bssid,
- IEEE80211_TX_SYNC_AUTH);
-
if (!assoc) {
sta_info_destroy_addr(sdata, auth_data->bss->bssid);
@@ -1862,10 +1836,6 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
out:
- if (ifmgd->auth_data->synced)
- drv_finish_tx_sync(sdata->local, sdata, bssid,
- IEEE80211_TX_SYNC_AUTH);
- ifmgd->auth_data->synced = false;
ifmgd->auth_data->done = true;
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
run_again(ifmgd, ifmgd->auth_data->timeout);
@@ -2005,11 +1975,6 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
lockdep_assert_held(&sdata->u.mgd.mtx);
- if (assoc_data->synced)
- drv_finish_tx_sync(sdata->local, sdata,
- assoc_data->bss->bssid,
- IEEE80211_TX_SYNC_ASSOC);
-
if (!assoc) {
sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
@@ -2030,15 +1995,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
struct sta_info *sta;
u8 *pos;
- u32 rates, basic_rates;
u16 capab_info, aid;
struct ieee802_11_elems elems;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u32 changed = 0;
int err;
- bool have_higher_than_11mbit = false;
u16 ap_ht_cap_flags;
- int min_rate = INT_MAX, min_rate_index = -1;
/* AssocResp and ReassocResp have identical structure */
@@ -2083,39 +2045,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false;
}
- rates = 0;
- basic_rates = 0;
sband = local->hw.wiphy->bands[local->oper_channel->band];
- ieee80211_get_rates(sband, elems.supp_rates, elems.supp_rates_len,
- &rates, &basic_rates, &have_higher_than_11mbit,
- &min_rate, &min_rate_index);
-
- ieee80211_get_rates(sband, elems.ext_supp_rates,
- elems.ext_supp_rates_len, &rates, &basic_rates,
- &have_higher_than_11mbit,
- &min_rate, &min_rate_index);
-
- /*
- * some buggy APs don't advertise basic_rates. use the lowest
- * supported rate instead.
- */
- if (unlikely(!basic_rates) && min_rate_index >= 0) {
- printk(KERN_DEBUG "%s: No basic rates in AssocResp. "
- "Using min supported rate instead.\n", sdata->name);
- basic_rates = BIT(min_rate_index);
- }
-
- sta->sta.supp_rates[local->oper_channel->band] = rates;
- sdata->vif.bss_conf.basic_rates = basic_rates;
-
- /* cf. IEEE 802.11 9.2.12 */
- if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
- have_higher_than_11mbit)
- sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
- else
- sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
@@ -2162,7 +2093,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_QOS;
if (elems.ht_info_elem && elems.wmm_param &&
- (sdata->local->hw.queues >= 4) &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
cbss->bssid, ap_ht_cap_flags,
@@ -2255,14 +2185,6 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
} else {
printk(KERN_DEBUG "%s: associated\n", sdata->name);
- /* tell driver about sync done first */
- if (assoc_data->synced) {
- drv_finish_tx_sync(sdata->local, sdata,
- assoc_data->bss->bssid,
- IEEE80211_TX_SYNC_ASSOC);
- assoc_data->synced = false;
- }
-
if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
ieee80211_destroy_assoc_data(sdata, true);
@@ -2747,14 +2669,6 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
if (WARN_ON_ONCE(!auth_data))
return -EINVAL;
- if (!auth_data->synced) {
- int ret = drv_tx_sync(local, sdata, auth_data->bss->bssid,
- IEEE80211_TX_SYNC_AUTH);
- if (ret)
- return ret;
- }
- auth_data->synced = true;
-
auth_data->tries++;
if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -2811,14 +2725,6 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
lockdep_assert_held(&sdata->u.mgd.mtx);
- if (!assoc_data->synced) {
- int ret = drv_tx_sync(local, sdata, assoc_data->bss->bssid,
- IEEE80211_TX_SYNC_ASSOC);
- if (ret)
- return ret;
- }
- assoc_data->synced = true;
-
assoc_data->tries++;
if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
printk(KERN_DEBUG "%s: association with %pM timed out\n",
@@ -3107,6 +3013,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd->flags = 0;
ifmgd->powersave = sdata->wdev.ps;
+ ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
+ ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
mutex_init(&ifmgd->mtx);
@@ -3143,6 +3051,101 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
return 0;
}
+static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_bss *cbss, bool assoc)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_bss *bss = (void *)cbss->priv;
+ struct sta_info *sta;
+ bool have_sta = false;
+ int err;
+
+ if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
+ return -EINVAL;
+
+ if (assoc) {
+ rcu_read_lock();
+ have_sta = sta_info_get(sdata, cbss->bssid);
+ rcu_read_unlock();
+ }
+
+ if (!have_sta) {
+ sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
+ if (!sta)
+ return -ENOMEM;
+ }
+
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(sdata->local);
+ mutex_unlock(&local->mtx);
+
+ /* switch to the right channel */
+ local->oper_channel = cbss->channel;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ if (!have_sta) {
+ struct ieee80211_supported_band *sband;
+ u32 rates = 0, basic_rates = 0;
+ bool have_higher_than_11mbit;
+ int min_rate = INT_MAX, min_rate_index = -1;
+
+ sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+
+ ieee80211_get_rates(sband, bss->supp_rates,
+ bss->supp_rates_len,
+ &rates, &basic_rates,
+ &have_higher_than_11mbit,
+ &min_rate, &min_rate_index);
+
+ /*
+ * This used to be a workaround for basic rates missing
+ * in the association response frame. Now that we no
+ * longer use the basic rates from there, it probably
+ * doesn't happen any more, but keep the workaround so
+ * in case some *other* APs are buggy in different ways
+ * we can connect -- with a warning.
+ */
+ if (!basic_rates && min_rate_index >= 0) {
+ printk(KERN_DEBUG
+ "%s: No basic rates, using min rate instead.\n",
+ sdata->name);
+ basic_rates = BIT(min_rate_index);
+ }
+
+ sta->sta.supp_rates[cbss->channel->band] = rates;
+ sdata->vif.bss_conf.basic_rates = basic_rates;
+
+ /* cf. IEEE 802.11 9.2.12 */
+ if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+ have_higher_than_11mbit)
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ else
+ sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
+ memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN);
+
+ /* tell driver about BSSID and basic rates */
+ ieee80211_bss_info_change_notify(sdata,
+ BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES);
+
+ if (assoc)
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+
+ err = sta_info_insert(sta);
+ sta = NULL;
+ if (err) {
+ printk(KERN_DEBUG
+ "%s: failed to insert STA entry for the AP (error %d)\n",
+ sdata->name, err);
+ return err;
+ }
+ } else
+ WARN_ON_ONCE(compare_ether_addr(ifmgd->bssid, cbss->bssid));
+
+ return 0;
+}
+
/* config hooks */
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
struct cfg80211_auth_request *req)
@@ -3150,7 +3153,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_auth_data *auth_data;
- struct sta_info *sta;
u16 auth_alg;
int err;
@@ -3216,38 +3218,12 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: authenticate with %pM\n",
sdata->name, req->bss->bssid);
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&local->mtx);
-
- /* switch to the right channel */
- local->oper_channel = req->bss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
-
- /* set BSSID */
- memcpy(ifmgd->bssid, req->bss->bssid, ETH_ALEN);
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
-
- /* add station entry */
- sta = sta_info_alloc(sdata, req->bss->bssid, GFP_KERNEL);
- if (!sta) {
- err = -ENOMEM;
+ err = ieee80211_prep_connection(sdata, req->bss, false);
+ if (err)
goto err_clear;
- }
-
- err = sta_info_insert(sta);
- if (err) {
- printk(KERN_DEBUG
- "%s: failed to insert STA entry for the AP %pM (error %d)\n",
- sdata->name, req->bss->bssid, err);
- goto err_clear;
- }
err = ieee80211_probe_auth(sdata);
if (err) {
- if (auth_data->synced)
- drv_finish_tx_sync(local, sdata, req->bss->bssid,
- IEEE80211_TX_SYNC_AUTH);
sta_info_destroy_addr(sdata, req->bss->bssid);
goto err_clear;
}
@@ -3274,7 +3250,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss *bss = (void *)req->bss->priv;
struct ieee80211_mgd_assoc_data *assoc_data;
- struct sta_info *sta;
+ struct ieee80211_supported_band *sband;
const u8 *ssidie;
int i, err;
@@ -3316,6 +3292,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->beacon_crc_valid = false;
+ /*
+ * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
+ * We still associate in non-HT mode (11a/b/g) if any one of these
+ * ciphers is configured as pairwise.
+ * We can set this to true for non-11n hardware, that'll be checked
+ * separately along with the peer capabilities.
+ */
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
@@ -3325,6 +3308,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (req->flags & ASSOC_REQ_DISABLE_HT)
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ /* Also disable HT if we don't support it or the AP doesn't use WMM */
+ sband = local->hw.wiphy->bands[req->bss->channel->band];
+ if (!sband->ht_cap.ht_supported ||
+ local->hw.queues < 4 || !bss->wmm_used)
+ ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+
memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
sizeof(ifmgd->ht_capa_mask));
@@ -3344,15 +3333,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
} else
ifmgd->ap_smps = ifmgd->req_smps;
- /*
- * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
- * We still associate in non-HT mode (11a/b/g) if any one of these
- * ciphers is configured as pairwise.
- * We can set this to true for non-11n hardware, that'll be checked
- * separately along with the peer capabilities.
- */
assoc_data->capability = req->bss->capability;
- assoc_data->wmm_used = bss->wmm_used;
+ assoc_data->wmm = bss->wmm_used && (local->hw.queues >= 4);
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
assoc_data->ht_information_ie =
@@ -3360,10 +3342,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
- assoc_data->uapsd_used = true;
+ assoc_data->uapsd = true;
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
} else {
- assoc_data->uapsd_used = false;
+ assoc_data->uapsd = false;
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
}
@@ -3393,41 +3375,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->assoc_data = assoc_data;
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&local->mtx);
-
- /* switch to the right channel */
- local->oper_channel = req->bss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
-
- rcu_read_lock();
- sta = sta_info_get(sdata, req->bss->bssid);
- rcu_read_unlock();
-
- if (!sta) {
- /* set BSSID */
- memcpy(ifmgd->bssid, req->bss->bssid, ETH_ALEN);
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
-
- sta = sta_info_alloc(sdata, req->bss->bssid, GFP_KERNEL);
- if (!sta) {
- err = -ENOMEM;
- goto err_clear;
- }
-
- sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
-
- err = sta_info_insert(sta);
- sta = NULL;
- if (err) {
- printk(KERN_DEBUG
- "%s: failed to insert STA entry for the AP (error %d)\n",
- sdata->name, err);
- goto err_clear;
- }
- } else
- WARN_ON_ONCE(compare_ether_addr(ifmgd->bssid, req->bss->bssid));
+ err = ieee80211_prep_connection(sdata, req->bss, true);
+ if (err)
+ goto err_clear;
if (!bss->dtim_period &&
sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index ff5f7b84e825..16e0b277b9a8 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -568,6 +568,13 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
minstrel_next_sample_idx(mi);
/*
+ * Sampling might add some overhead (RTS, no aggregation)
+ * to the frame. Hence, don't use sampling for the currently
+ * used max TP rate.
+ */
+ if (sample_idx == mi->max_tp_rate)
+ return -1;
+ /*
* When not using MRR, do not sample if the probability is already
* higher than 95% to avoid wasting airtime
*/
@@ -692,6 +699,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
int ack_dur;
int stbc;
int i;
+ unsigned int smps;
/* fall back to the old minstrel for legacy stations */
if (!sta->ht_cap.ht_supported)
@@ -731,6 +739,9 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
oper_chan_type != NL80211_CHAN_HT40PLUS)
sta_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >>
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
+
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
u16 req = 0;
@@ -748,6 +759,11 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
if ((sta_cap & req) != req)
continue;
+ /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
+ if (smps == WLAN_HT_CAP_SM_PS_STATIC &&
+ minstrel_mcs_groups[i].streams > 1)
+ continue;
+
mi->groups[i].supported =
mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5f6e32ca0858..bcfe8c77c839 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1063,20 +1063,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
}
- if (skb_linearize(rx->skb))
- return RX_DROP_UNUSABLE;
- /* the hdr variable is invalid now! */
-
switch (rx->key->conf.cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
- /* Check for weak IVs if possible */
- if (rx->sta && ieee80211_is_data(fc) &&
- (!(status->flag & RX_FLAG_IV_STRIPPED) ||
- !(status->flag & RX_FLAG_DECRYPTED)) &&
- ieee80211_wep_is_weak_iv(rx->skb, rx->key))
- rx->sta->wep_weak_iv_count++;
-
result = ieee80211_crypto_wep_decrypt(rx);
break;
case WLAN_CIPHER_SUITE_TKIP:
@@ -1096,6 +1085,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE;
}
+ /* the hdr variable is invalid after the decrypt handlers */
+
/* either the frame has been decrypted or will be dropped */
status->flag |= RX_FLAG_DECRYPTED;
@@ -2278,9 +2269,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
sband = rx->local->hw.wiphy->bands[status->band];
- rate_control_rate_update(local, sband, rx->sta,
- IEEE80211_RC_SMPS_CHANGED,
- local->_oper_channel_type);
+ rate_control_rate_update(
+ local, sband, rx->sta,
+ IEEE80211_RC_SMPS_CHANGED,
+ ieee80211_get_tx_channel_type(
+ local, local->_oper_channel_type));
goto handled;
}
default:
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 570737df2d22..782a60198df4 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -226,12 +226,12 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
* have correct qos tag for some reason, due the network or the
* peer application.
*
- * Note: local->uapsd_queues access is racy here. If the value is
+ * Note: ifmgd->uapsd_queues access is racy here. If the value is
* changed via debugfs, user needs to reassociate manually to have
* everything in sync.
*/
if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
- && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+ && (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
&& skb_get_queue_mapping(tx->skb) == 0)
return TX_CONTINUE;
@@ -1065,6 +1065,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
{
bool queued = false;
bool reset_agg_timer = false;
+ struct sk_buff *purge_skb = NULL;
if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
info->flags |= IEEE80211_TX_CTL_AMPDU;
@@ -1106,8 +1107,13 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
info->control.vif = &tx->sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
__skb_queue_tail(&tid_tx->pending, skb);
+ if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)
+ purge_skb = __skb_dequeue(&tid_tx->pending);
}
spin_unlock(&tx->sta->lock);
+
+ if (purge_skb)
+ dev_kfree_skb(purge_skb);
}
/* reset session timer */
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 68ad351479df..7aa31bbfaa3b 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -263,16 +263,14 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
}
-bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
+static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb,
+ struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
unsigned int hdrlen;
u8 *ivpos;
u32 iv;
- if (!ieee80211_has_protected(hdr->frame_control))
- return false;
-
hdrlen = ieee80211_hdrlen(hdr->frame_control);
ivpos = skb->data + hdrlen;
iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
@@ -286,18 +284,27 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ __le16 fc = hdr->frame_control;
- if (!ieee80211_is_data(hdr->frame_control) &&
- !ieee80211_is_auth(hdr->frame_control))
+ if (!ieee80211_is_data(fc) && !ieee80211_is_auth(fc))
return RX_CONTINUE;
if (!(status->flag & RX_FLAG_DECRYPTED)) {
+ if (skb_linearize(rx->skb))
+ return RX_DROP_UNUSABLE;
+ if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
+ rx->sta->wep_weak_iv_count++;
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
return RX_DROP_UNUSABLE;
} else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + WEP_IV_LEN))
+ return RX_DROP_UNUSABLE;
+ if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
+ rx->sta->wep_weak_iv_count++;
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
- skb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN);
+ if (pskb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN))
+ return RX_DROP_UNUSABLE;
}
return RX_CONTINUE;
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index 01e54840a628..9615749d1f65 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -25,7 +25,6 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,
const u8 *key, int keylen, int keyidx);
int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
size_t klen, u8 *data, size_t data_len);
-bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index b758350919ff..0ae23c60968c 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -138,6 +138,10 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
if (skb->len < hdrlen + MICHAEL_MIC_LEN)
return RX_DROP_UNUSABLE;
+ if (skb_linearize(rx->skb))
+ return RX_DROP_UNUSABLE;
+ hdr = (void *)skb->data;
+
data = skb->data + hdrlen;
data_len = skb->len - hdrlen - MICHAEL_MIC_LEN;
key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
@@ -253,6 +257,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
if (!rx->sta || skb->len - hdrlen < 12)
return RX_DROP_UNUSABLE;
+ /* it may be possible to optimize this a bit more */
+ if (skb_linearize(rx->skb))
+ return RX_DROP_UNUSABLE;
+ hdr = (void *)skb->data;
+
/*
* Let TKIP code verify IV, but skip decryption.
* In the case where hardware checks the IV as well,
@@ -484,6 +493,14 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
+ if (status->flag & RX_FLAG_DECRYPTED) {
+ if (!pskb_may_pull(rx->skb, hdrlen + CCMP_HDR_LEN))
+ return RX_DROP_UNUSABLE;
+ } else {
+ if (skb_linearize(rx->skb))
+ return RX_DROP_UNUSABLE;
+ }
+
ccmp_hdr2pn(pn, skb->data + hdrlen);
queue = rx->security_idx;
@@ -509,7 +526,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN);
/* Remove CCMP header and MIC */
- skb_trim(skb, skb->len - CCMP_MIC_LEN);
+ if (pskb_trim(skb, skb->len - CCMP_MIC_LEN))
+ return RX_DROP_UNUSABLE;
memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen);
skb_pull(skb, CCMP_HDR_LEN);
@@ -609,6 +627,8 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
if (!ieee80211_is_mgmt(hdr->frame_control))
return RX_CONTINUE;
+ /* management frames are already linear */
+
if (skb->len < 24 + sizeof(*mmie))
return RX_DROP_UNUSABLE;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 39dbdf2adb12..4c1eb9472ddb 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -205,6 +205,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
},
[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
};
/* policy for the key attributes */
@@ -5116,6 +5117,13 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
wiphy = &rdev->wiphy;
+ connect.bg_scan_period = -1;
+ if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
+ (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
+ connect.bg_scan_period =
+ nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
+ }
+
if (info->attrs[NL80211_ATTR_MAC])
connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index afde7e5f0010..70faadf16a32 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -734,9 +734,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_bss*
cfg80211_inform_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
- const u8 *bssid,
- u64 timestamp, u16 capability, u16 beacon_interval,
- const u8 *ie, size_t ielen,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
struct cfg80211_internal_bss *res;
@@ -758,7 +757,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
memcpy(res->pub.bssid, bssid, ETH_ALEN);
res->pub.channel = channel;
res->pub.signal = signal;
- res->pub.tsf = timestamp;
+ res->pub.tsf = tsf;
res->pub.beacon_interval = beacon_interval;
res->pub.capability = capability;
/*
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 326750b99151..7c01c2f3b6cf 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -30,6 +30,9 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
wdev->wext.connect.ie = wdev->wext.ie;
wdev->wext.connect.ie_len = wdev->wext.ie_len;
+ /* Use default background scan period */
+ wdev->wext.connect.bg_scan_period = -1;
+
if (wdev->wext.keys) {
wdev->wext.keys->def = wdev->wext.default_key;
wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;