summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/agg-rx.c8
-rw-r--r--net/mac80211/agg-tx.c16
-rw-r--r--net/mac80211/cfg.c79
-rw-r--r--net/mac80211/debugfs_sta.c3
-rw-r--r--net/mac80211/ht.c17
-rw-r--r--net/mac80211/ibss.c65
-rw-r--r--net/mac80211/ieee80211_i.h20
-rw-r--r--net/mac80211/iface.c50
-rw-r--r--net/mac80211/key.c95
-rw-r--r--net/mac80211/key.h3
-rw-r--r--net/mac80211/main.c27
-rw-r--r--net/mac80211/mlme.c33
-rw-r--r--net/mac80211/pm.c5
-rw-r--r--net/mac80211/rx.c42
-rw-r--r--net/mac80211/scan.c154
-rw-r--r--net/mac80211/sta_info.c12
-rw-r--r--net/mac80211/sta_info.h8
-rw-r--r--net/mac80211/status.c6
-rw-r--r--net/mac80211/tx.c5
-rw-r--r--net/mac80211/util.c22
20 files changed, 410 insertions, 260 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 58eab9e8e4e..720b7a84af5 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -56,7 +56,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
}
void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
- u16 initiator, u16 reason)
+ u16 initiator, u16 reason, bool tx)
{
struct ieee80211_local *local = sta->local;
struct tid_ampdu_rx *tid_rx;
@@ -81,7 +81,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
"aggregation for tid %d\n", tid);
/* check if this is a self generated aggregation halt */
- if (initiator == WLAN_BACK_RECIPIENT)
+ if (initiator == WLAN_BACK_RECIPIENT && tx)
ieee80211_send_delba(sta->sdata, sta->sta.addr,
tid, 0, reason);
@@ -92,10 +92,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
}
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
- u16 initiator, u16 reason)
+ u16 initiator, u16 reason, bool tx)
{
mutex_lock(&sta->ampdu_mlme.mtx);
- ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason);
+ ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, tx);
mutex_unlock(&sta->ampdu_mlme.mtx);
}
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index c893f236ace..d4679b265ba 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -145,7 +145,8 @@ static void kfree_tid_tx(struct rcu_head *rcu_head)
}
int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator)
+ enum ieee80211_back_parties initiator,
+ bool tx)
{
struct ieee80211_local *local = sta->local;
struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid];
@@ -175,6 +176,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state);
+ del_timer_sync(&tid_tx->addba_resp_timer);
+
/*
* After this packets are no longer handed right through
* to the driver but are put onto tid_tx->pending instead,
@@ -183,6 +186,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state);
tid_tx->stop_initiator = initiator;
+ tid_tx->tx_stop = tx;
ret = drv_ampdu_action(local, sta->sdata,
IEEE80211_AMPDU_TX_STOP,
@@ -575,13 +579,14 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe);
int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator)
+ enum ieee80211_back_parties initiator,
+ bool tx)
{
int ret;
mutex_lock(&sta->ampdu_mlme.mtx);
- ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator);
+ ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx);
mutex_unlock(&sta->ampdu_mlme.mtx);
@@ -670,7 +675,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
goto unlock_sta;
}
- if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR)
+ if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
@@ -770,7 +775,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
sta->ampdu_mlme.addba_req_num[tid] = 0;
} else {
- ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR);
+ ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
+ true);
}
out:
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c981604b71e..ecf9b7166ed 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -68,14 +68,42 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
params && params->use_4addr >= 0)
sdata->u.mgd.use_4addr = params->use_4addr;
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags)
- sdata->u.mntr_flags = *flags;
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
+ struct ieee80211_local *local = sdata->local;
+
+ if (ieee80211_sdata_running(sdata)) {
+ /*
+ * Prohibit MONITOR_FLAG_COOK_FRAMES to be
+ * changed while the interface is up.
+ * Else we would need to add a lot of cruft
+ * to update everything:
+ * cooked_mntrs, monitor and all fif_* counters
+ * reconfigure hardware
+ */
+ if ((*flags & MONITOR_FLAG_COOK_FRAMES) !=
+ (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
+ return -EBUSY;
+
+ ieee80211_adjust_monitor_flags(sdata, -1);
+ sdata->u.mntr_flags = *flags;
+ ieee80211_adjust_monitor_flags(sdata, 1);
+
+ ieee80211_configure_filter(local);
+ } else {
+ /*
+ * Because the interface is down, ieee80211_do_stop
+ * and ieee80211_do_open take care of "everything"
+ * mentioned in the comment above.
+ */
+ sdata->u.mntr_flags = *flags;
+ }
+ }
return 0;
}
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, const u8 *mac_addr,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
struct key_params *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -103,6 +131,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (IS_ERR(key))
return PTR_ERR(key);
+ if (pairwise)
+ key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
+
mutex_lock(&sdata->local->sta_mtx);
if (mac_addr) {
@@ -125,7 +156,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, const u8 *mac_addr)
+ u8 key_idx, bool pairwise, const u8 *mac_addr)
{
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
@@ -142,10 +173,17 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
goto out_unlock;
- if (sta->key) {
- ieee80211_key_free(sdata->local, sta->key);
- WARN_ON(sta->key);
- ret = 0;
+ if (pairwise) {
+ if (sta->ptk) {
+ ieee80211_key_free(sdata->local, sta->ptk);
+ ret = 0;
+ }
+ } else {
+ if (sta->gtk[key_idx]) {
+ ieee80211_key_free(sdata->local,
+ sta->gtk[key_idx]);
+ ret = 0;
+ }
}
goto out_unlock;
@@ -167,7 +205,8 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_idx, const u8 *mac_addr, void *cookie,
+ u8 key_idx, bool pairwise, const u8 *mac_addr,
+ void *cookie,
void (*callback)(void *cookie,
struct key_params *params))
{
@@ -175,7 +214,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta = NULL;
u8 seq[6] = {0};
struct key_params params;
- struct ieee80211_key *key;
+ struct ieee80211_key *key = NULL;
u32 iv32;
u16 iv16;
int err = -ENOENT;
@@ -189,7 +228,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
goto out;
- key = sta->key;
+ if (pairwise)
+ key = sta->ptk;
+ else if (key_idx < NUM_DEFAULT_KEYS)
+ key = sta->gtk[key_idx];
} else
key = sdata->keys[key_idx];
@@ -285,6 +327,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
STATION_INFO_TX_BYTES |
STATION_INFO_RX_PACKETS |
STATION_INFO_TX_PACKETS |
+ STATION_INFO_TX_RETRIES |
+ STATION_INFO_TX_FAILED |
STATION_INFO_TX_BITRATE;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
@@ -292,6 +336,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->tx_bytes = sta->tx_bytes;
sinfo->rx_packets = sta->rx_packets;
sinfo->tx_packets = sta->tx_packets;
+ sinfo->tx_retries = sta->tx_retry_count;
+ sinfo->tx_failed = sta->tx_retry_failed;
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
@@ -1317,7 +1363,7 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
}
static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
- u8 *addr)
+ const u8 *addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1366,7 +1412,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
if (!sdata->u.mgd.associated ||
sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
mutex_lock(&sdata->local->iflist_mtx);
- ieee80211_recalc_smps(sdata->local, sdata);
+ ieee80211_recalc_smps(sdata->local);
mutex_unlock(&sdata->local->iflist_mtx);
return 0;
}
@@ -1521,7 +1567,11 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
switch (sdata->vif.type) {
case NL80211_IFTYPE_ADHOC:
- if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!ieee80211_is_action(mgmt->frame_control) ||
+ mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
break;
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->da);
@@ -1530,6 +1580,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
return -ENOLINK;
break;
case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
break;
default:
return -EOPNOTSUPP;
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 6b7ff9fb460..50c40ea3cb4 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -196,7 +196,8 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
else
ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);
} else {
- __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, 3);
+ __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
+ 3, true);
ret = 0;
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 11f74f5f7b2..4214bb6e12f 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -101,16 +101,16 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
ht_cap->mcs.rx_mask[32/8] |= 1;
}
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta)
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
{
int i;
cancel_work_sync(&sta->ampdu_mlme.work);
for (i = 0; i < STA_TID_NUM; i++) {
- __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR);
+ __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_LEAVE_QBSS);
+ WLAN_REASON_QSTA_LEAVE_QBSS, tx);
}
}
@@ -135,7 +135,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
___ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_TIMEOUT);
+ WLAN_REASON_QSTA_TIMEOUT, true);
tid_tx = sta->ampdu_mlme.tid_tx[tid];
if (!tid_tx)
@@ -146,7 +146,8 @@ void ieee80211_ba_session_work(struct work_struct *work)
else if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
&tid_tx->state))
___ieee80211_stop_tx_ba_session(sta, tid,
- WLAN_BACK_INITIATOR);
+ WLAN_BACK_INITIATOR,
+ true);
}
mutex_unlock(&sta->ampdu_mlme.mtx);
}
@@ -214,9 +215,11 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (initiator == WLAN_BACK_INITIATOR)
- __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0);
+ __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
+ true);
else
- __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT);
+ __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
+ true);
}
int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 1a3aae54f0c..ff60c022f51 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -173,6 +173,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
memcpy(skb_put(skb, ifibss->ie_len),
ifibss->ie, ifibss->ie_len);
+ if (local->hw.queues >= 4) {
+ pos = skb_put(skb, 9);
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 7; /* len */
+ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+ *pos++ = 0x50;
+ *pos++ = 0xf2;
+ *pos++ = 2; /* WME */
+ *pos++ = 0; /* WME info */
+ *pos++ = 1; /* WME ver */
+ *pos++ = 0; /* U-APSD no in use */
+ }
+
rcu_assign_pointer(ifibss->presp, skb);
sdata->vif.bss_conf.beacon_int = beacon_int;
@@ -266,37 +279,45 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC && elems->supp_rates &&
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
memcmp(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) {
- supp_rates = ieee80211_sta_get_rates(local, elems, band);
rcu_read_lock();
-
sta = sta_info_get(sdata, mgmt->sa);
- if (sta) {
- u32 prev_rates;
- prev_rates = sta->sta.supp_rates[band];
- /* make sure mandatory rates are always added */
- sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(local, band);
+ if (elems->supp_rates) {
+ supp_rates = ieee80211_sta_get_rates(local, elems,
+ band);
+ if (sta) {
+ u32 prev_rates;
- if (sta->sta.supp_rates[band] != prev_rates) {
+ prev_rates = sta->sta.supp_rates[band];
+ /* make sure mandatory rates are always added */
+ sta->sta.supp_rates[band] = supp_rates |
+ ieee80211_mandatory_rates(local, band);
+
+ if (sta->sta.supp_rates[band] != prev_rates) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: updated supp_rates set "
- "for %pM based on beacon/probe_response "
- "(0x%x -> 0x%x)\n",
- sdata->name, sta->sta.addr,
- prev_rates, sta->sta.supp_rates[band]);
+ printk(KERN_DEBUG
+ "%s: updated supp_rates set "
+ "for %pM based on beacon"
+ "/probe_resp (0x%x -> 0x%x)\n",
+ sdata->name, sta->sta.addr,
+ prev_rates,
+ sta->sta.supp_rates[band]);
#endif
- rate_control_rate_init(sta);
- }
- rcu_read_unlock();
- } else {
- rcu_read_unlock();
- ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
- supp_rates, GFP_KERNEL);
+ rate_control_rate_init(sta);
+ }
+ } else
+ sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
+ mgmt->sa, supp_rates,
+ GFP_ATOMIC);
}
+
+ if (sta && elems->wmm_info)
+ set_sta_flags(sta, WLAN_STA_WME);
+
+ rcu_read_unlock();
}
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 945fbf29719..f0610fa4fbe 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -369,6 +369,7 @@ struct ieee80211_if_managed {
unsigned int flags;
+ bool beacon_crc_valid;
u32 beacon_crc;
enum {
@@ -548,8 +549,6 @@ struct ieee80211_sub_if_data {
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next;
-#define NUM_DEFAULT_KEYS 4
-#define NUM_DEFAULT_MGMT_KEYS 2
struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
struct ieee80211_key *default_key;
struct ieee80211_key *default_mgmt_key;
@@ -1132,6 +1131,8 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
void ieee80211_remove_interfaces(struct ieee80211_local *local);
u32 __ieee80211_recalc_idle(struct ieee80211_local *local);
void ieee80211_recalc_idle(struct ieee80211_local *local);
+void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
+ const int offset);
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
@@ -1172,10 +1173,10 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
void ieee80211_request_smps_work(struct work_struct *work);
void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
- u16 initiator, u16 reason);
+ u16 initiator, u16 reason, bool stop);
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
- u16 initiator, u16 reason);
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta);
+ u16 initiator, u16 reason, bool stop);
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx);
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt, size_t len);
@@ -1189,9 +1190,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
size_t len);
int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator);
+ enum ieee80211_back_parties initiator,
+ bool tx);
int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator);
+ enum ieee80211_back_parties initiator,
+ bool tx);
void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);
void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
void ieee80211_ba_session_work(struct work_struct *work);
@@ -1294,8 +1297,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
enum ieee80211_band band);
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode);
-void ieee80211_recalc_smps(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *forsdata);
+void ieee80211_recalc_smps(struct ieee80211_local *local);
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
const u8 *ids, int n_ids, size_t offset);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 66785739dad..e99d1b60557 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -24,6 +24,7 @@
#include "led.h"
#include "driver-ops.h"
#include "wme.h"
+#include "rate.h"
/**
* DOC: Interface list locking
@@ -148,6 +149,26 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
return 0;
}
+void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
+ const int offset)
+{
+ struct ieee80211_local *local = sdata->local;
+ u32 flags = sdata->u.mntr_flags;
+
+#define ADJUST(_f, _s) do { \
+ if (flags & MONITOR_FLAG_##_f) \
+ local->fif_##_s += offset; \
+ } while (0)
+
+ ADJUST(FCSFAIL, fcsfail);
+ ADJUST(PLCPFAIL, plcpfail);
+ ADJUST(CONTROL, control);
+ ADJUST(CONTROL, pspoll);
+ ADJUST(OTHER_BSS, other_bss);
+
+#undef ADJUST
+}
+
/*
* NOTE: Be very careful when changing this function, it must NOT return
* an error on interface type changes that have been pre-checked, so most
@@ -240,17 +261,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
- if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
- local->fif_fcsfail++;
- if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
- local->fif_plcpfail++;
- if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) {
- local->fif_control++;
- local->fif_pspoll++;
- }
- if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
- local->fif_other_bss++;
-
+ ieee80211_adjust_monitor_flags(sdata, 1);
ieee80211_configure_filter(local);
netif_carrier_on(dev);
@@ -301,6 +312,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
/* STA has been freed */
goto err_del_interface;
}
+
+ rate_control_rate_init(sta);
}
/*
@@ -477,17 +490,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
- if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
- local->fif_fcsfail--;
- if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
- local->fif_plcpfail--;
- if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) {
- local->fif_pspoll--;
- local->fif_control--;
- }
- if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
- local->fif_other_bss--;
-
+ ieee80211_adjust_monitor_flags(sdata, -1);
ieee80211_configure_filter(local);
break;
case NL80211_IFTYPE_MESH_POINT:
@@ -793,7 +796,8 @@ static void ieee80211_iface_work(struct work_struct *work)
__ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
+ WLAN_REASON_QSTA_REQUIRE_SETUP,
+ true);
}
mutex_unlock(&local->sta_mtx);
} else switch (sdata->vif.type) {
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 6a63d1abd14..ccd676b2f59 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -68,15 +68,21 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
might_sleep();
- if (!key->local->ops->set_key) {
- ret = -EOPNOTSUPP;
+ if (!key->local->ops->set_key)
goto out_unsupported;
- }
assert_key_lock(key->local);
sta = get_sta_for_key(key);
+ /*
+ * If this is a per-STA GTK, check if it
+ * is supported; if not, return.
+ */
+ if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
+ !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK))
+ goto out_unsupported;
+
sdata = key->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
@@ -85,31 +91,28 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf);
- if (!ret)
+ if (!ret) {
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+ return 0;
+ }
- if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
+ if (ret != -ENOSPC && ret != -EOPNOTSUPP)
wiphy_err(key->local->hw.wiphy,
"failed to set key (%d, %pM) to hardware (%d)\n",
key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
-out_unsupported:
- if (ret) {
- switch (key->conf.cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- case WLAN_CIPHER_SUITE_TKIP:
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_AES_CMAC:
- /* all of these we can do in software */
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- }
+ out_unsupported:
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ /* all of these we can do in software */
+ return 0;
+ default:
+ return -EINVAL;
}
-
- return ret;
}
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
@@ -147,6 +150,26 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
}
+void ieee80211_key_removed(struct ieee80211_key_conf *key_conf)
+{
+ struct ieee80211_key *key;
+
+ key = container_of(key_conf, struct ieee80211_key, conf);
+
+ might_sleep();
+ assert_key_lock(key->local);
+
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ /*
+ * Flush TX path to avoid attempts to use this key
+ * after this function returns. Until then, drivers
+ * must be prepared to handle the key.
+ */
+ synchronize_rcu();
+}
+EXPORT_SYMBOL_GPL(ieee80211_key_removed);
+
static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
int idx)
{
@@ -202,6 +225,7 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
+ bool pairwise,
struct ieee80211_key *old,
struct ieee80211_key *new)
{
@@ -210,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (new)
list_add(&new->list, &sdata->key_list);
- if (sta) {
- rcu_assign_pointer(sta->key, new);
+ if (sta && pairwise) {
+ rcu_assign_pointer(sta->ptk, new);
+ } else if (sta) {
+ if (old)
+ idx = old->conf.keyidx;
+ else
+ idx = new->conf.keyidx;
+ rcu_assign_pointer(sta->gtk[idx], new);
} else {
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
@@ -355,6 +385,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
{
struct ieee80211_key *old_key;
int idx, ret;
+ bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
BUG_ON(!sdata);
BUG_ON(!key);
@@ -371,13 +402,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
*/
if (test_sta_flags(sta, WLAN_STA_WME))
key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
-
- /*
- * This key is for a specific sta interface,
- * inform the driver that it should try to store
- * this key as pairwise key.
- */
- key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
} else {
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
struct sta_info *ap;
@@ -399,12 +423,14 @@ int ieee80211_key_link(struct ieee80211_key *key,
mutex_lock(&sdata->local->key_mtx);
- if (sta)
- old_key = sta->key;
+ if (sta && pairwise)
+ old_key = sta->ptk;
+ else if (sta)
+ old_key = sta->gtk[idx];
else
old_key = sdata->keys[idx];
- __ieee80211_key_replace(sdata, sta, old_key, key);
+ __ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
__ieee80211_key_destroy(old_key);
ieee80211_debugfs_key_add(key);
@@ -423,7 +449,8 @@ static void __ieee80211_key_free(struct ieee80211_key *key)
*/
if (key->sdata)
__ieee80211_key_replace(key->sdata, key->sta,
- key, NULL);
+ key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+ key, NULL);
__ieee80211_key_destroy(key);
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index cb9a4a65cc6..0db1c0f5f69 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -16,6 +16,9 @@
#include <linux/rcupdate.h>
#include <net/mac80211.h>
+#define NUM_DEFAULT_KEYS 4
+#define NUM_DEFAULT_MGMT_KEYS 2
+
#define WEP_IV_LEN 4
#define WEP_ICV_LEN 4
#define ALG_TKIP_KEY_LEN 32
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index db341a99c7c..eb0f5997767 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -201,6 +201,8 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
else if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata->vif.bss_conf.bssid = sdata->vif.addr;
+ else if (sdata->vif.type == NL80211_IFTYPE_WDS)
+ sdata->vif.bss_conf.bssid = NULL;
else if (ieee80211_vif_is_mesh(&sdata->vif)) {
sdata->vif.bss_conf.bssid = zero;
} else {
@@ -211,6 +213,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
break;
default:
@@ -295,7 +298,16 @@ static void ieee80211_restart_work(struct work_struct *work)
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, restart_work);
+ /* wait for scan work complete */
+ flush_workqueue(local->workqueue);
+
+ mutex_lock(&local->mtx);
+ WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+ "%s called with hardware scan in progress\n", __func__);
+ mutex_unlock(&local->mtx);
+
rtnl_lock();
+ ieee80211_scan_cancel(local);
ieee80211_reconfig(local);
rtnl_unlock();
}
@@ -306,15 +318,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
trace_api_restart_hw(local);
- /* wait for scan work complete */
- flush_workqueue(local->workqueue);
-
- WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
- "%s called with hardware scan in progress\n", __func__);
-
- if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)))
- ieee80211_scan_cancel(local);
-
/* use this reason, ieee80211_reconfig will unblock it */
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
@@ -329,7 +332,7 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
container_of(work, struct ieee80211_local, recalc_smps);
mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_smps(local, NULL);
+ ieee80211_recalc_smps(local);
mutex_unlock(&local->iflist_mtx);
}
@@ -533,6 +536,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
/* set up some defaults */
local->hw.queues = 1;
local->hw.max_rates = 1;
+ local->hw.max_report_rates = 0;
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;
@@ -608,6 +612,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};
+ if (hw->max_report_rates == 0)
+ hw->max_report_rates = hw->max_rates;
+
/*
* generic code guarantees at least one band,
* set this very early because much code assumes
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 77913a15f53..5695c94c49a 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -913,7 +913,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
- ieee80211_recalc_smps(local, sdata);
+ ieee80211_recalc_smps(local);
mutex_unlock(&local->iflist_mtx);
netif_tx_start_all_queues(sdata->dev);
@@ -921,7 +921,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
}
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- bool remove_sta)
+ bool remove_sta, bool tx)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
@@ -960,7 +960,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(sdata, bssid);
if (sta) {
set_sta_flags(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta);
+ ieee80211_sta_tear_down_BA_sessions(sta, tx);
}
mutex_unlock(&local->sta_mtx);
@@ -1124,7 +1124,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
- ieee80211_set_disassoc(sdata, true);
+ ieee80211_set_disassoc(sdata, true, true);
mutex_unlock(&ifmgd->mtx);
mutex_lock(&local->mtx);
@@ -1197,7 +1197,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
sdata->name, bssid, reason_code);
- ieee80211_set_disassoc(sdata, true);
+ ieee80211_set_disassoc(sdata, true, false);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
@@ -1229,7 +1229,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
sdata->name, mgmt->sa, reason_code);
- ieee80211_set_disassoc(sdata, true);
+ ieee80211_set_disassoc(sdata, true, false);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
@@ -1291,7 +1291,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
rates = 0;
basic_rates = 0;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ sband = local->hw.wiphy->bands[wk->chan->band];
for (i = 0; i < elems.supp_rates_len; i++) {
int rate = (elems.supp_rates[i] & 0x7f) * 5;
@@ -1327,11 +1327,11 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
}
}
- sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
+ sta->sta.supp_rates[wk->chan->band] = rates;
sdata->vif.bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
- if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+ if (wk->chan->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
@@ -1639,7 +1639,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
ifmgd->aid);
- if (ncrc != ifmgd->beacon_crc) {
+ if (ncrc != ifmgd->beacon_crc || !ifmgd->beacon_crc_valid) {
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
true);
@@ -1670,9 +1670,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
- if (ncrc == ifmgd->beacon_crc)
+ if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
+ ifmgd->beacon_crc_valid = true;
if (elems.erp_info && elems.erp_info_len >= 1) {
erp_valid = true;
@@ -1879,7 +1880,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "No probe response from AP %pM"
" after %dms, disconnecting.\n",
bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
- ieee80211_set_disassoc(sdata, true);
+ ieee80211_set_disassoc(sdata, true, true);
mutex_unlock(&ifmgd->mtx);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
@@ -2203,7 +2204,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
/* Trying to reassociate - clear previous association state */
- ieee80211_set_disassoc(sdata, true);
+ ieee80211_set_disassoc(sdata, true, false);
}
mutex_unlock(&ifmgd->mtx);
@@ -2214,6 +2215,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+ ifmgd->beacon_crc_valid = false;
+
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 ||
@@ -2315,7 +2318,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
memcpy(bssid, req->bss->bssid, ETH_ALEN);
if (ifmgd->associated == req->bss) {
- ieee80211_set_disassoc(sdata, false);
+ ieee80211_set_disassoc(sdata, false, true);
mutex_unlock(&ifmgd->mtx);
assoc_bss = true;
} else {
@@ -2398,7 +2401,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->name, req->bss->bssid, req->reason_code);
memcpy(bssid, req->bss->bssid, ETH_ALEN);
- ieee80211_set_disassoc(sdata, false);
+ ieee80211_set_disassoc(sdata, false, true);
mutex_unlock(&ifmgd->mtx);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index ce671dfd238..e37355193ed 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -12,8 +12,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)))
- ieee80211_scan_cancel(local);
+ ieee80211_scan_cancel(local);
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
@@ -46,7 +45,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
list_for_each_entry(sta, &local->sta_list, list) {
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
set_sta_flags(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta);
+ ieee80211_sta_tear_down_BA_sessions(sta, true);
}
if (sta->uploaded) {
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 0b0e83ebe3d..b67221def58 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -819,6 +819,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
if (unlikely((ieee80211_is_data(hdr->frame_control) ||
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
(!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) {
if ((!ieee80211_has_fromds(hdr->frame_control) &&
!ieee80211_has_tods(hdr->frame_control) &&
@@ -845,7 +846,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
int keyidx;
int hdrlen;
ieee80211_rx_result result = RX_DROP_UNUSABLE;
- struct ieee80211_key *stakey = NULL;
+ struct ieee80211_key *sta_ptk = NULL;
int mmie_keyidx = -1;
__le16 fc;
@@ -887,15 +888,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
rx->key = NULL;
if (rx->sta)
- stakey = rcu_dereference(rx->sta->key);
+ sta_ptk = rcu_dereference(rx->sta->ptk);
fc = hdr->frame_control;
if (!ieee80211_has_protected(fc))
mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
- if (!is_multicast_ether_addr(hdr->addr1) && stakey) {
- rx->key = stakey;
+ if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
+ rx->key = sta_ptk;
if ((status->flag & RX_FLAG_DECRYPTED) &&
(status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
@@ -911,7 +912,10 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (mmie_keyidx < NUM_DEFAULT_KEYS ||
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
return RX_DROP_MONITOR; /* unexpected BIP keyidx */
- rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
+ if (rx->sta)
+ rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
+ if (!rx->key)
+ rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
} else if (!ieee80211_has_protected(fc)) {
/*
* The frame was not protected, so skip decryption. However, we
@@ -954,17 +958,25 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
keyidx = keyid >> 6;
- rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+ /* check per-station GTK first, if multicast packet */
+ if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
+ rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
- /*
- * RSNA-protected unicast frames should always be sent with
- * pairwise or station-to-station keys, but for WEP we allow
- * using a key index as well.
- */
- if (rx->key && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
- rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
- !is_multicast_ether_addr(hdr->addr1))
- rx->key = NULL;
+ /* if not found, try default key */
+ if (!rx->key) {
+ rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+
+ /*
+ * RSNA-protected unicast frames should always be
+ * sent with pairwise or station-to-station keys,
+ * but for WEP we allow using a key index as well.
+ */
+ if (rx->key &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
+ !is_multicast_ether_addr(hdr->addr1))
+ rx->key = NULL;
+ }
}
if (rx->key) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 5171a958163..fb274db77e3 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -249,12 +249,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
return true;
}
-static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
+static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
+ bool was_hw_scan)
{
struct ieee80211_local *local = hw_to_local(hw);
- bool was_hw_scan;
- mutex_lock(&local->mtx);
+ lockdep_assert_held(&local->mtx);
/*
* It's ok to abort a not-yet-running scan (that
@@ -265,17 +265,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (WARN_ON(!local->scanning && !aborted))
aborted = true;
- if (WARN_ON(!local->scan_req)) {
- mutex_unlock(&local->mtx);
- return;
- }
+ if (WARN_ON(!local->scan_req))
+ return false;
- was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
- ieee80211_queue_delayed_work(&local->hw,
- &local->scan_work, 0);
- mutex_unlock(&local->mtx);
- return;
+ int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req);
+ if (rc == 0)
+ return false;
}
kfree(local->hw_scan_req);
@@ -289,23 +285,25 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
local->scanning = 0;
local->scan_channel = NULL;
- /* we only have to protect scan_req and hw/sw scan */
- mutex_unlock(&local->mtx);
-
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
- if (was_hw_scan)
- goto done;
-
- ieee80211_configure_filter(local);
+ return true;
+}
- drv_sw_scan_complete(local);
+static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw,
+ bool was_hw_scan)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
- ieee80211_offchannel_return(local, true);
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ if (!was_hw_scan) {
+ ieee80211_configure_filter(local);
+ drv_sw_scan_complete(local);
+ ieee80211_offchannel_return(local, true);
+ }
- done:
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
+
ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
@@ -366,6 +364,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
int rc;
+ lockdep_assert_held(&local->mtx);
+
if (local->scan_req)
return -EBUSY;
@@ -447,8 +447,8 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
}
-static int ieee80211_scan_state_decision(struct ieee80211_local *local,
- unsigned long *next_delay)
+static void ieee80211_scan_state_decision(struct ieee80211_local *local,
+ unsigned long *next_delay)
{
bool associated = false;
bool tx_empty = true;
@@ -458,12 +458,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
- /* if no more bands/channels left, complete scan and advance to the idle state */
- if (local->scan_channel_idx >= local->scan_req->n_channels) {
- __ieee80211_scan_completed(&local->hw, false);
- return 1;
- }
-
/*
* check if at least one STA interface is associated,
* check if at least one STA interface has pending tx frames
@@ -535,7 +529,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
}
*next_delay = 0;
- return 0;
}
static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
@@ -651,28 +644,17 @@ void ieee80211_scan_work(struct work_struct *work)
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
unsigned long next_delay = 0;
+ bool aborted, hw_scan, finish;
- if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {
- bool aborted;
+ mutex_lock(&local->mtx);
+ if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {
aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
- __ieee80211_scan_completed(&local->hw, aborted);
- return;
- }
-
- mutex_lock(&local->mtx);
- if (!sdata || !local->scan_req) {
- mutex_unlock(&local->mtx);
- return;
+ goto out_complete;
}
- if (local->hw_scan_req) {
- int rc = drv_hw_scan(local, sdata, local->hw_scan_req);
- mutex_unlock(&local->mtx);
- if (rc)
- __ieee80211_scan_completed(&local->hw, true);
- return;
- }
+ if (!sdata || !local->scan_req)
+ goto out;
if (local->scan_req && !local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
@@ -682,21 +664,21 @@ void ieee80211_scan_work(struct work_struct *work)
local->scan_sdata = NULL;
rc = __ieee80211_start_scan(sdata, req);
- mutex_unlock(&local->mtx);
-
- if (rc)
- __ieee80211_scan_completed(&local->hw, true);
- return;
+ if (rc) {
+ /* need to complete scan in cfg80211 */
+ local->scan_req = req;
+ aborted = true;
+ goto out_complete;
+ } else
+ goto out;
}
- mutex_unlock(&local->mtx);
-
/*
* Avoid re-scheduling when the sdata is going away.
*/
if (!ieee80211_sdata_running(sdata)) {
- __ieee80211_scan_completed(&local->hw, true);
- return;
+ aborted = true;
+ goto out_complete;
}
/*
@@ -706,8 +688,12 @@ void ieee80211_scan_work(struct work_struct *work)
do {
switch (local->next_scan_state) {
case SCAN_DECISION:
- if (ieee80211_scan_state_decision(local, &next_delay))
- return;
+ /* if no more bands/channels left, complete scan */
+ if (local->scan_channel_idx >= local->scan_req->n_channels) {
+ aborted = false;
+ goto out_complete;
+ }
+ ieee80211_scan_state_decision(local, &next_delay);
break;
case SCAN_SET_CHANNEL:
ieee80211_scan_state_set_channel(local, &next_delay);
@@ -725,6 +711,19 @@ void ieee80211_scan_work(struct work_struct *work)
} while (next_delay == 0);
ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
+ mutex_unlock(&local->mtx);
+ return;
+
+out_complete:
+ hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
+ finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan);
+ mutex_unlock(&local->mtx);
+ if (finish)
+ __ieee80211_scan_completed_finish(&local->hw, hw_scan);
+ return;
+
+out:
+ mutex_unlock(&local->mtx);
}
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
@@ -786,21 +785,40 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
return ret;
}
+/*
+ * Only call this function when a scan can't be queued -- under RTNL.
+ */
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
bool abortscan;
-
- cancel_delayed_work_sync(&local->scan_work);
+ bool finish = false;
/*
- * Only call this function when a scan can't be
- * queued -- mostly at suspend under RTNL.
+ * We are only canceling software scan, or deferred scan that was not
+ * yet really started (see __ieee80211_start_scan ).
+ *
+ * Regarding hardware scan:
+ * - we can not call __ieee80211_scan_completed() as when
+ * SCAN_HW_SCANNING bit is set this function change
+ * local->hw_scan_req to operate on 5G band, what race with
+ * driver which can use local->hw_scan_req
+ *
+ * - we can not cancel scan_work since driver can schedule it
+ * by ieee80211_scan_completed(..., true) to finish scan
+ *
+ * Hence low lever driver is responsible for canceling HW scan.
*/
+
mutex_lock(&local->mtx);
- abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
- (!local->scanning && local->scan_req);
+ abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning);
+ if (abortscan)
+ finish = __ieee80211_scan_completed(&local->hw, true, false);
mutex_unlock(&local->mtx);
- if (abortscan)
- __ieee80211_scan_completed(&local->hw, true);
+ if (abortscan) {
+ /* The scan is canceled, but stop work from being pending */
+ cancel_delayed_work_sync(&local->scan_work);
+ }
+ if (finish)
+ __ieee80211_scan_completed_finish(&local->hw, false);
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ca2cba9cea8..6d8f897d876 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -616,7 +616,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
unsigned long flags;
- int ret;
+ int ret, i;
might_sleep();
@@ -633,7 +633,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
* will be sufficient.
*/
set_sta_flags(sta, WLAN_STA_BLOCK_BA);
- ieee80211_sta_tear_down_BA_sessions(sta);
+ ieee80211_sta_tear_down_BA_sessions(sta, true);
spin_lock_irqsave(&local->sta_lock, flags);
ret = sta_info_hash_del(local, sta);
@@ -644,10 +644,10 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
if (ret)
return ret;
- if (sta->key) {
- ieee80211_key_free(local, sta->key);
- WARN_ON(sta->key);
- }
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++)
+ ieee80211_key_free(local, sta->gtk[i]);
+ if (sta->ptk)
+ ieee80211_key_free(local, sta->ptk);
sta->dead = true;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 810c5ce9831..9265acadef3 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -79,6 +79,7 @@ enum ieee80211_sta_info_flags {
* @dialog_token: dialog token for aggregation session
* @state: session state (see above)
* @stop_initiator: initiator of a session stop
+ * @tx_stop: TX DelBA frame when stopping
*
* This structure is protected by RCU and the per-station
* spinlock. Assignments to the array holding it must hold
@@ -95,6 +96,7 @@ struct tid_ampdu_tx {
unsigned long state;
u8 dialog_token;
u8 stop_initiator;
+ bool tx_stop;
};
/**
@@ -197,7 +199,8 @@ enum plink_state {
* @hnext: hash table linked list pointer
* @local: pointer to the global information
* @sdata: virtual interface this station belongs to
- * @key: peer key negotiated with this station, if any
+ * @ptk: peer key negotiated with this station, if any
+ * @gtk: group keys negotiated with this station, if any
* @rate_ctrl: rate control algorithm reference
* @rate_ctrl_priv: rate control private per-STA pointer
* @last_tx_rate: rate used for last transmit, to report to userspace as
@@ -252,7 +255,8 @@ struct sta_info {
struct sta_info *hnext;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_key *key;
+ struct ieee80211_key *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
+ struct ieee80211_key *ptk;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
spinlock_t lock;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index dd85006c4fe..3153c19893b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -176,7 +176,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
/* the HW cannot have attempted that rate */
- if (i >= hw->max_rates) {
+ if (i >= hw->max_report_rates) {
info->status.rates[i].idx = -1;
info->status.rates[i].count = 0;
} else if (info->status.rates[i].idx >= 0) {
@@ -377,7 +377,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2) {
skb2->dev = prev_dev;
- netif_receive_skb(skb2);
+ netif_rx(skb2);
}
}
@@ -386,7 +386,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
}
if (prev_dev) {
skb->dev = prev_dev;
- netif_receive_skb(skb);
+ netif_rx(skb);
skb = NULL;
}
rcu_read_unlock();
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index e1733dcb58a..96c59430950 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -273,6 +273,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
*/
return TX_DROP;
+ if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
+ return TX_CONTINUE;
+
if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
return TX_CONTINUE;
@@ -529,7 +532,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
tx->key = NULL;
- else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
+ else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
tx->key = key;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index aba025d748e..0b6fc92bc0d 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1221,7 +1221,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
- ieee80211_sta_tear_down_BA_sessions(sta);
+ ieee80211_sta_tear_down_BA_sessions(sta, true);
clear_sta_flags(sta, WLAN_STA_BLOCK_BA);
}
@@ -1297,16 +1297,12 @@ static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
}
/* must hold iflist_mtx */
-void ieee80211_recalc_smps(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *forsdata)
+void ieee80211_recalc_smps(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
int count = 0;
- if (forsdata)
- lockdep_assert_held(&forsdata->u.mgd.mtx);
-
lockdep_assert_held(&local->iflist_mtx);
/*
@@ -1324,18 +1320,8 @@ void ieee80211_recalc_smps(struct ieee80211_local *local,
continue;
if (sdata->vif.type != NL80211_IFTYPE_STATION)
goto set;
- if (sdata != forsdata) {
- /*
- * This nested is ok -- we are holding the iflist_mtx
- * so can't get here twice or so. But it's required
- * since normally we acquire it first and then the
- * iflist_mtx.
- */
- mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
- count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
- mutex_unlock(&sdata->u.mgd.mtx);
- } else
- count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+
+ count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
if (count > 1) {
smps_mode = IEEE80211_SMPS_OFF;