Merge tag 'mac80211-next-for-davem-2016-02-26' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says: ==================== Here's another round of updates for -next: * big A-MSDU RX performance improvement (avoid linearize of paged RX) * rfkill changes: cleanups, documentation, platform properties * basic PBSS support in cfg80211 * MU-MIMO action frame processing support * BlockAck reordering & duplicate detection offload support * various cleanups & little fixes ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -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, ¶ms))
|
||||
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, ¶ms);
|
||||
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 = kmalloc(sizeof(struct tid_ampdu_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, ¶ms);
|
||||
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:
|
||||
|
@@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
|
||||
/* 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, ¶ms);
|
||||
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, ¶ms);
|
||||
|
||||
/*
|
||||
* synchronize with TX path, while splicing the TX path
|
||||
|
@@ -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);
|
||||
|
@@ -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));
|
||||
|
@@ -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
|
||||
};
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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));
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
/*
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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))
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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")
|
||||
@@ -2199,9 +2223,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);
|
||||
@@ -2231,7 +2252,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);
|
||||
@@ -2290,6 +2311,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();
|
||||
@@ -2300,7 +2322,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;
|
||||
@@ -2738,6 +2761,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;
|
||||
}
|
||||
@@ -3275,6 +3303,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)
|
||||
|
@@ -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 &&
|
||||
|
@@ -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];
|
||||
|
@@ -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:
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
)
|
||||
);
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
if (!oper)
|
||||
return;
|
||||
struct cfg80211_chan_def new = *chandef;
|
||||
int cf1, cf2;
|
||||
|
||||
chandef->chan = control_chan;
|
||||
if (!oper)
|
||||
return false;
|
||||
|
||||
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);
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user