summaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-19 17:05:34 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-19 17:05:34 (GMT)
commit1200b6809dfd9d73bc4c7db76d288c35fa4b2ebe (patch)
tree552e03de245cdbd0780ca1215914edc4a26540f7 /net/mac80211
parent6b5f04b6cf8ebab9a65d9c0026c650bb2538fd0f (diff)
parentfe30937b65354c7fec244caebbdaae68e28ca797 (diff)
downloadlinux-1200b6809dfd9d73bc4c7db76d288c35fa4b2ebe.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights: 1) Support more Realtek wireless chips, from Jes Sorenson. 2) New BPF types for per-cpu hash and arrap maps, from Alexei Starovoitov. 3) Make several TCP sysctls per-namespace, from Nikolay Borisov. 4) Allow the use of SO_REUSEPORT in order to do per-thread processing of incoming TCP/UDP connections. The muxing can be done using a BPF program which hashes the incoming packet. From Craig Gallek. 5) Add a multiplexer for TCP streams, to provide a messaged based interface. BPF programs can be used to determine the message boundaries. From Tom Herbert. 6) Add 802.1AE MACSEC support, from Sabrina Dubroca. 7) Avoid factorial complexity when taking down an inetdev interface with lots of configured addresses. We were doing things like traversing the entire address less for each address removed, and flushing the entire netfilter conntrack table for every address as well. 8) Add and use SKB bulk free infrastructure, from Jesper Brouer. 9) Allow offloading u32 classifiers to hardware, and implement for ixgbe, from John Fastabend. 10) Allow configuring IRQ coalescing parameters on a per-queue basis, from Kan Liang. 11) Extend ethtool so that larger link mode masks can be supported. From David Decotigny. 12) Introduce devlink, which can be used to configure port link types (ethernet vs Infiniband, etc.), port splitting, and switch device level attributes as a whole. From Jiri Pirko. 13) Hardware offload support for flower classifiers, from Amir Vadai. 14) Add "Local Checksum Offload". Basically, for a tunneled packet the checksum of the outer header is 'constant' (because with the checksum field filled into the inner protocol header, the payload of the outer frame checksums to 'zero'), and we can take advantage of that in various ways. From Edward Cree" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1548 commits) bonding: fix bond_get_stats() net: bcmgenet: fix dma api length mismatch net/mlx4_core: Fix backward compatibility on VFs phy: mdio-thunder: Fix some Kconfig typos lan78xx: add ndo_get_stats64 lan78xx: handle statistics counter rollover RDS: TCP: Remove unused constant RDS: TCP: Add sysctl tunables for sndbuf/rcvbuf on rds-tcp socket net: smc911x: convert pxa dma to dmaengine team: remove duplicate set of flag IFF_MULTICAST bonding: remove duplicate set of flag IFF_MULTICAST net: fix a comment typo ethernet: micrel: fix some error codes ip_tunnels, bpf: define IP_TUNNEL_OPTS_MAX and use it bpf, dst: add and use dst_tclassid helper bpf: make skb->tc_classid also readable net: mvneta: bm: clarify dependencies cls_bpf: reset class and reuse major in da ldmvsw: Checkpatch sunvnet.c and sunvnet_common.c ldmvsw: Add ldmvsw.c driver code ...
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/agg-rx.c50
-rw-r--r--net/mac80211/agg-tx.c53
-rw-r--r--net/mac80211/cfg.c34
-rw-r--r--net/mac80211/chan.c2
-rw-r--r--net/mac80211/debugfs.c1
-rw-r--r--net/mac80211/debugfs_key.c5
-rw-r--r--net/mac80211/driver-ops.c10
-rw-r--r--net/mac80211/driver-ops.h4
-rw-r--r--net/mac80211/ht.c5
-rw-r--r--net/mac80211/ibss.c32
-rw-r--r--net/mac80211/ieee80211_i.h37
-rw-r--r--net/mac80211/iface.c14
-rw-r--r--net/mac80211/key.c86
-rw-r--r--net/mac80211/key.h10
-rw-r--r--net/mac80211/mesh.c9
-rw-r--r--net/mac80211/mesh.h3
-rw-r--r--net/mac80211/mesh_hwmp.c6
-rw-r--r--net/mac80211/mesh_pathtbl.c111
-rw-r--r--net/mac80211/mesh_plink.c10
-rw-r--r--net/mac80211/mlme.c79
-rw-r--r--net/mac80211/rx.c141
-rw-r--r--net/mac80211/sta_info.c35
-rw-r--r--net/mac80211/sta_info.h24
-rw-r--r--net/mac80211/status.c2
-rw-r--r--net/mac80211/tkip.c36
-rw-r--r--net/mac80211/tkip.h2
-rw-r--r--net/mac80211/trace.h43
-rw-r--r--net/mac80211/tx.c100
-rw-r--r--net/mac80211/util.c116
-rw-r--r--net/mac80211/vht.c57
-rw-r--r--net/mac80211/wpa.c11
31 files changed, 758 insertions, 370 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 367784b..3a8f881 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -7,6 +7,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -61,16 +62,25 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
{
struct ieee80211_local *local = sta->local;
struct tid_ampdu_rx *tid_rx;
+ struct ieee80211_ampdu_params params = {
+ .sta = &sta->sta,
+ .action = IEEE80211_AMPDU_RX_STOP,
+ .tid = tid,
+ .amsdu = false,
+ .timeout = 0,
+ .ssn = 0,
+ };
lockdep_assert_held(&sta->ampdu_mlme.mtx);
tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid],
lockdep_is_held(&sta->ampdu_mlme.mtx));
- if (!tid_rx)
+ if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid))
return;
RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL);
+ __clear_bit(tid, sta->ampdu_mlme.agg_session_valid);
ht_dbg(sta->sdata,
"Rx BA session stop requested for %pM tid %u %s reason: %d\n",
@@ -78,8 +88,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator",
(int)reason);
- if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
- &sta->sta, tid, NULL, 0, false))
+ if (drv_ampdu_action(local, sta->sdata, &params))
sdata_info(sta->sdata,
"HW problem - can not stop rx aggregation for %pM tid %d\n",
sta->sta.addr, tid);
@@ -89,6 +98,13 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
ieee80211_send_delba(sta->sdata, sta->sta.addr,
tid, WLAN_BACK_RECIPIENT, reason);
+ /*
+ * return here in case tid_rx is not assigned - which will happen if
+ * IEEE80211_HW_SUPPORTS_REORDERING_BUFFER is set.
+ */
+ if (!tid_rx)
+ return;
+
del_timer_sync(&tid_rx->session_timer);
/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
@@ -237,6 +253,15 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
{
struct ieee80211_local *local = sta->sdata->local;
struct tid_ampdu_rx *tid_agg_rx;
+ struct ieee80211_ampdu_params params = {
+ .sta = &sta->sta,
+ .action = IEEE80211_AMPDU_RX_START,
+ .tid = tid,
+ .amsdu = false,
+ .timeout = timeout,
+ .ssn = start_seq_num,
+ };
+
int i, ret = -EOPNOTSUPP;
u16 status = WLAN_STATUS_REQUEST_DECLINED;
@@ -275,11 +300,12 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
/* make sure the size doesn't exceed the maximum supported by the hw */
if (buf_size > local->hw.max_rx_aggregation_subframes)
buf_size = local->hw.max_rx_aggregation_subframes;
+ params.buf_size = buf_size;
/* examine state machine */
mutex_lock(&sta->ampdu_mlme.mtx);
- if (sta->ampdu_mlme.tid_rx[tid]) {
+ if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
ht_dbg_ratelimited(sta->sdata,
"unexpected AddBA Req from %pM on tid %u\n",
sta->sta.addr, tid);
@@ -290,6 +316,16 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
false);
}
+ if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) {
+ ret = drv_ampdu_action(local, sta->sdata, &params);
+ ht_dbg(sta->sdata,
+ "Rx A-MPDU request on %pM tid %d result %d\n",
+ sta->sta.addr, tid, ret);
+ if (!ret)
+ status = WLAN_STATUS_SUCCESS;
+ goto end;
+ }
+
/* prepare A-MPDU MLME for Rx aggregation */
tid_agg_rx = kzalloc(sizeof(*tid_agg_rx), GFP_KERNEL);
if (!tid_agg_rx)
@@ -322,8 +358,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
for (i = 0; i < buf_size; i++)
__skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
- ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
- &sta->sta, tid, &start_seq_num, 0, false);
+ ret = drv_ampdu_action(local, sta->sdata, &params);
ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
sta->sta.addr, tid, ret);
if (ret) {
@@ -341,6 +376,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
tid_agg_rx->timeout = timeout;
tid_agg_rx->stored_mpdu_num = 0;
tid_agg_rx->auto_seq = auto_seq;
+ tid_agg_rx->reorder_buf_filtered = 0;
status = WLAN_STATUS_SUCCESS;
/* activate it for RX */
@@ -352,6 +388,8 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
}
end:
+ if (status == WLAN_STATUS_SUCCESS)
+ __set_bit(tid, sta->ampdu_mlme.agg_session_valid);
mutex_unlock(&sta->ampdu_mlme.mtx);
end_no_lock:
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index ff75718..4932e9f 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -7,6 +7,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -295,7 +296,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
{
struct ieee80211_local *local = sta->local;
struct tid_ampdu_tx *tid_tx;
- enum ieee80211_ampdu_mlme_action action;
+ struct ieee80211_ampdu_params params = {
+ .sta = &sta->sta,
+ .tid = tid,
+ .buf_size = 0,
+ .amsdu = false,
+ .timeout = 0,
+ .ssn = 0,
+ };
int ret;
lockdep_assert_held(&sta->ampdu_mlme.mtx);
@@ -304,10 +312,10 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
case AGG_STOP_DECLINED:
case AGG_STOP_LOCAL_REQUEST:
case AGG_STOP_PEER_REQUEST:
- action = IEEE80211_AMPDU_TX_STOP_CONT;
+ params.action = IEEE80211_AMPDU_TX_STOP_CONT;
break;
case AGG_STOP_DESTROY_STA:
- action = IEEE80211_AMPDU_TX_STOP_FLUSH;
+ params.action = IEEE80211_AMPDU_TX_STOP_FLUSH;
break;
default:
WARN_ON_ONCE(1);
@@ -330,9 +338,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
spin_unlock_bh(&sta->lock);
if (reason != AGG_STOP_DESTROY_STA)
return -EALREADY;
- ret = drv_ampdu_action(local, sta->sdata,
- IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
- &sta->sta, tid, NULL, 0, false);
+ params.action = IEEE80211_AMPDU_TX_STOP_FLUSH_CONT;
+ ret = drv_ampdu_action(local, sta->sdata, &params);
WARN_ON_ONCE(ret);
return 0;
}
@@ -381,8 +388,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
WLAN_BACK_INITIATOR;
tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
- ret = drv_ampdu_action(local, sta->sdata, action,
- &sta->sta, tid, NULL, 0, false);
+ ret = drv_ampdu_action(local, sta->sdata, &params);
/* HW shall not deny going back to legacy */
if (WARN_ON(ret)) {
@@ -445,7 +451,14 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
struct tid_ampdu_tx *tid_tx;
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- u16 start_seq_num;
+ struct ieee80211_ampdu_params params = {
+ .sta = &sta->sta,
+ .action = IEEE80211_AMPDU_TX_START,
+ .tid = tid,
+ .buf_size = 0,
+ .amsdu = false,
+ .timeout = 0,
+ };
int ret;
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
@@ -467,10 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
*/
synchronize_net();
- start_seq_num = sta->tid_seq[tid] >> 4;
-
- ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
- &sta->sta, tid, &start_seq_num, 0, false);
+ params.ssn = sta->tid_seq[tid] >> 4;
+ ret = drv_ampdu_action(local, sdata, &params);
if (ret) {
ht_dbg(sdata,
"BA request denied - HW unavailable for %pM tid %d\n",
@@ -499,7 +510,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
/* send AddBA request */
ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
- tid_tx->dialog_token, start_seq_num,
+ tid_tx->dialog_token, params.ssn,
IEEE80211_MAX_AMPDU_BUF,
tid_tx->timeout);
}
@@ -684,18 +695,24 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
struct sta_info *sta, u16 tid)
{
struct tid_ampdu_tx *tid_tx;
+ struct ieee80211_ampdu_params params = {
+ .sta = &sta->sta,
+ .action = IEEE80211_AMPDU_TX_OPERATIONAL,
+ .tid = tid,
+ .timeout = 0,
+ .ssn = 0,
+ };
lockdep_assert_held(&sta->ampdu_mlme.mtx);
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+ params.buf_size = tid_tx->buf_size;
+ params.amsdu = tid_tx->amsdu;
ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n",
sta->sta.addr, tid);
- drv_ampdu_action(local, sta->sdata,
- IEEE80211_AMPDU_TX_OPERATIONAL,
- &sta->sta, tid, NULL, tid_tx->buf_size,
- tid_tx->amsdu);
+ drv_ampdu_action(local, sta->sdata, &params);
/*
* synchronize with TX path, while splicing the TX path
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 166a29f..fe1704c 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -339,8 +339,9 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- iv32 = key->u.tkip.tx.iv32;
- iv16 = key->u.tkip.tx.iv16;
+ pn64 = atomic64_read(&key->conf.tx_pn);
+ iv32 = TKIP_PN_TO_IV32(pn64);
+ iv16 = TKIP_PN_TO_IV16(pn64);
if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
@@ -1131,6 +1132,34 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta->sta.max_sp = params->max_sp;
}
+ /* The sender might not have sent the last bit, consider it to be 0 */
+ if (params->ext_capab_len >= 8) {
+ u8 val = (params->ext_capab[7] &
+ WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB) >> 7;
+
+ /* we did get all the bits, take the MSB as well */
+ if (params->ext_capab_len >= 9) {
+ u8 val_msb = params->ext_capab[8] &
+ WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB;
+ val_msb <<= 1;
+ val |= val_msb;
+ }
+
+ switch (val) {
+ case 1:
+ sta->sta.max_amsdu_subframes = 32;
+ break;
+ case 2:
+ sta->sta.max_amsdu_subframes = 16;
+ break;
+ case 3:
+ sta->sta.max_amsdu_subframes = 8;
+ break;
+ default:
+ sta->sta.max_amsdu_subframes = 0;
+ }
+ }
+
/*
* cfg80211 validates this (1-2007) and allows setting the AID
* only when creating a new station entry
@@ -1160,6 +1189,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
params->ht_capa, sta);
+ /* VHT can override some HT caps such as the A-MSDU max length */
if (params->vht_capa)
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
params->vht_capa, sta);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 1d1b9b7..2839811 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -231,7 +231,7 @@ ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
!(sta->sdata->bss && sta->sdata->bss == sdata->bss))
continue;
- if (!sta->uploaded)
+ if (!sta->uploaded || !test_sta_flag(sta, WLAN_STA_ASSOC))
continue;
max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 3e24d0d..4ab5c52 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -126,6 +126,7 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_AMSDU_IN_AMPDU),
FLAG(BEACON_TX_STATUS),
FLAG(NEEDS_UNIQUE_STA_ADDR),
+ FLAG(SUPPORTS_REORDERING_BUFFER),
#undef FLAG
};
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 7961e7d..a2ef95f 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -132,9 +132,10 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
len = scnprintf(buf, sizeof(buf), "\n");
break;
case WLAN_CIPHER_SUITE_TKIP:
+ pn = atomic64_read(&key->conf.tx_pn);
len = scnprintf(buf, sizeof(buf), "%08x %04x\n",
- key->u.tkip.tx.iv32,
- key->u.tkip.tx.iv16);
+ TKIP_PN_TO_IV32(pn),
+ TKIP_PN_TO_IV16(pn));
break;
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index ca1fe55..c258f10 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -284,9 +284,7 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local,
int drv_ampdu_action(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid,
- u16 *ssn, u8 buf_size, bool amsdu)
+ struct ieee80211_ampdu_params *params)
{
int ret = -EOPNOTSUPP;
@@ -296,12 +294,10 @@ int drv_ampdu_action(struct ieee80211_local *local,
if (!check_sdata_in_driver(sdata))
return -EIO;
- trace_drv_ampdu_action(local, sdata, action, sta, tid,
- ssn, buf_size, amsdu);
+ trace_drv_ampdu_action(local, sdata, params);
if (local->ops->ampdu_action)
- ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
- sta, tid, ssn, buf_size, amsdu);
+ ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params);
trace_drv_return_int(local, ret);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 154ce4b..18b0d65 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -585,9 +585,7 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local)
int drv_ampdu_action(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid,
- u16 *ssn, u8 buf_size, bool amsdu);
+ struct ieee80211_ampdu_params *params);
static inline int drv_get_survey(struct ieee80211_local *local, int idx,
struct survey_info *survey)
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 7a76ce6..f4a5287 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -230,6 +230,11 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
/* set Rx highest rate */
ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
+ if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
+ sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
+ else
+ sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
+
apply:
changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 978d3bc..fc32383 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -7,6 +7,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -1050,9 +1051,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def chandef;
enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
- ieee80211_ht_oper_to_chandef(channel,
- elems->ht_operation,
- &chandef);
+ cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
+ ieee80211_chandef_ht_oper(elems->ht_operation, &chandef);
memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
@@ -1066,9 +1066,8 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_vht_cap cap_ie;
struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
- ieee80211_vht_oper_to_chandef(channel,
- elems->vht_operation,
- &chandef);
+ ieee80211_chandef_vht_oper(elems->vht_operation,
+ &chandef);
memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie));
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
&cap_ie, sta);
@@ -1485,14 +1484,21 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
- num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
- &ifibss->chandef,
- channels,
- ARRAY_SIZE(channels));
scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
- ieee80211_request_ibss_scan(sdata, ifibss->ssid,
- ifibss->ssid_len, channels, num,
- scan_width);
+
+ if (ifibss->fixed_channel) {
+ num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy,
+ &ifibss->chandef,
+ channels,
+ ARRAY_SIZE(channels));
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, channels,
+ num, scan_width);
+ } else {
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, NULL,
+ 0, scan_width);
+ }
} else {
int interval = IEEE80211_SCAN_INTERVAL;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f006f4a..804575f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -716,7 +716,6 @@ struct ieee80211_if_mesh {
* back to wireless media and to the local net stack.
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
* @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
- * @IEEE80211_SDATA_MU_MIMO_OWNER: indicates interface owns MU-MIMO capability
*/
enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_ALLMULTI = BIT(0),
@@ -724,7 +723,6 @@ enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
IEEE80211_SDATA_IN_DRIVER = BIT(5),
- IEEE80211_SDATA_MU_MIMO_OWNER = BIT(6),
};
/**
@@ -804,6 +802,7 @@ enum txq_info_flags {
struct txq_info {
struct sk_buff_head queue;
unsigned long flags;
+ unsigned long byte_cnt;
/* keep last! */
struct ieee80211_txq txq;
@@ -1466,7 +1465,13 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status)
{
WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START &&
status->flag & RX_FLAG_MACTIME_END);
- return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END);
+ if (status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END))
+ return true;
+ /* can't handle HT/VHT preamble yet */
+ if (status->flag & RX_FLAG_MACTIME_PLCP_START &&
+ !(status->flag & (RX_FLAG_HT | RX_FLAG_VHT)))
+ return true;
+ return false;
}
u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
@@ -1714,6 +1719,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta);
enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
void ieee80211_sta_set_rx_nss(struct sta_info *sta);
+void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt);
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
enum ieee80211_band band);
@@ -1829,20 +1836,6 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
}
-static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
-{
- struct sk_buff *tail = skb_peek_tail(frames);
- struct ieee80211_rx_status *status;
-
- if (!tail)
- return false;
-
- status = IEEE80211_SKB_RXCB(tail);
- if (status->flag & RX_FLAG_AMSDU_MORE)
- return false;
-
- return true;
-}
extern const int ieee802_1d_to_ac[8];
@@ -1986,12 +1979,10 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
/* channel management */
-void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
- const struct ieee80211_ht_operation *ht_oper,
- struct cfg80211_chan_def *chandef);
-void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
- const struct ieee80211_vht_operation *oper,
- struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper,
+ struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index c9e325d..453b4e74 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -977,7 +977,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.txq) {
struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+ spin_lock_bh(&txqi->queue.lock);
ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+ txqi->byte_cnt = 0;
+ spin_unlock_bh(&txqi->queue.lock);
+
atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
}
@@ -1271,6 +1275,16 @@ static void ieee80211_iface_work(struct work_struct *work)
}
}
mutex_unlock(&local->sta_mtx);
+ } else if (ieee80211_is_action(mgmt->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_VHT) {
+ switch (mgmt->u.action.u.vht_group_notif.action_code) {
+ case WLAN_VHT_ACTION_GROUPID_MGMT:
+ ieee80211_process_mu_groups(sdata, mgmt);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
} else if (ieee80211_is_data_qos(mgmt->frame_control)) {
struct ieee80211_hdr *hdr = (void *)mgmt;
/*
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 5e5bc59..3df7b03 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -932,50 +932,6 @@ void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
}
EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_notify);
-void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf,
- struct ieee80211_key_seq *seq)
-{
- struct ieee80211_key *key;
- u64 pn64;
-
- if (WARN_ON(!(keyconf->flags & IEEE80211_KEY_FLAG_GENERATE_IV)))
- return;
-
- key = container_of(keyconf, struct ieee80211_key, conf);
-
- switch (key->conf.cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- seq->tkip.iv32 = key->u.tkip.tx.iv32;
- seq->tkip.iv16 = key->u.tkip.tx.iv16;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_CCMP_256:
- case WLAN_CIPHER_SUITE_AES_CMAC:
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
- offsetof(typeof(*seq), aes_cmac));
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
- offsetof(typeof(*seq), aes_gmac));
- case WLAN_CIPHER_SUITE_GCMP:
- case WLAN_CIPHER_SUITE_GCMP_256:
- BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
- offsetof(typeof(*seq), gcmp));
- pn64 = atomic64_read(&key->conf.tx_pn);
- seq->ccmp.pn[5] = pn64;
- seq->ccmp.pn[4] = pn64 >> 8;
- seq->ccmp.pn[3] = pn64 >> 16;
- seq->ccmp.pn[2] = pn64 >> 24;
- seq->ccmp.pn[1] = pn64 >> 32;
- seq->ccmp.pn[0] = pn64 >> 40;
- break;
- default:
- WARN_ON(1);
- }
-}
-EXPORT_SYMBOL(ieee80211_get_key_tx_seq);
-
void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
int tid, struct ieee80211_key_seq *seq)
{
@@ -1029,48 +985,6 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
}
EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
-void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
- struct ieee80211_key_seq *seq)
-{
- struct ieee80211_key *key;
- u64 pn64;
-
- key = container_of(keyconf, struct ieee80211_key, conf);
-
- switch (key->conf.cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- key->u.tkip.tx.iv32 = seq->tkip.iv32;
- key->u.tkip.tx.iv16 = seq->tkip.iv16;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_CCMP_256:
- case WLAN_CIPHER_SUITE_AES_CMAC:
- case WLAN_CIPHER_SUITE_BIP_CMAC_256:
- BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
- offsetof(typeof(*seq), aes_cmac));
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
- offsetof(typeof(*seq), aes_gmac));
- case WLAN_CIPHER_SUITE_GCMP:
- case WLAN_CIPHER_SUITE_GCMP_256:
- BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
- offsetof(typeof(*seq), gcmp));
- pn64 = (u64)seq->ccmp.pn[5] |
- ((u64)seq->ccmp.pn[4] << 8) |
- ((u64)seq->ccmp.pn[3] << 16) |
- ((u64)seq->ccmp.pn[2] << 24) |
- ((u64)seq->ccmp.pn[1] << 32) |
- ((u64)seq->ccmp.pn[0] << 40);
- atomic64_set(&key->conf.tx_pn, pn64);
- break;
- default:
- WARN_ON(1);
- break;
- }
-}
-EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq);
-
void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
int tid, struct ieee80211_key_seq *seq)
{
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 9951ef0..4aa20ce 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -44,13 +44,17 @@ enum ieee80211_internal_tkip_state {
};
struct tkip_ctx {
- u32 iv32; /* current iv32 */
- u16 iv16; /* current iv16 */
u16 p1k[5]; /* p1k cache */
u32 p1k_iv32; /* iv32 for which p1k computed */
enum ieee80211_internal_tkip_state state;
};
+struct tkip_ctx_rx {
+ struct tkip_ctx ctx;
+ u32 iv32; /* current iv32 */
+ u16 iv16; /* current iv16 */
+};
+
struct ieee80211_key {
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
@@ -71,7 +75,7 @@ struct ieee80211_key {
struct tkip_ctx tx;
/* last received RSC */
- struct tkip_ctx rx[IEEE80211_NUM_TIDS];
+ struct tkip_ctx_rx rx[IEEE80211_NUM_TIDS];
/* number of mic failures */
u32 mic_failures;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 6f85b6a..d32cefc 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -91,11 +91,10 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.bss_conf.basic_rates != basic_rates)
return false;
- ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
- ie->ht_operation, &sta_chan_def);
-
- ieee80211_vht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
- ie->vht_operation, &sta_chan_def);
+ cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan,
+ NL80211_CHAN_NO_HT);
+ ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def);
+ ieee80211_chandef_vht_oper(ie->vht_operation, &sta_chan_def);
if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
&sta_chan_def))
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 4a8019f..87c017a 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -137,8 +137,6 @@ struct mesh_path {
* @copy_node: function to copy nodes of the table
* @size_order: determines size of the table, there will be 2^size_order hash
* buckets
- * @mean_chain_len: maximum average length for the hash buckets' list, if it is
- * reached, the table will grow
* @known_gates: list of known mesh gates and their mpaths by the station. The
* gate's mpath may or may not be resolved and active.
*
@@ -154,7 +152,6 @@ struct mesh_table {
void (*free_node) (struct hlist_node *p, bool free_leafs);
int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
int size_order;
- int mean_chain_len;
struct hlist_head *known_gates;
spinlock_t gates_lock;
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index c6be0b4..5b6aec1 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -205,9 +205,9 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- skb_set_mac_header(skb, 0);
- skb_set_network_header(skb, 0);
- skb_set_transport_header(skb, 0);
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index dadf8dc..2ba7aa5 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -55,16 +55,21 @@ int mpp_paths_generation;
static DEFINE_RWLOCK(pathtbl_resize_lock);
+static inline struct mesh_table *resize_dereference_paths(
+ struct mesh_table __rcu *table)
+{
+ return rcu_dereference_protected(table,
+ lockdep_is_held(&pathtbl_resize_lock));
+}
+
static inline struct mesh_table *resize_dereference_mesh_paths(void)
{
- return rcu_dereference_protected(mesh_paths,
- lockdep_is_held(&pathtbl_resize_lock));
+ return resize_dereference_paths(mesh_paths);
}
static inline struct mesh_table *resize_dereference_mpp_paths(void)
{
- return rcu_dereference_protected(mpp_paths,
- lockdep_is_held(&pathtbl_resize_lock));
+ return resize_dereference_paths(mpp_paths);
}
/*
@@ -160,11 +165,10 @@ static int mesh_table_grow(struct mesh_table *oldtbl,
int i;
if (atomic_read(&oldtbl->entries)
- < oldtbl->mean_chain_len * (oldtbl->hash_mask + 1))
+ < MEAN_CHAIN_LEN * (oldtbl->hash_mask + 1))
return -EAGAIN;
newtbl->free_node = oldtbl->free_node;
- newtbl->mean_chain_len = oldtbl->mean_chain_len;
newtbl->copy_node = oldtbl->copy_node;
newtbl->known_gates = oldtbl->known_gates;
atomic_set(&newtbl->entries, atomic_read(&oldtbl->entries));
@@ -585,7 +589,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
hlist_add_head_rcu(&new_node->list, bucket);
if (atomic_inc_return(&tbl->entries) >=
- tbl->mean_chain_len * (tbl->hash_mask + 1))
+ MEAN_CHAIN_LEN * (tbl->hash_mask + 1))
grow = 1;
mesh_paths_generation++;
@@ -714,7 +718,7 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
hlist_add_head_rcu(&new_node->list, bucket);
if (atomic_inc_return(&tbl->entries) >=
- tbl->mean_chain_len * (tbl->hash_mask + 1))
+ MEAN_CHAIN_LEN * (tbl->hash_mask + 1))
grow = 1;
spin_unlock(&tbl->hashwlock[hash_idx]);
@@ -835,6 +839,29 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta)
rcu_read_unlock();
}
+static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata,
+ const u8 *proxy)
+{
+ struct mesh_table *tbl;
+ struct mesh_path *mpp;
+ struct mpath_node *node;
+ int i;
+
+ rcu_read_lock();
+ read_lock_bh(&pathtbl_resize_lock);
+ tbl = resize_dereference_mpp_paths();
+ for_each_mesh_entry(tbl, node, i) {
+ mpp = node->mpath;
+ if (ether_addr_equal(mpp->mpp, proxy)) {
+ spin_lock(&tbl->hashwlock[i]);
+ __mesh_path_del(tbl, node);
+ spin_unlock(&tbl->hashwlock[i]);
+ }
+ }
+ read_unlock_bh(&pathtbl_resize_lock);
+ rcu_read_unlock();
+}
+
static void table_flush_by_iface(struct mesh_table *tbl,
struct ieee80211_sub_if_data *sdata)
{
@@ -876,14 +903,17 @@ void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata)
}
/**
- * mesh_path_del - delete a mesh path from the table
+ * table_path_del - delete a path from the mesh or mpp table
*
- * @addr: dst address (ETH_ALEN length)
+ * @tbl: mesh or mpp path table
* @sdata: local subif
+ * @addr: dst address (ETH_ALEN length)
*
* Returns: 0 if successful
*/
-int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+static int table_path_del(struct mesh_table __rcu *rcu_tbl,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *addr)
{
struct mesh_table *tbl;
struct mesh_path *mpath;
@@ -892,8 +922,7 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
int hash_idx;
int err = 0;
- read_lock_bh(&pathtbl_resize_lock);
- tbl = resize_dereference_mesh_paths();
+ tbl = resize_dereference_paths(rcu_tbl);
hash_idx = mesh_table_hash(addr, sdata, tbl);
bucket = &tbl->hash_buckets[hash_idx];
@@ -909,9 +938,50 @@ int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
err = -ENXIO;
enddel:
- mesh_paths_generation++;
spin_unlock(&tbl->hashwlock[hash_idx]);
+ return err;
+}
+
+/**
+ * mesh_path_del - delete a mesh path from the table
+ *
+ * @addr: dst address (ETH_ALEN length)
+ * @sdata: local subif
+ *
+ * Returns: 0 if successful
+ */
+int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+{
+ int err = 0;
+
+ /* flush relevant mpp entries first */
+ mpp_flush_by_proxy(sdata, addr);
+
+ read_lock_bh(&pathtbl_resize_lock);
+ err = table_path_del(mesh_paths, sdata, addr);
+ mesh_paths_generation++;
read_unlock_bh(&pathtbl_resize_lock);
+
+ return err;
+}
+
+/**
+ * mpp_path_del - delete a mesh proxy path from the table
+ *
+ * @addr: addr address (ETH_ALEN length)
+ * @sdata: local subif
+ *
+ * Returns: 0 if successful
+ */
+static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+{
+ int err = 0;
+
+ read_lock_bh(&pathtbl_resize_lock);
+ err = table_path_del(mpp_paths, sdata, addr);
+ mpp_paths_generation++;
+ read_unlock_bh(&pathtbl_resize_lock);
+
return err;
}
@@ -1076,7 +1146,6 @@ int mesh_pathtbl_init(void)
return -ENOMEM;
tbl_path->free_node = &mesh_path_node_free;
tbl_path->copy_node = &mesh_path_node_copy;
- tbl_path->mean_chain_len = MEAN_CHAIN_LEN;
tbl_path->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC);
if (!tbl_path->known_gates) {
ret = -ENOMEM;
@@ -1092,7 +1161,6 @@ int mesh_pathtbl_init(void)
}
tbl_mpp->free_node = &mesh_path_node_free;
tbl_mpp->copy_node = &mesh_path_node_copy;
- tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN;
tbl_mpp->known_gates = kzalloc(sizeof(struct hlist_head), GFP_ATOMIC);
if (!tbl_mpp->known_gates) {
ret = -ENOMEM;
@@ -1131,6 +1199,17 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
mesh_path_del(mpath->sdata, mpath->dst);
}
+
+ tbl = rcu_dereference(mpp_paths);
+ for_each_mesh_entry(tbl, node, i) {
+ if (node->mpath->sdata != sdata)
+ continue;
+ mpath = node->mpath;
+ if ((!(mpath->flags & MESH_PATH_FIXED)) &&
+ time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
+ mpp_path_del(mpath->sdata, mpath->dst);
+ }
+
rcu_read_unlock();
}
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index bd3d55e..a07e93c 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -976,6 +976,10 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
goto out;
}
+
+ /* new matching peer */
+ event = OPN_ACPT;
+ goto out;
} else {
if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
@@ -985,12 +989,6 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
goto out;
}
- /* new matching peer */
- if (!sta) {
- event = OPN_ACPT;
- goto out;
- }
-
switch (ftype) {
case WLAN_SP_MESH_PEERING_OPEN:
if (!matches_local)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index bfbb1ac..281b8d6 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -6,7 +6,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright (C) 2015 Intel Deutschland GmbH
+ * Copyright (C) 2015 - 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -196,16 +196,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
/* check 40 MHz support, if we have it */
if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
- switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- chandef->width = NL80211_CHAN_WIDTH_40;
- chandef->center_freq1 += 10;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- chandef->width = NL80211_CHAN_WIDTH_40;
- chandef->center_freq1 -= 10;
- break;
- }
+ ieee80211_chandef_ht_oper(ht_oper, chandef);
} else {
/* 40 MHz (and 80 MHz) must be supported for VHT */
ret = IEEE80211_STA_DISABLE_VHT;
@@ -219,35 +210,11 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
goto out;
}
- vht_chandef.chan = channel;
- vht_chandef.center_freq1 =
- ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
- channel->band);
- vht_chandef.center_freq2 = 0;
-
- switch (vht_oper->chan_width) {
- case IEEE80211_VHT_CHANWIDTH_USE_HT:
- vht_chandef.width = chandef->width;
- vht_chandef.center_freq1 = chandef->center_freq1;
- break;
- case IEEE80211_VHT_CHANWIDTH_80MHZ:
- vht_chandef.width = NL80211_CHAN_WIDTH_80;
- break;
- case IEEE80211_VHT_CHANWIDTH_160MHZ:
- vht_chandef.width = NL80211_CHAN_WIDTH_160;
- break;
- case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
- vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
- vht_chandef.center_freq2 =
- ieee80211_channel_to_frequency(
- vht_oper->center_freq_seg2_idx,
- channel->band);
- break;
- default:
+ vht_chandef = *chandef;
+ if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) {
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
sdata_info(sdata,
- "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
- vht_oper->chan_width);
+ "AP VHT information is invalid, disable VHT\n");
ret = IEEE80211_STA_DISABLE_VHT;
goto out;
}
@@ -592,7 +559,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sub_if_data *other;
list_for_each_entry_rcu(other, &local->interfaces, list) {
- if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) {
+ if (other->vif.mu_mimo_owner) {
disable_mu_mimo = true;
break;
}
@@ -600,7 +567,7 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
if (disable_mu_mimo)
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
else
- sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER;
+ sdata->vif.mu_mimo_owner = true;
}
mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
@@ -1638,8 +1605,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
void ieee80211_dfs_cac_timer_work(struct work_struct *work)
{
- struct delayed_work *delayed_work =
- container_of(work, struct delayed_work, work);
+ struct delayed_work *delayed_work = to_delayed_work(work);
struct ieee80211_sub_if_data *sdata =
container_of(delayed_work, struct ieee80211_sub_if_data,
dfs_cac_timer_work);
@@ -2079,7 +2045,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
- sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
+
+ /* reset MU-MIMO ownership and group data */
+ memset(sdata->vif.bss_conf.mu_group.membership, 0,
+ sizeof(sdata->vif.bss_conf.mu_group.membership));
+ memset(sdata->vif.bss_conf.mu_group.position, 0,
+ sizeof(sdata->vif.bss_conf.mu_group.position));
+ changed |= BSS_CHANGED_MU_GROUPS;
+ sdata->vif.mu_mimo_owner = false;
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
@@ -2536,7 +2509,8 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0;
- sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
+ sdata->vif.mu_mimo_owner = false;
+
mutex_lock(&sdata->local->mtx);
ieee80211_vif_release_channel(sdata);
mutex_unlock(&sdata->local->mtx);
@@ -3571,6 +3545,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
elems.ht_cap_elem, elems.ht_operation,
elems.vht_operation, bssid, &changed)) {
mutex_unlock(&local->sta_mtx);
+ sdata_info(sdata,
+ "failed to follow AP %pM bandwidth change, disconnect\n",
+ bssid);
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DEAUTH_LEAVING,
true, deauth_buf);
@@ -3946,11 +3923,9 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
* We actually lost the connection ... or did we?
* Let's make sure!
*/
- wiphy_debug(local->hw.wiphy,
- "%s: No probe response from AP %pM"
- " after %dms, disconnecting.\n",
- sdata->name,
- bssid, probe_wait_ms);
+ mlme_dbg(sdata,
+ "No probe response from AP %pM after %dms, disconnecting.\n",
+ bssid, probe_wait_ms);
ieee80211_sta_connection_lost(sdata, bssid,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
@@ -4536,6 +4511,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
if (ifmgd->associated) {
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+ sdata_info(sdata,
+ "disconnect from AP %pM for new auth to %pM\n",
+ ifmgd->associated->bssid, req->bss->bssid);
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
@@ -4604,6 +4582,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (ifmgd->associated) {
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+ sdata_info(sdata,
+ "disconnect from AP %pM for new assoc to %pM\n",
+ ifmgd->associated->bssid, req->bss->bssid);
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 60d093f..dc27bec 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4,6 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -18,6 +19,7 @@
#include <linux/etherdevice.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
+#include <linux/bitops.h>
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h>
@@ -122,7 +124,8 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
hdr = (void *)(skb->data + rtap_vendor_space);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
- RX_FLAG_FAILED_PLCP_CRC))
+ RX_FLAG_FAILED_PLCP_CRC |
+ RX_FLAG_ONLY_MONITOR))
return true;
if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
@@ -507,7 +510,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return NULL;
}
- if (!local->monitors) {
+ if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) {
if (should_drop_frame(origskb, present_fcs_len,
rtap_vendor_space)) {
dev_kfree_skb(origskb);
@@ -797,6 +800,26 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
+static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx,
+ int index)
+{
+ struct sk_buff_head *frames = &tid_agg_rx->reorder_buf[index];
+ struct sk_buff *tail = skb_peek_tail(frames);
+ struct ieee80211_rx_status *status;
+
+ if (tid_agg_rx->reorder_buf_filtered & BIT_ULL(index))
+ return true;
+
+ if (!tail)
+ return false;
+
+ status = IEEE80211_SKB_RXCB(tail);
+ if (status->flag & RX_FLAG_AMSDU_MORE)
+ return false;
+
+ return true;
+}
+
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
int index,
@@ -811,7 +834,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
if (skb_queue_empty(skb_list))
goto no_frame;
- if (!ieee80211_rx_reorder_ready(skb_list)) {
+ if (!ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
__skb_queue_purge(skb_list);
goto no_frame;
}
@@ -825,6 +848,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
}
no_frame:
+ tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
}
@@ -865,7 +889,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
/* release the buffer until next missing frame */
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
- if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
+ if (!ieee80211_rx_reorder_ready(tid_agg_rx, index) &&
tid_agg_rx->stored_mpdu_num) {
/*
* No buffers ready to be released, but check whether any
@@ -874,8 +898,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
int skipped = 1;
for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
j = (j + 1) % tid_agg_rx->buf_size) {
- if (!ieee80211_rx_reorder_ready(
- &tid_agg_rx->reorder_buf[j])) {
+ if (!ieee80211_rx_reorder_ready(tid_agg_rx, j)) {
skipped++;
continue;
}
@@ -902,8 +925,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
skipped) & IEEE80211_SN_MASK;
skipped = 0;
}
- } else while (ieee80211_rx_reorder_ready(
- &tid_agg_rx->reorder_buf[index])) {
+ } else while (ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
frames);
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
@@ -914,8 +936,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
for (; j != (index - 1) % tid_agg_rx->buf_size;
j = (j + 1) % tid_agg_rx->buf_size) {
- if (ieee80211_rx_reorder_ready(
- &tid_agg_rx->reorder_buf[j]))
+ if (ieee80211_rx_reorder_ready(tid_agg_rx, j))
break;
}
@@ -986,7 +1007,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
index = mpdu_seq_num % tid_agg_rx->buf_size;
/* check if we already stored this frame */
- if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
+ if (ieee80211_rx_reorder_ready(tid_agg_rx, index)) {
dev_kfree_skb(skb);
goto out;
}
@@ -1099,6 +1120,9 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+ if (status->flag & RX_FLAG_DUP_VALIDATED)
+ return RX_CONTINUE;
+
/*
* Drop duplicate 802.11 retransmissions
* (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
@@ -2217,9 +2241,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
skb->dev = dev;
__skb_queue_head_init(&frame_list);
- if (skb_linearize(skb))
- return RX_DROP_UNUSABLE;
-
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type,
rx->local->hw.extra_tx_headroom, true);
@@ -2249,7 +2270,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u16 q, hdrlen;
+ u16 ac, q, hdrlen;
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -2308,6 +2329,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
spin_lock_bh(&mppath->state_lock);
if (!ether_addr_equal(mppath->mpp, mpp_addr))
memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
+ mppath->exp_time = jiffies;
spin_unlock_bh(&mppath->state_lock);
}
rcu_read_unlock();
@@ -2318,7 +2340,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
ether_addr_equal(sdata->vif.addr, hdr->addr3))
return RX_CONTINUE;
- q = ieee80211_select_queue_80211(sdata, skb, hdr);
+ ac = ieee80211_select_queue_80211(sdata, skb, hdr);
+ q = sdata->vif.hw_queue[ac];
if (ieee80211_queue_stopped(&local->hw, q)) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
return RX_DROP_MONITOR;
@@ -2756,6 +2779,11 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
opmode, status->band);
goto handled;
}
+ case WLAN_VHT_ACTION_GROUPID_MGMT: {
+ if (len < IEEE80211_MIN_ACTION_SIZE + 25)
+ goto invalid;
+ goto queue;
+ }
default:
break;
}
@@ -3091,7 +3119,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom,
false);
- skb_set_mac_header(skb, 0);
+ skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
@@ -3293,6 +3321,85 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
ieee80211_rx_handlers(&rx, &frames);
}
+void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
+ u16 ssn, u64 filtered,
+ u16 received_mpdus)
+{
+ struct sta_info *sta;
+ struct tid_ampdu_rx *tid_agg_rx;
+ struct sk_buff_head frames;
+ struct ieee80211_rx_data rx = {
+ /* This is OK -- must be QoS data frame */
+ .security_idx = tid,
+ .seqno_idx = tid,
+ };
+ int i, diff;
+
+ if (WARN_ON(!pubsta || tid >= IEEE80211_NUM_TIDS))
+ return;
+
+ __skb_queue_head_init(&frames);
+
+ sta = container_of(pubsta, struct sta_info, sta);
+
+ rx.sta = sta;
+ rx.sdata = sta->sdata;
+ rx.local = sta->local;
+
+ rcu_read_lock();
+ tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
+ if (!tid_agg_rx)
+ goto out;
+
+ spin_lock_bh(&tid_agg_rx->reorder_lock);
+
+ if (received_mpdus >= IEEE80211_SN_MODULO >> 1) {
+ int release;
+
+ /* release all frames in the reorder buffer */
+ release = (tid_agg_rx->head_seq_num + tid_agg_rx->buf_size) %
+ IEEE80211_SN_MODULO;
+ ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx,
+ release, &frames);
+ /* update ssn to match received ssn */
+ tid_agg_rx->head_seq_num = ssn;
+ } else {
+ ieee80211_release_reorder_frames(sta->sdata, tid_agg_rx, ssn,
+ &frames);
+ }
+
+ /* handle the case that received ssn is behind the mac ssn.
+ * it can be tid_agg_rx->buf_size behind and still be valid */
+ diff = (tid_agg_rx->head_seq_num - ssn) & IEEE80211_SN_MASK;
+ if (diff >= tid_agg_rx->buf_size) {
+ tid_agg_rx->reorder_buf_filtered = 0;
+ goto release;
+ }
+ filtered = filtered >> diff;
+ ssn += diff;
+
+ /* update bitmap */
+ for (i = 0; i < tid_agg_rx->buf_size; i++) {
+ int index = (ssn + i) % tid_agg_rx->buf_size;
+
+ tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
+ if (filtered & BIT_ULL(i))
+ tid_agg_rx->reorder_buf_filtered |= BIT_ULL(index);
+ }
+
+ /* now process also frames that the filter marking released */
+ ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames);
+
+release:
+ spin_unlock_bh(&tid_agg_rx->reorder_lock);
+
+ ieee80211_rx_handlers(&rx, &frames);
+
+ out:
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee80211_mark_rx_ba_filtered_frames);
+
/* main receive path */
static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a4a4f89..d20bab5 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -116,6 +116,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+ txqi->byte_cnt = 0;
}
}
@@ -498,11 +499,17 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- struct station_info sinfo;
+ struct station_info *sinfo;
int err = 0;
lockdep_assert_held(&local->sta_mtx);
+ sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
+ if (!sinfo) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
/* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) {
err = -EEXIST;
@@ -530,14 +537,12 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
/* accept BA sessions now */
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
- ieee80211_recalc_min_chandef(sdata);
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.filled = 0;
- sinfo.generation = local->sta_generation;
- cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+ sinfo->generation = local->sta_generation;
+ cfg80211_new_sta(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
+ kfree(sinfo);
sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr);
@@ -557,6 +562,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
__cleanup_single_sta(sta);
out_err:
mutex_unlock(&local->sta_mtx);
+ kfree(sinfo);
rcu_read_lock();
return err;
}
@@ -898,7 +904,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
- struct station_info sinfo = {};
+ struct station_info *sinfo;
int ret;
/*
@@ -936,12 +942,14 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
- sta_set_sinfo(sta, &sinfo);
- cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+ sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+ if (sinfo)
+ sta_set_sinfo(sta, sinfo);
+ cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
+ kfree(sinfo);
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
- ieee80211_recalc_min_chandef(sdata);
cleanup_single_sta(sta);
}
@@ -1808,14 +1816,17 @@ int sta_info_move_state(struct sta_info *sta,
clear_bit(WLAN_STA_AUTH, &sta->_flags);
break;
case IEEE80211_STA_AUTH:
- if (sta->sta_state == IEEE80211_STA_NONE)
+ if (sta->sta_state == IEEE80211_STA_NONE) {
set_bit(WLAN_STA_AUTH, &sta->_flags);
- else if (sta->sta_state == IEEE80211_STA_ASSOC)
+ } else if (sta->sta_state == IEEE80211_STA_ASSOC) {
clear_bit(WLAN_STA_ASSOC, &sta->_flags);
+ ieee80211_recalc_min_chandef(sta->sdata);
+ }
break;
case IEEE80211_STA_ASSOC:
if (sta->sta_state == IEEE80211_STA_AUTH) {
set_bit(WLAN_STA_ASSOC, &sta->_flags);
+ ieee80211_recalc_min_chandef(sta->sdata);
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
(sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index d605162..053f5c4 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -1,6 +1,7 @@
/*
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -167,6 +168,8 @@ struct tid_ampdu_tx {
*
* @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
* A-MSDU with individually reported subframes.
+ * @reorder_buf_filtered: bitmap indicating where there are filtered frames in
+ * the reorder buffer that should be ignored when releasing frames
* @reorder_time: jiffies when skb was added
* @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
* @reorder_timer: releases expired frames from the reorder buffer.
@@ -194,6 +197,7 @@ struct tid_ampdu_tx {
struct tid_ampdu_rx {
struct rcu_head rcu_head;
spinlock_t reorder_lock;
+ u64 reorder_buf_filtered;
struct sk_buff_head *reorder_buf;
unsigned long *reorder_time;
struct timer_list session_timer;
@@ -212,20 +216,21 @@ struct tid_ampdu_rx {
/**
* struct sta_ampdu_mlme - STA aggregation information.
*
+ * @mtx: mutex to protect all TX data (except non-NULL assignments
+ * to tid_tx[idx], which are protected by the sta spinlock)
+ * tid_start_tx is also protected by sta->lock.
* @tid_rx: aggregation info for Rx per TID -- RCU protected
- * @tid_tx: aggregation info for Tx per TID
- * @tid_start_tx: sessions where start was requested
- * @addba_req_num: number of times addBA request has been sent.
- * @last_addba_req_time: timestamp of the last addBA request.
- * @dialog_token_allocator: dialog token enumerator for each new session;
- * @work: work struct for starting/stopping aggregation
* @tid_rx_timer_expired: bitmap indicating on which TIDs the
* RX timer expired until the work for it runs
* @tid_rx_stop_requested: bitmap indicating which BA sessions per TID the
* driver requested to close until the work for it runs
- * @mtx: mutex to protect all TX data (except non-NULL assignments
- * to tid_tx[idx], which are protected by the sta spinlock)
- * tid_start_tx is also protected by sta->lock.
+ * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
+ * @work: work struct for starting/stopping aggregation
+ * @tid_tx: aggregation info for Tx per TID
+ * @tid_start_tx: sessions where start was requested
+ * @last_addba_req_time: timestamp of the last addBA request.
+ * @addba_req_num: number of times addBA request has been sent.
+ * @dialog_token_allocator: dialog token enumerator for each new session;
*/
struct sta_ampdu_mlme {
struct mutex mtx;
@@ -233,6 +238,7 @@ struct sta_ampdu_mlme {
struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS];
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
+ unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
/* tx */
struct work_struct work;
struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 6101deb..8b1b2ea 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -697,7 +697,7 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
rtap_len, shift);
/* XXX: is this sufficient for BPF? */
- skb_set_mac_header(skb, 0);
+ skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 0ae2077..b362282 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -1,6 +1,7 @@
/*
* Copyright 2002-2004, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
+ * Copyright (C) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -142,15 +143,14 @@ static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
/* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets
* of the IV. Returns pointer to the octet following IVs (i.e., beginning of
* the packet payload). */
-u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key)
+u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key_conf *keyconf, u64 pn)
{
- lockdep_assert_held(&key->u.tkip.txlock);
-
- pos = write_tkip_iv(pos, key->u.tkip.tx.iv16);
- *pos++ = (key->conf.keyidx << 6) | (1 << 5) /* Ext IV */;
- put_unaligned_le32(key->u.tkip.tx.iv32, pos);
+ pos = write_tkip_iv(pos, TKIP_PN_TO_IV16(pn));
+ *pos++ = (keyconf->keyidx << 6) | (1 << 5) /* Ext IV */;
+ put_unaligned_le32(TKIP_PN_TO_IV32(pn), pos);
return pos + 4;
}
+EXPORT_SYMBOL_GPL(ieee80211_tkip_add_iv);
static void ieee80211_compute_tkip_p1k(struct ieee80211_key *key, u32 iv32)
{
@@ -250,6 +250,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm,
u8 rc4key[16], keyid, *pos = payload;
int res;
const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
+ struct tkip_ctx_rx *rx_ctx = &key->u.tkip.rx[queue];
if (payload_len < 12)
return -1;
@@ -265,37 +266,36 @@ int ieee80211_tkip_decrypt_data(struct crypto_cipher *tfm,
if ((keyid >> 6) != key->conf.keyidx)
return TKIP_DECRYPT_INVALID_KEYIDX;
- if (key->u.tkip.rx[queue].state != TKIP_STATE_NOT_INIT &&
- (iv32 < key->u.tkip.rx[queue].iv32 ||
- (iv32 == key->u.tkip.rx[queue].iv32 &&
- iv16 <= key->u.tkip.rx[queue].iv16)))
+ if (rx_ctx->ctx.state != TKIP_STATE_NOT_INIT &&
+ (iv32 < rx_ctx->iv32 ||
+ (iv32 == rx_ctx->iv32 && iv16 <= rx_ctx->iv16)))
return TKIP_DECRYPT_REPLAY;
if (only_iv) {
res = TKIP_DECRYPT_OK;
- key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED;
+ rx_ctx->ctx.state = TKIP_STATE_PHASE1_HW_UPLOADED;
goto done;
}
- if (key->u.tkip.rx[queue].state == TKIP_STATE_NOT_INIT ||
- key->u.tkip.rx[queue].iv32 != iv32) {
+ if (rx_ctx->ctx.state == TKIP_STATE_NOT_INIT ||
+ rx_ctx->iv32 != iv32) {
/* IV16 wrapped around - perform TKIP phase 1 */
- tkip_mixing_phase1(tk, &key->u.tkip.rx[queue], ta, iv32);
+ tkip_mixing_phase1(tk, &rx_ctx->ctx, ta, iv32);
}
if (key->local->ops->update_tkip_key &&
key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
- key->u.tkip.rx[queue].state != TKIP_STATE_PHASE1_HW_UPLOADED) {
+ rx_ctx->ctx.state != TKIP_STATE_PHASE1_HW_UPLOADED) {
struct ieee80211_sub_if_data *sdata = key->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(key->sdata->bss,
struct ieee80211_sub_if_data, u.ap);
drv_update_tkip_key(key->local, sdata, &key->conf, key->sta,
- iv32, key->u.tkip.rx[queue].p1k);
- key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED;
+ iv32, rx_ctx->ctx.p1k);
+ rx_ctx->ctx.state = TKIP_STATE_PHASE1_HW_UPLOADED;
}
- tkip_mixing_phase2(tk, &key->u.tkip.rx[queue], iv16, rc4key);
+ tkip_mixing_phase2(tk, &rx_ctx->ctx, iv16, rc4key);
res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12);
done:
diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h
index e3ecb65..a1bcbfb 100644
--- a/net/mac80211/tkip.h
+++ b/net/mac80211/tkip.h
@@ -13,8 +13,6 @@
#include <linux/crypto.h>
#include "key.h"
-u8 *ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key);
-
int ieee80211_tkip_encrypt_data(struct crypto_cipher *tfm,
struct ieee80211_key *key,
struct sk_buff *skb,
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index a6b4442..2b0a17e 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -80,7 +80,23 @@
#define KEY_PR_FMT " cipher:0x%x, flags=%#x, keyidx=%d, hw_key_idx=%d"
#define KEY_PR_ARG __entry->cipher, __entry->flags, __entry->keyidx, __entry->hw_key_idx
-
+#define AMPDU_ACTION_ENTRY __field(enum ieee80211_ampdu_mlme_action, \
+ ieee80211_ampdu_mlme_action) \
+ STA_ENTRY \
+ __field(u16, tid) \
+ __field(u16, ssn) \
+ __field(u8, buf_size) \
+ __field(bool, amsdu) \
+ __field(u16, timeout)
+#define AMPDU_ACTION_ASSIGN STA_NAMED_ASSIGN(params->sta); \
+ __entry->tid = params->tid; \
+ __entry->ssn = params->ssn; \
+ __entry->buf_size = params->buf_size; \
+ __entry->amsdu = params->amsdu; \
+ __entry->timeout = params->timeout;
+#define AMPDU_ACTION_PR_FMT STA_PR_FMT " tid %d, ssn %d, buf_size %u, amsdu %d, timeout %d"
+#define AMPDU_ACTION_PR_ARG STA_PR_ARG, __entry->tid, __entry->ssn, \
+ __entry->buf_size, __entry->amsdu, __entry->timeout
/*
* Tracing for driver callbacks.
@@ -970,38 +986,25 @@ DEFINE_EVENT(local_only_evt, drv_tx_last_beacon,
TRACE_EVENT(drv_ampdu_action,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid,
- u16 *ssn, u8 buf_size, bool amsdu),
+ struct ieee80211_ampdu_params *params),
- TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size, amsdu),
+ TP_ARGS(local, sdata, params),
TP_STRUCT__entry(
LOCAL_ENTRY
- STA_ENTRY
- __field(u32, action)
- __field(u16, tid)
- __field(u16, ssn)
- __field(u8, buf_size)
- __field(bool, amsdu)
VIF_ENTRY
+ AMPDU_ACTION_ENTRY
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
- STA_ASSIGN;
- __entry->action = action;
- __entry->tid = tid;
- __entry->ssn = ssn ? *ssn : 0;
- __entry->buf_size = buf_size;
- __entry->amsdu = amsdu;
+ AMPDU_ACTION_ASSIGN;
),
TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d amsdu:%d",
- LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action,
- __entry->tid, __entry->buf_size, __entry->amsdu
+ LOCAL_PR_FMT VIF_PR_FMT AMPDU_ACTION_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, AMPDU_ACTION_PR_ARG
)
);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3311ce0..62ad532 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
info->control.short_preamble = txrc.short_preamble;
+ /* don't ask rate control when rate already injected via radiotap */
+ if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)
+ return TX_CONTINUE;
+
if (tx->sta)
assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
@@ -1266,7 +1270,11 @@ static void ieee80211_drv_tx(struct ieee80211_local *local,
if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending)
netif_stop_subqueue(sdata->dev, ac);
- skb_queue_tail(&txqi->queue, skb);
+ spin_lock_bh(&txqi->queue.lock);
+ txqi->byte_cnt += skb->len;
+ __skb_queue_tail(&txqi->queue, skb);
+ spin_unlock_bh(&txqi->queue.lock);
+
drv_wake_tx_queue(local, txqi);
return;
@@ -1294,6 +1302,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
if (!skb)
goto out;
+ txqi->byte_cnt -= skb->len;
+
atomic_dec(&sdata->txqs_len[ac]);
if (__netif_subqueue_stopped(sdata->dev, ac))
ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]);
@@ -1665,15 +1675,24 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
ieee80211_tx(sdata, sta, skb, false);
}
-static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
+static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
+ struct sk_buff *skb)
{
struct ieee80211_radiotap_iterator iterator;
struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_supported_band *sband =
+ local->hw.wiphy->bands[info->band];
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
NULL);
u16 txflags;
+ u16 rate = 0;
+ bool rate_found = false;
+ u8 rate_retries = 0;
+ u16 rate_flags = 0;
+ u8 mcs_known, mcs_flags;
+ int i;
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_DONTFRAG;
@@ -1724,6 +1743,35 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
info->flags |= IEEE80211_TX_CTL_NO_ACK;
break;
+ case IEEE80211_RADIOTAP_RATE:
+ rate = *iterator.this_arg;
+ rate_flags = 0;
+ rate_found = true;
+ break;
+
+ case IEEE80211_RADIOTAP_DATA_RETRIES:
+ rate_retries = *iterator.this_arg;
+ break;
+
+ case IEEE80211_RADIOTAP_MCS:
+ mcs_known = iterator.this_arg[0];
+ mcs_flags = iterator.this_arg[1];
+ if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS))
+ break;
+
+ rate_found = true;
+ rate = iterator.this_arg[2];
+ rate_flags = IEEE80211_TX_RC_MCS;
+
+ if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI &&
+ mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
+ rate_flags |= IEEE80211_TX_RC_SHORT_GI;
+
+ if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
+ mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
+ rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ break;
+
/*
* Please update the file
* Documentation/networking/mac80211-injection.txt
@@ -1738,6 +1786,32 @@ static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
return false;
+ if (rate_found) {
+ info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->control.rates[i].idx = -1;
+ info->control.rates[i].flags = 0;
+ info->control.rates[i].count = 0;
+ }
+
+ if (rate_flags & IEEE80211_TX_RC_MCS) {
+ info->control.rates[0].idx = rate;
+ } else {
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (rate * 5 != sband->bitrates[i].bitrate)
+ continue;
+
+ info->control.rates[0].idx = i;
+ break;
+ }
+ }
+
+ info->control.rates[0].flags = rate_flags;
+ info->control.rates[0].count = min_t(u8, rate_retries + 1,
+ local->hw.max_rate_tries);
+ }
+
/*
* remove the radiotap header
* iterator->_max_length was sanity-checked against
@@ -1818,10 +1892,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
IEEE80211_TX_CTL_INJECTED;
- /* process and remove the injection radiotap header */
- if (!ieee80211_parse_tx_radiotap(skb))
- goto fail;
-
rcu_read_lock();
/*
@@ -1883,6 +1953,11 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
goto fail_rcu;
info->band = chandef->chan->band;
+
+ /* process and remove the injection radiotap header */
+ if (!ieee80211_parse_tx_radiotap(local, skb))
+ goto fail_rcu;
+
ieee80211_xmit(sdata, NULL, skb);
rcu_read_unlock();
@@ -2099,8 +2174,11 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
mpp_lookup = true;
}
- if (mpp_lookup)
+ if (mpp_lookup) {
mppath = mpp_path_lookup(sdata, skb->data);
+ if (mppath)
+ mppath->exp_time = jiffies;
+ }
if (mppath && mpath)
mesh_path_del(mpath->sdata, mpath->dst);
@@ -2380,7 +2458,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
/* Update skb pointers to various headers since this modified frame
* is going to go through Linux networking code that may potentially
* need things like pointer to IP header. */
- skb_set_mac_header(skb, 0);
+ skb_reset_mac_header(skb);
skb_set_network_header(skb, nh_pos);
skb_set_transport_header(skb, h_pos);
@@ -3895,9 +3973,9 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
{
int ac = ieee802_1d_to_ac[tid & 7];
- skb_set_mac_header(skb, 0);
- skb_set_network_header(skb, 0);
- skb_set_transport_header(skb, 0);
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
skb_set_queue_mapping(skb, ac);
skb->priority = tid;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 58f58bd..7390de4 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -4,7 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright (C) 2015 Intel Deutschland GmbH
+ * Copyright (C) 2015-2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -1928,6 +1928,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_IDLE |
BSS_CHANGED_TXPOWER;
+ if (sdata->vif.mu_mimo_owner)
+ changed |= BSS_CHANGED_MU_GROUPS;
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
changed |= BSS_CHANGED_ASSOC |
@@ -2371,10 +2374,23 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
switch (chandef->width) {
case NL80211_CHAN_WIDTH_160:
- vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
+ /*
+ * Convert 160 MHz channel width to new style as interop
+ * workaround.
+ */
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
+ vht_oper->center_freq_seg2_idx = vht_oper->center_freq_seg1_idx;
+ if (chandef->chan->center_freq < chandef->center_freq1)
+ vht_oper->center_freq_seg1_idx -= 8;
+ else
+ vht_oper->center_freq_seg1_idx += 8;
break;
case NL80211_CHAN_WIDTH_80P80:
- vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+ /*
+ * Convert 80+80 MHz channel width to new style as interop
+ * workaround.
+ */
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
break;
case NL80211_CHAN_WIDTH_80:
vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
@@ -2390,17 +2406,13 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
return pos + sizeof(struct ieee80211_vht_operation);
}
-void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
- const struct ieee80211_ht_operation *ht_oper,
- struct cfg80211_chan_def *chandef)
+bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef)
{
enum nl80211_channel_type channel_type;
- if (!ht_oper) {
- cfg80211_chandef_create(chandef, control_chan,
- NL80211_CHAN_NO_HT);
- return;
- }
+ if (!ht_oper)
+ return false;
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
@@ -2414,42 +2426,66 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
break;
default:
channel_type = NL80211_CHAN_NO_HT;
+ return false;
}
- cfg80211_chandef_create(chandef, control_chan, channel_type);
+ cfg80211_chandef_create(chandef, chandef->chan, channel_type);
+ return true;
}
-void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan,
- const struct ieee80211_vht_operation *oper,
- struct cfg80211_chan_def *chandef)
+bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper,
+ struct cfg80211_chan_def *chandef)
{
+ struct cfg80211_chan_def new = *chandef;
+ int cf1, cf2;
+
if (!oper)
- return;
+ return false;
- chandef->chan = control_chan;
+ cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
+ chandef->chan->band);
+ cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
+ chandef->chan->band);
switch (oper->chan_width) {
case IEEE80211_VHT_CHANWIDTH_USE_HT:
break;
case IEEE80211_VHT_CHANWIDTH_80MHZ:
- chandef->width = NL80211_CHAN_WIDTH_80;
+ new.width = NL80211_CHAN_WIDTH_80;
+ new.center_freq1 = cf1;
+ /* If needed, adjust based on the newer interop workaround. */
+ if (oper->center_freq_seg2_idx) {
+ unsigned int diff;
+
+ diff = abs(oper->center_freq_seg2_idx -
+ oper->center_freq_seg1_idx);
+ if (diff == 8) {
+ new.width = NL80211_CHAN_WIDTH_160;
+ new.center_freq1 = cf2;
+ } else if (diff > 8) {
+ new.width = NL80211_CHAN_WIDTH_80P80;
+ new.center_freq2 = cf2;
+ }
+ }
break;
case IEEE80211_VHT_CHANWIDTH_160MHZ:
- chandef->width = NL80211_CHAN_WIDTH_160;
+ new.width = NL80211_CHAN_WIDTH_160;
+ new.center_freq1 = cf1;
break;
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
- chandef->width = NL80211_CHAN_WIDTH_80P80;
+ new.width = NL80211_CHAN_WIDTH_80P80;
+ new.center_freq1 = cf1;
+ new.center_freq2 = cf2;
break;
default:
- break;
+ return false;
}
- chandef->center_freq1 =
- ieee80211_channel_to_frequency(oper->center_freq_seg1_idx,
- control_chan->band);
- chandef->center_freq2 =
- ieee80211_channel_to_frequency(oper->center_freq_seg2_idx,
- control_chan->band);
+ if (!cfg80211_chandef_valid(&new))
+ return false;
+
+ *chandef = new;
+ return true;
}
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
@@ -2672,6 +2708,18 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
sband = local->hw.wiphy->bands[status->band];
bitrate = sband->bitrates[status->rate_idx].bitrate;
ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
+
+ if (status->flag & RX_FLAG_MACTIME_PLCP_START) {
+ /* TODO: handle HT/VHT preambles */
+ if (status->band == IEEE80211_BAND_5GHZ) {
+ ts += 20 << shift;
+ mpdu_offset += 2;
+ } else if (status->flag & RX_FLAG_SHORTPRE) {
+ ts += 96;
+ } else {
+ ts += 192;
+ }
+ }
}
rate = cfg80211_calculate_bitrate(&ri);
@@ -3357,3 +3405,17 @@ void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
txqi->txq.ac = IEEE80211_AC_BE;
}
}
+
+void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
+ unsigned long *frame_cnt,
+ unsigned long *byte_cnt)
+{
+ struct txq_info *txqi = to_txq_info(txq);
+
+ if (frame_cnt)
+ *frame_cnt = txqi->queue.qlen;
+
+ if (byte_cnt)
+ *byte_cnt = txqi->byte_cnt;
+}
+EXPORT_SYMBOL(ieee80211_txq_get_depth);
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index c38b2f0..89e04d5 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -1,6 +1,9 @@
/*
* VHT handling
*
+ * Portions of this file
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -278,6 +281,23 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
}
sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
+
+ /* If HT IE reported 3839 bytes only, stay with that size. */
+ if (sta->sta.max_amsdu_len == IEEE80211_MAX_MPDU_LEN_HT_3839)
+ return;
+
+ switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
+ case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
+ sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454;
+ break;
+ case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
+ sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991;
+ break;
+ case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895:
+ default:
+ sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895;
+ break;
+ }
}
enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta)
@@ -425,6 +445,43 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
return changed;
}
+void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt)
+{
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+
+ if (!sdata->vif.mu_mimo_owner)
+ return;
+
+ if (!memcmp(mgmt->u.action.u.vht_group_notif.position,
+ bss_conf->mu_group.position, WLAN_USER_POSITION_LEN) &&
+ !memcmp(mgmt->u.action.u.vht_group_notif.membership,
+ bss_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN))
+ return;
+
+ memcpy(bss_conf->mu_group.membership,
+ mgmt->u.action.u.vht_group_notif.membership,
+ WLAN_MEMBERSHIP_LEN);
+ memcpy(bss_conf->mu_group.position,
+ mgmt->u.action.u.vht_group_notif.position,
+ WLAN_USER_POSITION_LEN);
+
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS);
+}
+
+void ieee80211_update_mu_groups(struct ieee80211_vif *vif,
+ const u8 *membership, const u8 *position)
+{
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+
+ if (WARN_ON_ONCE(!vif->mu_mimo_owner))
+ return;
+
+ memcpy(bss_conf->mu_group.membership, membership, WLAN_MEMBERSHIP_LEN);
+ memcpy(bss_conf->mu_group.position, position, WLAN_USER_POSITION_LEN);
+}
+EXPORT_SYMBOL_GPL(ieee80211_update_mu_groups);
+
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
enum ieee80211_band band)
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index d824c38..1884825 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -1,6 +1,7 @@
/*
* Copyright 2002-2004, Instant802 Networks, Inc.
* Copyright 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (C) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -183,7 +184,6 @@ mic_fail_no_key:
return RX_DROP_UNUSABLE;
}
-
static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -191,6 +191,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
unsigned int hdrlen;
int len, tail;
+ u64 pn;
u8 *pos;
if (info->control.hw_key &&
@@ -222,12 +223,8 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0;
/* Increase IV for the frame */
- spin_lock(&key->u.tkip.txlock);
- key->u.tkip.tx.iv16++;
- if (key->u.tkip.tx.iv16 == 0)
- key->u.tkip.tx.iv32++;
- pos = ieee80211_tkip_add_iv(pos, key);
- spin_unlock(&key->u.tkip.txlock);
+ pn = atomic64_inc_return(&key->conf.tx_pn);
+ pos = ieee80211_tkip_add_iv(pos, &key->conf, pn);
/* hwaccel - with software IV */
if (info->control.hw_key)