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:
David S. Miller
2016-03-01 17:02:30 -05:00
80 changed files with 1519 additions and 913 deletions

View File

@@ -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 = 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, &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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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