aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/agg-rx.c5
-rw-r--r--net/mac80211/agg-tx.c82
-rw-r--r--net/mac80211/cfg.c106
-rw-r--r--net/mac80211/chan.c1
-rw-r--r--net/mac80211/debugfs_netdev.c2
-rw-r--r--net/mac80211/driver-ops.h4
-rw-r--r--net/mac80211/he.c61
-rw-r--r--net/mac80211/ibss.c16
-rw-r--r--net/mac80211/ieee80211_i.h34
-rw-r--r--net/mac80211/iface.c5
-rw-r--r--net/mac80211/main.c15
-rw-r--r--net/mac80211/mesh.c55
-rw-r--r--net/mac80211/mesh.h2
-rw-r--r--net/mac80211/mesh_plink.c9
-rw-r--r--net/mac80211/mlme.c182
-rw-r--r--net/mac80211/offchannel.c4
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c3
-rw-r--r--net/mac80211/rx.c122
-rw-r--r--net/mac80211/scan.c27
-rw-r--r--net/mac80211/spectmgmt.c4
-rw-r--r--net/mac80211/sta_info.h4
-rw-r--r--net/mac80211/status.c9
-rw-r--r--net/mac80211/tdls.c9
-rw-r--r--net/mac80211/trace.h41
-rw-r--r--net/mac80211/tx.c74
-rw-r--r--net/mac80211/util.c298
-rw-r--r--net/mac80211/vht.c10
27 files changed, 947 insertions, 237 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 4d1c335e06e5..7f245e9f114c 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
* Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018-2020 Intel Corporation
*/
/**
@@ -292,7 +292,8 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
goto end;
}
- if (!sta->sta.ht_cap.ht_supported) {
+ if (!sta->sta.ht_cap.ht_supported &&
+ sta->sdata->vif.bss_conf.chandef.chan->band != NL80211_BAND_6GHZ) {
ht_dbg(sta->sdata,
"STA %pM erroneously requests BA session on tid %d w/o QoS\n",
sta->sta.addr, tid);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 33da6f738c99..b37c8a983d88 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
* Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2020 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -448,6 +448,45 @@ static void sta_addba_resp_timer_expired(struct timer_list *t)
ieee80211_stop_tx_ba_session(&sta->sta, tid);
}
+static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
+ struct tid_ampdu_tx *tid_tx)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sta->local;
+ u8 tid = tid_tx->tid;
+ u16 buf_size;
+
+ /* activate the timer for the recipient's addBA response */
+ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
+ ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
+ sta->sta.addr, tid);
+
+ spin_lock_bh(&sta->lock);
+ sta->ampdu_mlme.last_addba_req_time[tid] = jiffies;
+ sta->ampdu_mlme.addba_req_num[tid]++;
+ spin_unlock_bh(&sta->lock);
+
+ if (sta->sta.he_cap.has_he) {
+ buf_size = local->hw.max_tx_aggregation_subframes;
+ } else {
+ /*
+ * We really should use what the driver told us it will
+ * transmit as the maximum, but certain APs (e.g. the
+ * LinkSys WRT120N with FW v1.0.07 build 002 Jun 18 2012)
+ * will crash when we use a lower number.
+ */
+ buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
+ }
+
+ /* send AddBA request */
+ ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
+ tid_tx->dialog_token,
+ sta->tid_seq[tid] >> 4,
+ buf_size, tid_tx->timeout);
+
+ WARN_ON(test_and_set_bit(HT_AGG_STATE_SENT_ADDBA, &tid_tx->state));
+}
+
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
{
struct tid_ampdu_tx *tid_tx;
@@ -462,7 +501,6 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
.timeout = 0,
};
int ret;
- u16 buf_size;
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
@@ -485,7 +523,9 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
params.ssn = sta->tid_seq[tid] >> 4;
ret = drv_ampdu_action(local, sdata, &params);
- if (ret == IEEE80211_AMPDU_TX_START_IMMEDIATE) {
+ if (ret == IEEE80211_AMPDU_TX_START_DELAY_ADDBA) {
+ return;
+ } else if (ret == IEEE80211_AMPDU_TX_START_IMMEDIATE) {
/*
* We didn't send the request yet, so don't need to check
* here if we already got a response, just mark as driver
@@ -508,32 +548,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
return;
}
- /* activate the timer for the recipient's addBA response */
- mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
- ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
- sta->sta.addr, tid);
-
- spin_lock_bh(&sta->lock);
- sta->ampdu_mlme.last_addba_req_time[tid] = jiffies;
- sta->ampdu_mlme.addba_req_num[tid]++;
- spin_unlock_bh(&sta->lock);
-
- if (sta->sta.he_cap.has_he) {
- buf_size = local->hw.max_tx_aggregation_subframes;
- } else {
- /*
- * We really should use what the driver told us it will
- * transmit as the maximum, but certain APs (e.g. the
- * LinkSys WRT120N with FW v1.0.07 build 002 Jun 18 2012)
- * will crash when we use a lower number.
- */
- buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
- }
-
- /* send AddBA request */
- ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
- tid_tx->dialog_token, params.ssn,
- buf_size, tid_tx->timeout);
+ ieee80211_send_addba_with_timeout(sta, tid_tx);
}
/*
@@ -578,7 +593,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
"Requested to start BA session on reserved tid=%d", tid))
return -EINVAL;
- if (!pubsta->ht_cap.ht_supported)
+ if (!pubsta->ht_cap.ht_supported &&
+ sta->sdata->vif.bss_conf.chandef.chan->band != NL80211_BAND_6GHZ)
return -EINVAL;
if (WARN_ON_ONCE(!local->ops->ampdu_action))
@@ -754,6 +770,12 @@ void ieee80211_start_tx_ba_cb(struct sta_info *sta, int tid,
if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)))
return;
+ if (!test_bit(HT_AGG_STATE_SENT_ADDBA, &tid_tx->state)) {
+ ieee80211_send_addba_with_timeout(sta, tid_tx);
+ /* RESPONSE_RECEIVED state whould trigger the flow again */
+ return;
+ }
+
if (test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state))
ieee80211_agg_tx_operational(local, sta, tid);
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 0f72813fed53..9b360544ad6f 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -994,7 +994,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
BSS_CHANGED_TWT |
BSS_CHANGED_HE_OBSS_PD |
BSS_CHANGED_HE_BSS_COLOR;
- int err;
+ int i, err;
int prev_beacon_int;
old = sdata_dereference(sdata->u.ap.beacon, sdata);
@@ -1085,6 +1085,17 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
IEEE80211_P2P_OPPPS_ENABLE_BIT;
+ sdata->beacon_rate_set = false;
+ if (wiphy_ext_feature_isset(local->hw.wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) {
+ for (i = 0; i < NUM_NL80211_BANDS; i++) {
+ sdata->beacon_rateidx_mask[i] =
+ params->beacon_rate.control[i].legacy;
+ if (sdata->beacon_rateidx_mask[i])
+ sdata->beacon_rate_set = true;
+ }
+ }
+
err = ieee80211_assign_beacon(sdata, &params->beacon, NULL);
if (err < 0) {
ieee80211_vif_release_channel(sdata);
@@ -1189,6 +1200,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
ieee80211_free_keys(sdata, true);
sdata->vif.bss_conf.enable_beacon = false;
+ sdata->beacon_rate_set = false;
sdata->vif.bss_conf.ssid_len = 0;
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
@@ -1508,7 +1520,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (params->he_capa)
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
(void *)params->he_capa,
- params->he_capa_len, sta);
+ params->he_capa_len,
+ (void *)params->he_6ghz_capa,
+ sta);
if (params->opmode_notif_used) {
/* returned value is only needed for rc update, but the
@@ -1949,6 +1963,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
const u8 *old_ie;
struct ieee80211_sub_if_data *sdata = container_of(ifmsh,
struct ieee80211_sub_if_data, u.mesh);
+ int i;
/* allocate information elements */
new_ie = NULL;
@@ -1987,6 +2002,17 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
sdata->vif.bss_conf.dtim_period = setup->dtim_period;
+ sdata->beacon_rate_set = false;
+ if (wiphy_ext_feature_isset(sdata->local->hw.wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) {
+ for (i = 0; i < NUM_NL80211_BANDS; i++) {
+ sdata->beacon_rateidx_mask[i] =
+ setup->beacon_rate.control[i].legacy;
+ if (sdata->beacon_rateidx_mask[i])
+ sdata->beacon_rate_set = true;
+ }
+ }
+
return 0;
}
@@ -2172,7 +2198,8 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
}
if (!sdata->vif.bss_conf.use_short_slot &&
- sband->band == NL80211_BAND_5GHZ) {
+ (sband->band == NL80211_BAND_5GHZ ||
+ sband->band == NL80211_BAND_6GHZ)) {
sdata->vif.bss_conf.use_short_slot = true;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -3287,6 +3314,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
goto out;
}
+ if (params->chandef.chan->freq_offset) {
+ /* this may work, but is untested */
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
ch_switch.timestamp = 0;
@@ -3398,41 +3431,43 @@ int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
return 0;
}
-static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
+static void
+ieee80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
struct wireless_dev *wdev,
- u16 frame_type, bool reg)
+ struct mgmt_frame_regs *upd)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ u32 preq_mask = BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
+ u32 action_mask = BIT(IEEE80211_STYPE_ACTION >> 4);
+ bool global_change, intf_change;
+
+ global_change =
+ (local->probe_req_reg != !!(upd->global_stypes & preq_mask)) ||
+ (local->rx_mcast_action_reg !=
+ !!(upd->global_mcast_stypes & action_mask));
+ local->probe_req_reg = upd->global_stypes & preq_mask;
+ local->rx_mcast_action_reg = upd->global_mcast_stypes & action_mask;
+
+ intf_change = (sdata->vif.probe_req_reg !=
+ !!(upd->interface_stypes & preq_mask)) ||
+ (sdata->vif.rx_mcast_action_reg !=
+ !!(upd->interface_mcast_stypes & action_mask));
+ sdata->vif.probe_req_reg = upd->interface_stypes & preq_mask;
+ sdata->vif.rx_mcast_action_reg =
+ upd->interface_mcast_stypes & action_mask;
+
+ if (!local->open_count)
+ return;
- switch (frame_type) {
- case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
- if (reg) {
- local->probe_req_reg++;
- sdata->vif.probe_req_reg++;
- } else {
- if (local->probe_req_reg)
- local->probe_req_reg--;
-
- if (sdata->vif.probe_req_reg)
- sdata->vif.probe_req_reg--;
- }
-
- if (!local->open_count)
- break;
-
- if (sdata->vif.probe_req_reg == 1)
- drv_config_iface_filter(local, sdata, FIF_PROBE_REQ,
- FIF_PROBE_REQ);
- else if (sdata->vif.probe_req_reg == 0)
- drv_config_iface_filter(local, sdata, 0,
- FIF_PROBE_REQ);
+ if (intf_change && ieee80211_sdata_running(sdata))
+ drv_config_iface_filter(local, sdata,
+ sdata->vif.probe_req_reg ?
+ FIF_PROBE_REQ : 0,
+ FIF_PROBE_REQ);
+ if (global_change)
ieee80211_configure_filter(local);
- break;
- default:
- break;
- }
}
static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
@@ -3925,7 +3960,7 @@ static int ieee80211_set_tid_config(struct wiphy *wiphy,
static int ieee80211_reset_tid_config(struct wiphy *wiphy,
struct net_device *dev,
- const u8 *peer, u8 tid)
+ const u8 *peer, u8 tids)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
@@ -3935,7 +3970,7 @@ static int ieee80211_reset_tid_config(struct wiphy *wiphy,
return -EOPNOTSUPP;
if (!peer)
- return drv_reset_tid_config(sdata->local, sdata, NULL, tid);
+ return drv_reset_tid_config(sdata->local, sdata, NULL, tids);
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get_bss(sdata, peer);
@@ -3944,7 +3979,7 @@ static int ieee80211_reset_tid_config(struct wiphy *wiphy,
return -ENOENT;
}
- ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tid);
+ ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids);
mutex_unlock(&sdata->local->sta_mtx);
return ret;
@@ -4017,7 +4052,8 @@ const struct cfg80211_ops mac80211_config_ops = {
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
.set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config,
- .mgmt_frame_register = ieee80211_mgmt_frame_register,
+ .update_mgmt_frame_registrations =
+ ieee80211_update_mgmt_frame_registrations,
.set_antenna = ieee80211_set_antenna,
.get_antenna = ieee80211_get_antenna,
.set_rekey_data = ieee80211_set_rekey_data,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 9c94baaf693c..e6e192f53e4e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -533,6 +533,7 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
struct cfg80211_chan_def *chandef = &local->_oper_chandef;
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = chandef->chan->center_freq;
+ chandef->freq1_offset = chandef->chan->freq_offset;
chandef->center_freq2 = 0;
/* NOTE: Disabling radar is only valid here for
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 3dbe7c5cefd1..d7e955127d5c 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -236,7 +236,7 @@ IEEE80211_IF_FILE_R(hw_queues);
/* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
-IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
+IEEE80211_IF_FILE(aid, vif.bss_conf.aid, DEC);
IEEE80211_IF_FILE(beacon_timeout, u.mgd.beacon_timeout, JIFFIES_TO_MS);
static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 3877710e3b48..de69fc9c4f07 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1375,12 +1375,12 @@ static inline int drv_set_tid_config(struct ieee80211_local *local,
static inline int drv_reset_tid_config(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- struct ieee80211_sta *sta, u8 tid)
+ struct ieee80211_sta *sta, u8 tids)
{
int ret;
might_sleep();
- ret = local->ops->reset_tid_config(&local->hw, &sdata->vif, sta, tid);
+ ret = local->ops->reset_tid_config(&local->hw, &sdata->vif, sta, tids);
trace_drv_return_int(local, ret);
return ret;
diff --git a/net/mac80211/he.c b/net/mac80211/he.c
index 1087f715338b..cc26f239838b 100644
--- a/net/mac80211/he.c
+++ b/net/mac80211/he.c
@@ -8,10 +8,55 @@
#include "ieee80211_i.h"
+static void
+ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
+ struct sta_info *sta)
+{
+ enum ieee80211_smps_mode smps_mode;
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ switch (le16_get_bits(he_6ghz_capa->capa,
+ IEEE80211_HE_6GHZ_CAP_SM_PS)) {
+ case WLAN_HT_CAP_SM_PS_INVALID:
+ case WLAN_HT_CAP_SM_PS_STATIC:
+ smps_mode = IEEE80211_SMPS_STATIC;
+ break;
+ case WLAN_HT_CAP_SM_PS_DYNAMIC:
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ break;
+ case WLAN_HT_CAP_SM_PS_DISABLED:
+ smps_mode = IEEE80211_SMPS_OFF;
+ break;
+ }
+
+ sta->sta.smps_mode = smps_mode;
+ } else {
+ sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+ }
+
+ switch (le16_get_bits(he_6ghz_capa->capa,
+ IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
+ 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;
+ }
+
+ sta->sta.he_6ghz_capa = *he_6ghz_capa;
+}
+
void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
struct sta_info *sta)
{
struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap;
@@ -53,21 +98,21 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(sta);
sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
+
+ if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa)
+ ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, sta);
}
void
ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
- const struct ieee80211_he_operation *he_op_ie_elem)
+ const struct ieee80211_he_operation *he_op_ie)
{
- struct ieee80211_he_operation *he_operation =
- &vif->bss_conf.he_operation;
-
- if (!he_op_ie_elem) {
- memset(he_operation, 0, sizeof(*he_operation));
+ memset(&vif->bss_conf.he_oper, 0, sizeof(vif->bss_conf.he_oper));
+ if (!he_op_ie)
return;
- }
- vif->bss_conf.he_operation = *he_op_ie_elem;
+ vif->bss_conf.he_oper.params = __le32_to_cpu(he_op_ie->he_oper_params);
+ vif->bss_conf.he_oper.nss_set = __le16_to_cpu(he_op_ie->he_mcs_nss_set);
}
void
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index d40744903fa9..81d26fef41e9 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -9,7 +9,7 @@
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2016 Intel Deutschland GmbH
- * Copyright(c) 2018-2019 Intel Corporation
+ * Copyright(c) 2018-2020 Intel Corporation
*/
#include <linux/delay.h>
@@ -781,6 +781,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type ch_type;
int err;
u32 sta_flags;
+ u32 vht_cap_info = 0;
sdata_assert_lock(sdata);
@@ -798,9 +799,13 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
break;
}
+ if (elems->vht_cap_elem)
+ vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
+
memset(&params, 0, sizeof(params));
err = ieee80211_parse_ch_switch_ie(sdata, elems,
ifibss->chandef.chan->band,
+ vht_cap_info,
sta_flags, ifibss->bssid, &csa_ie);
/* can't switch to destination channel, fail */
if (err < 0)
@@ -1060,8 +1065,10 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
/* we both use VHT */
struct ieee80211_vht_cap cap_ie;
struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
+ u32 vht_cap_info =
+ le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
- ieee80211_chandef_vht_oper(&local->hw,
+ ieee80211_chandef_vht_oper(&local->hw, vht_cap_info,
elems->vht_operation,
elems->ht_operation,
&chandef);
@@ -1758,6 +1765,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
int i;
int ret;
+ if (params->chandef.chan->freq_offset) {
+ /* this may work, but is untested */
+ return -EOPNOTSUPP;
+ }
+
ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
&params->chandef,
sdata->wdev.iftype);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f8ed4f621f7f..ec1a71ac65f2 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -111,6 +111,8 @@ struct ieee80211_bss {
size_t supp_rates_len;
struct ieee80211_rate *beacon_rate;
+ u32 vht_cap_info;
+
/*
* During association, we save an ERP value from a probe response so
* that we can feed ERP info to the driver when handling the
@@ -267,7 +269,7 @@ struct probe_resp {
struct rcu_head rcu_head;
int len;
u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
- u8 data[0];
+ u8 data[];
};
struct ps_data {
@@ -450,8 +452,6 @@ struct ieee80211_if_managed {
u8 bssid[ETH_ALEN] __aligned(2);
- u16 aid;
-
bool powersave; /* powersave requested for this iface */
bool broken_ap; /* AP is broken -- turn off powersave */
bool have_beacon;
@@ -964,6 +964,10 @@ struct ieee80211_sub_if_data {
bool rc_has_vht_mcs_mask[NUM_NL80211_BANDS];
u16 rc_rateidx_vht_mcs_mask[NUM_NL80211_BANDS][NL80211_VHT_NSS_MAX];
+ /* Beacon frame (non-MCS) rate (as a bitmap) */
+ u32 beacon_rateidx_mask[NUM_NL80211_BANDS];
+ bool beacon_rate_set;
+
union {
struct ieee80211_if_ap ap;
struct ieee80211_if_wds wds;
@@ -1169,7 +1173,8 @@ struct ieee80211_local {
/* number of interfaces with corresponding FIF_ flags */
int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
fif_probe_req;
- int probe_req_reg;
+ bool probe_req_reg;
+ bool rx_mcast_action_reg;
unsigned int filter_flags; /* FIF_* */
bool wiphy_ciphers_allocated;
@@ -1491,6 +1496,7 @@ struct ieee802_11_elems {
const struct ieee80211_he_operation *he_operation;
const struct ieee80211_he_spr *he_spr;
const struct ieee80211_mu_edca_param_set *mu_edca_param_set;
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa;
const u8 *uora_element;
const u8 *mesh_id;
const u8 *peering;
@@ -1780,7 +1786,8 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags,
- u32 ctrl_flags);
+ u32 ctrl_flags,
+ u64 *cookie);
void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
struct sk_buff_head *skbs);
struct sk_buff *
@@ -1797,7 +1804,8 @@ void ieee80211_check_fast_xmit_iface(struct ieee80211_sub_if_data *sdata);
void ieee80211_clear_fast_xmit(struct sta_info *sta);
int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len,
- const u8 *dest, __be16 proto, bool unencrypted);
+ const u8 *dest, __be16 proto, bool unencrypted,
+ u64 *cookie);
int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len);
@@ -1891,6 +1899,7 @@ void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const u8 *he_cap_ie, u8 he_cap_len,
+ const struct ieee80211_he_6ghz_capa *he_6ghz_capa,
struct sta_info *sta);
void
ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif,
@@ -1909,6 +1918,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
* @sdata: the sdata of the interface which has received the frame
* @elems: parsed 802.11 elements received with the frame
* @current_band: indicates the current band
+ * @vht_cap_info: VHT capabilities of the transmitter
* @sta_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted. Only the
* following subset of &enum ieee80211_sta_flags are evaluated:
@@ -1923,6 +1933,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
enum nl80211_band current_band,
+ u32 vht_cap_info,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie);
@@ -2133,7 +2144,7 @@ enum {
IEEE80211_PROBE_FLAG_RANDOM_SN = BIT(2),
};
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
size_t buffer_len,
struct ieee80211_scan_ies *ie_desc,
const u8 *ie, size_t ie_len,
@@ -2171,7 +2182,9 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype);
u8 *ieee80211_ie_build_he_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end);
-u8 *ieee80211_ie_build_he_oper(u8 *pos);
+void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
+u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef);
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates);
@@ -2186,10 +2199,13 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
/* channel management */
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef);
-bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
+bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
const struct ieee80211_vht_operation *oper,
const struct ieee80211_ht_operation *htop,
struct cfg80211_chan_def *chandef);
+bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_he_operation *he_oper,
+ struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index d069825705d6..f900c84fb40f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -644,6 +644,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
local->fif_probe_req++;
}
+ if (sdata->vif.probe_req_reg)
+ drv_config_iface_filter(local, sdata,
+ FIF_PROBE_REQ,
+ FIF_PROBE_REQ);
+
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
sdata->vif.type != NL80211_IFTYPE_NAN)
changed |= ieee80211_reset_erp_info(sdata);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 6423173bb87e..b4a2efe8e83a 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -64,6 +64,9 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
if (local->fif_pspoll)
new_flags |= FIF_PSPOLL;
+ if (local->rx_mcast_action_reg)
+ new_flags |= FIF_MCAST_ACTION;
+
spin_lock_bh(&local->filter_lock);
changed_flags = local->filter_flags ^ new_flags;
@@ -104,13 +107,15 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
chandef.chan = local->tmp_channel;
chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
chandef.center_freq1 = chandef.chan->center_freq;
+ chandef.freq1_offset = chandef.chan->freq_offset;
} else
chandef = local->_oper_chandef;
WARN(!cfg80211_chandef_valid(&chandef),
- "control:%d MHz width:%d center: %d/%d MHz",
- chandef.chan->center_freq, chandef.width,
- chandef.center_freq1, chandef.center_freq2);
+ "control:%d.%03d MHz width:%d center: %d.%03d/%d MHz",
+ chandef.chan->center_freq, chandef.chan->freq_offset,
+ chandef.width, chandef.center_freq1, chandef.freq1_offset,
+ chandef.center_freq2);
if (!cfg80211_chandef_identical(&chandef, &local->_oper_chandef))
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
@@ -591,6 +596,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH);
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS);
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_SCAN_FREQ_KHZ);
if (!ops->hw_scan) {
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 36978a0e5000..5f1ca25b6c97 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2020 Intel Corporation
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*/
@@ -63,6 +63,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
u32 basic_rates = 0;
struct cfg80211_chan_def sta_chan_def;
struct ieee80211_supported_band *sband;
+ u32 vht_cap_info = 0;
/*
* As support for each feature is added, check for matching
@@ -96,9 +97,14 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
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(&sdata->local->hw,
+
+ if (ie->vht_cap_elem)
+ vht_cap_info = le32_to_cpu(ie->vht_cap_elem->vht_cap_info);
+
+ ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
ie->vht_operation, ie->ht_operation,
&sta_chan_def);
+ ieee80211_chandef_he_6ghz_oper(sdata, ie->he_operation, &sta_chan_def);
if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
&sta_chan_def))
@@ -415,6 +421,10 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,
if (!sband)
return -EINVAL;
+ /* HT not allowed in 6 GHz */
+ if (sband->band == NL80211_BAND_6GHZ)
+ return 0;
+
if (!sband->ht_cap.ht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -452,6 +462,10 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[channel->band];
ht_cap = &sband->ht_cap;
+ /* HT not allowed in 6 GHz */
+ if (sband->band == NL80211_BAND_6GHZ)
+ return 0;
+
if (!ht_cap->ht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -479,6 +493,10 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
if (!sband)
return -EINVAL;
+ /* VHT not allowed in 6 GHz */
+ if (sband->band == NL80211_BAND_6GHZ)
+ return 0;
+
if (!sband->vht_cap.vht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -516,6 +534,10 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[channel->band];
vht_cap = &sband->vht_cap;
+ /* VHT not allowed in 6 GHz */
+ if (sband->band == NL80211_BAND_6GHZ)
+ return 0;
+
if (!vht_cap->vht_supported ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 ||
@@ -565,6 +587,7 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
{
const struct ieee80211_sta_he_cap *he_cap;
struct ieee80211_supported_band *sband;
+ u32 len;
u8 *pos;
sband = ieee80211_get_sband(sdata);
@@ -578,12 +601,23 @@ int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)
return 0;
- if (skb_tailroom(skb) < 2 + 1 + sizeof(struct ieee80211_he_operation))
+ len = 2 + 1 + sizeof(struct ieee80211_he_operation);
+ if (sdata->vif.bss_conf.chandef.chan->band == NL80211_BAND_6GHZ)
+ len += sizeof(struct ieee80211_he_6ghz_oper);
+
+ if (skb_tailroom(skb) < len)
return -ENOMEM;
- pos = skb_put(skb, 2 + 1 + sizeof(struct ieee80211_he_operation));
- ieee80211_ie_build_he_oper(pos);
+ pos = skb_put(skb, len);
+ ieee80211_ie_build_he_oper(pos, &sdata->vif.bss_conf.chandef);
+
+ return 0;
+}
+int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ ieee80211_ie_build_he_6ghz_cap(sdata, skb);
return 0;
}
@@ -766,6 +800,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
2 + sizeof(struct ieee80211_vht_operation) +
ie_len_he_cap +
2 + 1 + sizeof(struct ieee80211_he_operation) +
+ sizeof(struct ieee80211_he_6ghz_oper) +
+ 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
ifmsh->ie_len;
bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
@@ -885,6 +921,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
mesh_add_vht_oper_ie(sdata, skb) ||
mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
mesh_add_he_oper_ie(sdata, skb) ||
+ mesh_add_he_6ghz_cap_ie(sdata, skb) ||
mesh_add_vendor_ies(sdata, skb))
goto out_free;
@@ -994,6 +1031,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
/* stop the beacon */
ifmsh->mesh_id_len = 0;
sdata->vif.bss_conf.enable_beacon = false;
+ sdata->beacon_rate_set = false;
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
@@ -1044,7 +1082,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_supported_band *sband;
int err;
- u32 sta_flags;
+ u32 sta_flags, vht_cap_info = 0;
sdata_assert_lock(sdata);
@@ -1067,8 +1105,13 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
break;
}
+ if (elems->vht_cap_elem)
+ vht_cap_info =
+ le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
+
memset(&params, 0, sizeof(params));
err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band,
+ vht_cap_info,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 953f720754e8..40492d1bd8fd 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -222,6 +222,8 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ie_len);
int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
+int mesh_add_he_6ghz_cap_ie(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
void ieee80211s_init(void);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 737c5f4dbf52..798e4b6b383f 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -238,6 +238,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
2 + sizeof(struct ieee80211_vht_operation) +
ie_len_he_cap +
2 + 1 + sizeof(struct ieee80211_he_operation) +
+ sizeof(struct ieee80211_he_6ghz_oper) +
+ 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
2 + 8 + /* peering IE */
sdata->u.mesh.ie_len);
if (!skb)
@@ -328,7 +330,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
mesh_add_vht_cap_ie(sdata, skb) ||
mesh_add_vht_oper_ie(sdata, skb) ||
mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
- mesh_add_he_oper_ie(sdata, skb))
+ mesh_add_he_oper_ie(sdata, skb) ||
+ mesh_add_he_6ghz_cap_ie(sdata, skb))
goto free;
}
@@ -441,7 +444,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
elems->vht_cap_elem, sta);
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap,
- elems->he_cap_len, sta);
+ elems->he_cap_len,
+ elems->he_6ghz_capa,
+ sta);
if (bw != sta->sta.bandwidth)
changed |= IEEE80211_RC_BW_CHANGED;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 16d75da0996a..5820ef02a587 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -145,6 +145,7 @@ static u32
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
struct ieee80211_channel *channel,
+ u32 vht_cap_info,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
const struct ieee80211_he_operation *he_oper,
@@ -155,13 +156,23 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap sta_ht_cap;
u32 ht_cfreq, ret;
- memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
- ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
-
memset(chandef, 0, sizeof(struct cfg80211_chan_def));
chandef->chan = channel;
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = channel->center_freq;
+ chandef->freq1_offset = channel->freq_offset;
+
+ if (channel->band == NL80211_BAND_6GHZ) {
+ if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, chandef))
+ ret = IEEE80211_STA_DISABLE_HT |
+ IEEE80211_STA_DISABLE_VHT |
+ IEEE80211_STA_DISABLE_HE;
+ vht_chandef = *chandef;
+ goto out;
+ }
+
+ memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+ ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
if (!ht_oper || !sta_ht_cap.ht_supported) {
ret = IEEE80211_STA_DISABLE_HT |
@@ -222,7 +233,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
memcpy(&he_oper_vht_cap, he_oper->optional, 3);
he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0);
- if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
+ if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
&he_oper_vht_cap, ht_oper,
&vht_chandef)) {
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
@@ -231,8 +242,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
ret = IEEE80211_STA_DISABLE_HE;
goto out;
}
- } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_oper,
- ht_oper, &vht_chandef)) {
+ } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
+ vht_cap_info,
+ vht_oper, ht_oper,
+ &vht_chandef)) {
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
sdata_info(sdata,
"AP VHT information is invalid, disable VHT\n");
@@ -328,6 +341,7 @@ out:
static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
const struct ieee80211_ht_cap *ht_cap,
+ const struct ieee80211_vht_cap *vht_cap,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
const struct ieee80211_he_operation *he_oper,
@@ -342,6 +356,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
u16 ht_opmode;
u32 flags;
enum ieee80211_sta_rx_bandwidth new_sta_bw;
+ u32 vht_cap_info = 0;
int ret;
/* if HT was/is disabled, don't track any bandwidth changes */
@@ -370,8 +385,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
}
+ if (vht_cap)
+ vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info);
+
/* calculate new channel (type) based on HT/VHT/HE operation IEs */
- flags = ieee80211_determine_chantype(sdata, sband, chan,
+ flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info,
ht_oper, vht_oper, he_oper,
&chandef, true);
@@ -396,9 +414,12 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
return 0;
sdata_info(sdata,
- "AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n",
- ifmgd->bssid, chandef.chan->center_freq, chandef.width,
- chandef.center_freq1, chandef.center_freq2);
+ "AP %pM changed bandwidth, new config is %d.%03d MHz, "
+ "width %d (%d.%03d/%d MHz)\n",
+ ifmgd->bssid, chandef.chan->center_freq,
+ chandef.chan->freq_offset, chandef.width,
+ chandef.center_freq1, chandef.freq1_offset,
+ chandef.center_freq2);
if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_VHT |
@@ -654,6 +675,8 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
he_cap->he_cap_elem.phy_cap_info);
pos = skb_put(skb, he_cap_size);
ieee80211_ie_build_he_cap(pos, he_cap, pos + he_cap_size);
+
+ ieee80211_ie_build_he_6ghz_cap(sdata, skb);
}
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
@@ -727,6 +750,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
2 + 1 + sizeof(struct ieee80211_he_cap_elem) + /* HE */
sizeof(struct ieee80211_he_mcs_nss_supp) +
IEEE80211_HE_PPE_THRES_MAX_LEN +
+ 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
assoc_data->ie_len + /* extra IEs */
(assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) +
9, /* WMM */
@@ -899,7 +923,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
+ if (sband->band != NL80211_BAND_6GHZ &&
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
sband, chan, sdata->smps_mode);
@@ -953,7 +978,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
+ if (sband->band != NL80211_BAND_6GHZ &&
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
ieee80211_add_vht_ie(sdata, skb, sband,
&assoc_data->ap_vht_cap);
@@ -1320,6 +1346,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
enum nl80211_band current_band;
struct ieee80211_csa_ie csa_ie;
struct ieee80211_channel_switch ch_switch;
+ struct ieee80211_bss *bss;
int res;
sdata_assert_lock(sdata);
@@ -1331,7 +1358,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;
current_band = cbss->channel->band;
+ bss = (void *)cbss->priv;
res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
+ bss->vht_cap_info,
ifmgd->flags,
ifmgd->associated->bssid, &csa_ie);
@@ -1364,10 +1393,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
IEEE80211_CHAN_DISABLED)) {
sdata_info(sdata,
- "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+ "AP %pM switches to unsupported channel "
+ "(%d.%03d MHz, width:%d, CF1/2: %d.%03d/%d MHz), "
+ "disconnecting\n",
ifmgd->associated->bssid,
csa_ie.chandef.chan->center_freq,
+ csa_ie.chandef.chan->freq_offset,
csa_ie.chandef.width, csa_ie.chandef.center_freq1,
+ csa_ie.chandef.freq1_offset,
csa_ie.chandef.center_freq2);
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
@@ -1500,6 +1533,7 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
chan_increment = 1;
break;
case NL80211_BAND_5GHZ:
+ case NL80211_BAND_6GHZ:
chan_increment = 4;
break;
}
@@ -2137,7 +2171,8 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
}
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
- if (sband->band == NL80211_BAND_5GHZ)
+ if (sband->band == NL80211_BAND_5GHZ ||
+ sband->band == NL80211_BAND_6GHZ)
use_short_slot = true;
if (use_protection != bss_conf->use_cts_prot) {
@@ -2948,10 +2983,15 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
}
if (status_code != WLAN_STATUS_SUCCESS) {
+ cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+
+ if (auth_alg == WLAN_AUTH_SAE &&
+ status_code == WLAN_STATUS_ANTI_CLOG_REQUIRED)
+ return;
+
sdata_info(sdata, "%pM denied authentication (status %d)\n",
mgmt->sa, status_code);
ieee80211_destroy_auth_data(sdata, false);
- cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
event.u.mlme.status = MLME_DENIED;
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
@@ -3149,15 +3189,16 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
*have_higher_than_11mbit = true;
/*
- * Skip HT and VHT BSS membership selectors since they're not
- * rates.
+ * Skip HT, VHT and HE BSS membership selectors since they're
+ * not rates.
*
* Note: Even though the membership selector and the basic
* rate flag share the same bit, they are not exactly
* the same.
*/
if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) ||
- supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY))
+ supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY) ||
+ supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY))
continue;
for (j = 0; j < sband->n_bitrates; j++) {
@@ -3220,6 +3261,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
const struct cfg80211_bss_ies *bss_ies = NULL;
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
+ bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
u32 changed = 0;
int err;
bool ret;
@@ -3249,7 +3291,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false;
}
- ifmgd->aid = aid;
+ sdata->vif.bss_conf.aid = aid;
ifmgd->tdls_chan_switch_prohibited =
elems->ext_capab && elems->ext_capab_len >= 5 &&
(elems->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
@@ -3261,11 +3303,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
* 2G/3G/4G wifi routers, reported models include the "Onda PN51T",
* "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device.
*/
- if ((assoc_data->wmm && !elems->wmm_param) ||
- (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
- (!elems->ht_cap_elem || !elems->ht_operation)) ||
- (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
- (!elems->vht_cap_elem || !elems->vht_operation))) {
+ if (!is_6ghz &&
+ ((assoc_data->wmm && !elems->wmm_param) ||
+ (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ (!elems->ht_cap_elem || !elems->ht_operation)) ||
+ (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+ (!elems->vht_cap_elem || !elems->vht_operation)))) {
const struct cfg80211_bss_ies *ies;
struct ieee802_11_elems bss_elems;
@@ -3323,7 +3366,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
* We previously checked these in the beacon/probe response, so
* they should be present here. This is just a safety net.
*/
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ if (!is_6ghz && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
(!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) {
sdata_info(sdata,
"HT AP is missing WMM params or HT capability/operation\n");
@@ -3331,7 +3374,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
goto out;
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+ if (!is_6ghz && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
(!elems->vht_cap_elem || !elems->vht_operation)) {
sdata_info(sdata,
"VHT AP is missing VHT capability/operation\n");
@@ -3339,6 +3382,14 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
goto out;
}
+ if (is_6ghz && !(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
+ !elems->he_6ghz_capa) {
+ sdata_info(sdata,
+ "HE 6 GHz AP is missing HE 6 GHz band capability\n");
+ ret = false;
+ goto out;
+ }
+
mutex_lock(&sdata->local->sta_mtx);
/*
* station info was already allocated and inserted before
@@ -3381,13 +3432,23 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
elems->he_cap,
elems->he_cap_len,
+ elems->he_6ghz_capa,
sta);
bss_conf->he_support = sta->sta.he_cap.has_he;
+ if (elems->rsnx && elems->rsnx_len &&
+ (elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) &&
+ wiphy_ext_feature_isset(local->hw.wiphy,
+ NL80211_EXT_FEATURE_PROTECTED_TWT))
+ bss_conf->twt_protected = true;
+ else
+ bss_conf->twt_protected = false;
+
changed |= ieee80211_recalc_twt_req(sdata, sta, elems);
} else {
bss_conf->he_support = false;
bss_conf->twt_requester = false;
+ bss_conf->twt_protected = false;
}
if (bss_conf->he_support) {
@@ -3521,9 +3582,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
bss_conf->protected_keep_alive = false;
}
- /* set AID and assoc capability,
+ /* set assoc capability (AID was already set earlier),
* ieee80211_set_associated() will tell the driver */
- bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
ieee80211_set_associated(sdata, cbss, changed);
@@ -3661,7 +3721,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata_assert_lock(sdata);
- channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
+ channel = ieee80211_get_channel_khz(local->hw.wiphy,
+ ieee80211_rx_status_to_khz(rx_status));
if (!channel)
return;
@@ -3877,7 +3938,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
return;
}
- if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
+ if (ieee80211_rx_status_to_khz(rx_status) !=
+ ieee80211_channel_to_khz(chanctx_conf->def.chan)) {
rcu_read_unlock();
return;
}
@@ -3948,7 +4010,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
mgmt->bssid, bssid);
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
- ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) {
+ ieee80211_check_tim(elems.tim, elems.tim_len, bss_conf->aid)) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
@@ -4070,8 +4132,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
- if (ieee80211_config_bw(sdata, sta,
- elems.ht_cap_elem, elems.ht_operation,
+ if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
+ elems.vht_cap_elem, elems.ht_operation,
elems.vht_operation, elems.he_operation,
bssid, &changed)) {
mutex_unlock(&local->sta_mtx);
@@ -4788,6 +4850,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_he_operation *he_oper = NULL;
struct ieee80211_supported_band *sband;
struct cfg80211_chan_def chandef;
+ bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
+ struct ieee80211_bss *bss = (void *)cbss->priv;
int ret;
u32 i;
bool have_80mhz;
@@ -4799,21 +4863,23 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
IEEE80211_STA_DISABLE_160MHZ);
/* disable HT/VHT/HE if we don't support them */
- if (!sband->ht_cap.ht_supported) {
+ if (!sband->ht_cap.ht_supported && !is_6ghz) {
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
}
- if (!sband->vht_cap.vht_supported)
+ if (!sband->vht_cap.vht_supported && !is_6ghz) {
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+ }
if (!ieee80211_get_he_sta_cap(sband))
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
rcu_read_lock();
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && !is_6ghz) {
const u8 *ht_oper_ie, *ht_cap_ie;
ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
@@ -4830,7 +4896,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
}
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && !is_6ghz) {
const u8 *vht_oper_ie, *vht_cap;
vht_oper_ie = ieee80211_bss_get_ie(cbss,
@@ -4886,6 +4952,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
cbss->channel,
+ bss->vht_cap_info,
ht_oper, vht_oper, he_oper,
&chandef, false);
@@ -4894,6 +4961,11 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_HE && is_6ghz) {
+ sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection");
+ return -EINVAL;
+ }
+
/* will change later if needed */
sdata->smps_mode = IEEE80211_SMPS_OFF;
@@ -5022,8 +5094,16 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
* doesn't happen any more, but keep the workaround so
* in case some *other* APs are buggy in different ways
* we can connect -- with a warning.
+ * Allow this workaround only in case the AP provided at least
+ * one rate.
*/
- if (!basic_rates && min_rate_index >= 0) {
+ if (min_rate_index < 0) {
+ sdata_info(sdata,
+ "No legacy rates in association response\n");
+
+ sta_info_free(local, new_sta);
+ return -EINVAL;
+ } else if (!basic_rates) {
sdata_info(sdata,
"No basic rates, using min rate instead\n");
basic_rates = BIT(min_rate_index);
@@ -5267,6 +5347,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct cfg80211_assoc_request *req)
{
+ bool is_6ghz = req->bss->channel->band == NL80211_BAND_6GHZ;
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss *bss = (void *)req->bss->priv;
@@ -5409,14 +5490,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
assoc_data->ap_ht_param =
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
- else
+ else if (!is_6ghz)
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY);
if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap))
memcpy(&assoc_data->ap_vht_cap, vht_ie + 2,
sizeof(struct ieee80211_vht_cap));
- else
- ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+ else if (!is_6ghz)
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT |
+ IEEE80211_STA_DISABLE_HE;
rcu_read_unlock();
if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
@@ -5517,7 +5599,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->timeout_started = true;
assoc_data->need_beacon = true;
} else if (beacon_ies) {
- const u8 *ie;
+ const struct element *elem;
u8 dtim_count = 0;
ieee80211_get_dtim(beacon_ies, &dtim_count,
@@ -5534,15 +5616,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.sync_dtim_count = dtim_count;
}
- ie = cfg80211_find_ext_ie(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION,
- beacon_ies->data, beacon_ies->len);
- if (ie && ie[1] >= 3)
- sdata->vif.bss_conf.profile_periodicity = ie[4];
+ elem = cfg80211_find_ext_elem(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION,
+ beacon_ies->data, beacon_ies->len);
+ if (elem && elem->datalen >= 3)
+ sdata->vif.bss_conf.profile_periodicity = elem->data[2];
- ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY,
- beacon_ies->data, beacon_ies->len);
- if (ie && ie[1] >= 11 &&
- (ie[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
+ elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
+ beacon_ies->data, beacon_ies->len);
+ if (elem && elem->datalen >= 11 &&
+ (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
sdata->vif.bss_conf.ema_ap = true;
} else {
assoc_data->timeout = jiffies;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index c710504ccf1a..db3b8bf75656 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -557,6 +557,10 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
lockdep_assert_held(&local->mtx);
+ if (channel->freq_offset)
+ /* this may work, but is untested */
+ return -EOPNOTSUPP;
+
if (local->use_chanctx && !local->ops->remain_on_channel)
return -EOPNOTSUPP;
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 5dc3e5bc4e64..b11a2af55b06 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2019-2020 Intel Corporation
*/
#include <linux/netdevice.h>
#include <linux/types.h>
@@ -490,7 +491,7 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
- if (tmp_cck_tp_rate && tmp_cck_tp > tmp_mcs_tp) {
+ if (tmp_cck_tp > tmp_mcs_tp) {
for(i = 0; i < MAX_THR_RATES; i++) {
minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i],
tmp_mcs_tp_rate);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 91a13aee4378..21854a61a2b7 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -93,13 +93,44 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
* This function cleans up the SKB, i.e. it removes all the stuff
* only useful for monitoring.
*/
-static void remove_monitor_info(struct sk_buff *skb,
- unsigned int present_fcs_len,
- unsigned int rtap_space)
+static struct sk_buff *ieee80211_clean_skb(struct sk_buff *skb,
+ unsigned int present_fcs_len,
+ unsigned int rtap_space)
{
+ struct ieee80211_hdr *hdr;
+ unsigned int hdrlen;
+ __le16 fc;
+
if (present_fcs_len)
__pskb_trim(skb, skb->len - present_fcs_len);
__pskb_pull(skb, rtap_space);
+
+ hdr = (void *)skb->data;
+ fc = hdr->frame_control;
+
+ /*
+ * Remove the HT-Control field (if present) on management
+ * frames after we've sent the frame to monitoring. We
+ * (currently) don't need it, and don't properly parse
+ * frames with it present, due to the assumption of a
+ * fixed management header length.
+ */
+ if (likely(!ieee80211_is_mgmt(fc) || !ieee80211_has_order(fc)))
+ return skb;
+
+ hdrlen = ieee80211_hdrlen(fc);
+ hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_ORDER);
+
+ if (!pskb_may_pull(skb, hdrlen)) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+
+ memmove(skb->data + IEEE80211_HT_CTL_LEN, skb->data,
+ hdrlen - IEEE80211_HT_CTL_LEN);
+ __pskb_pull(skb, IEEE80211_HT_CTL_LEN);
+
+ return skb;
}
static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
@@ -412,6 +443,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
+ /* TODO: frequency offset in KHz */
put_unaligned_le16(status->freq, pos);
pos += 2;
if (status->bw == RATE_INFO_BW_10)
@@ -826,8 +858,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return NULL;
}
- remove_monitor_info(origskb, present_fcs_len, rtap_space);
- return origskb;
+ return ieee80211_clean_skb(origskb, present_fcs_len,
+ rtap_space);
}
ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
@@ -870,8 +902,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (!origskb)
return NULL;
- remove_monitor_info(origskb, present_fcs_len, rtap_space);
- return origskb;
+ return ieee80211_clean_skb(origskb, present_fcs_len, rtap_space);
}
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
@@ -1984,8 +2015,12 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (mmie_keyidx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS ||
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS +
- NUM_DEFAULT_BEACON_KEYS)
+ NUM_DEFAULT_BEACON_KEYS) {
+ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+ skb->data,
+ skb->len);
return RX_DROP_MONITOR; /* unexpected BIP keyidx */
+ }
rx->key = ieee80211_rx_get_bigtk(rx, mmie_keyidx);
if (!rx->key)
@@ -2131,6 +2166,10 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
/* either the frame has been decrypted or will be dropped */
status->flag |= RX_FLAG_DECRYPTED;
+ if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE))
+ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+ skb->data, skb->len);
+
return result;
}
@@ -2411,8 +2450,12 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
return -EACCES;
}
if (unlikely(ieee80211_is_beacon(fc) && rx->key &&
- ieee80211_get_mmie_keyidx(rx->skb) < 0))
+ ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
+ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+ rx->skb->data,
+ rx->skb->len);
return -EACCES;
+ }
/*
* When using MFP, Action frames are not allowed prior to
* having configured keys.
@@ -3082,9 +3125,10 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
!(status->flag & RX_FLAG_NO_SIGNAL_VAL))
sig = status->signal;
- cfg80211_report_obss_beacon(rx->local->hw.wiphy,
- rx->skb->data, rx->skb->len,
- status->freq, sig);
+ cfg80211_report_obss_beacon_khz(rx->local->hw.wiphy,
+ rx->skb->data, rx->skb->len,
+ ieee80211_rx_status_to_khz(status),
+ sig);
rx->flags |= IEEE80211_RX_BEACON_REPORTED;
}
@@ -3340,19 +3384,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
}
}
break;
- case WLAN_CATEGORY_SA_QUERY:
- if (len < (IEEE80211_MIN_ACTION_SIZE +
- sizeof(mgmt->u.action.u.sa_query)))
- break;
-
- switch (mgmt->u.action.u.sa_query.action) {
- case WLAN_ACTION_SA_QUERY_REQUEST:
- if (sdata->vif.type != NL80211_IFTYPE_STATION)
- break;
- ieee80211_process_sa_query_req(sdata, mgmt, len);
- goto handled;
- }
- break;
case WLAN_CATEGORY_SELF_PROTECTED:
if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.self_prot.action_code)))
@@ -3430,8 +3461,9 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
!(status->flag & RX_FLAG_NO_SIGNAL_VAL))
sig = status->signal;
- if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig,
- rx->skb->data, rx->skb->len, 0)) {
+ if (cfg80211_rx_mgmt_khz(&rx->sdata->wdev,
+ ieee80211_rx_status_to_khz(status), sig,
+ rx->skb->data, rx->skb->len, 0)) {
if (rx->sta)
rx->sta->rx_stats.packets++;
dev_kfree_skb(rx->skb);
@@ -3442,6 +3474,41 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
}
static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_action_post_userspace(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+ int len = rx->skb->len;
+
+ if (!ieee80211_is_action(mgmt->frame_control))
+ return RX_CONTINUE;
+
+ switch (mgmt->u.action.category) {
+ case WLAN_CATEGORY_SA_QUERY:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.sa_query)))
+ break;
+
+ switch (mgmt->u.action.u.sa_query.action) {
+ case WLAN_ACTION_SA_QUERY_REQUEST:
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ break;
+ ieee80211_process_sa_query_req(sdata, mgmt, len);
+ goto handled;
+ }
+ break;
+ }
+
+ return RX_CONTINUE;
+
+ handled:
+ if (rx->sta)
+ rx->sta->rx_stats.packets++;
+ dev_kfree_skb(rx->skb);
+ return RX_QUEUED;
+}
+
+static ieee80211_rx_result debug_noinline
ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
@@ -3721,6 +3788,7 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
CALL_RXH(ieee80211_rx_h_mgmt_check);
CALL_RXH(ieee80211_rx_h_action);
CALL_RXH(ieee80211_rx_h_userspace_mgmt);
+ CALL_RXH(ieee80211_rx_h_action_post_userspace);
CALL_RXH(ieee80211_rx_h_action_return);
CALL_RXH(ieee80211_rx_h_mgmt);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index fdac8192a519..ad90bbe57457 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -132,6 +132,12 @@ ieee80211_update_bss_from_elems(struct ieee80211_local *local,
bss->beacon_rate =
&sband->bitrates[rx_status->rate_idx];
}
+
+ if (elems->vht_cap_elem)
+ bss->vht_cap_info =
+ le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
+ else
+ bss->vht_cap_info = 0;
}
struct ieee80211_bss *
@@ -275,7 +281,8 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
return;
}
- channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
+ channel = ieee80211_get_channel_khz(local->hw.wiphy,
+ ieee80211_rx_status_to_khz(rx_status));
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
@@ -306,8 +313,9 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
}
/* return false if no more work */
-static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
+static bool ieee80211_prep_hw_scan(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_local *local = sdata->local;
struct cfg80211_scan_request *req;
struct cfg80211_chan_def chandef;
u8 bands_used = 0;
@@ -354,7 +362,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
if (req->flags & NL80211_SCAN_FLAG_MIN_PREQ_CONTENT)
flags |= IEEE80211_PROBE_FLAG_MIN_CONTENT;
- ielen = ieee80211_build_preq_ies(local,
+ ielen = ieee80211_build_preq_ies(sdata,
(u8 *)local->hw_scan_req->req.ie,
local->hw_scan_ies_bufsize,
&local->hw_scan_req->ies,
@@ -394,9 +402,12 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (WARN_ON(!local->scan_req))
return;
+ scan_sdata = rcu_dereference_protected(local->scan_sdata,
+ lockdep_is_held(&local->mtx));
+
if (hw_scan && !aborted &&
!ieee80211_hw_check(&local->hw, SINGLE_SCAN_ON_ALL_BANDS) &&
- ieee80211_prep_hw_scan(local)) {
+ ieee80211_prep_hw_scan(scan_sdata)) {
int rc;
rc = drv_hw_scan(local,
@@ -425,9 +436,6 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
cfg80211_scan_done(scan_req, &local->scan_info);
}
RCU_INIT_POINTER(local->scan_req, NULL);
-
- scan_sdata = rcu_dereference_protected(local->scan_sdata,
- lockdep_is_held(&local->mtx));
RCU_INIT_POINTER(local->scan_sdata, NULL);
local->scanning = 0;
@@ -769,7 +777,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_idle(local);
if (hw_scan) {
- WARN_ON(!ieee80211_prep_hw_scan(local));
+ WARN_ON(!ieee80211_prep_hw_scan(sdata));
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
} else {
rc = ieee80211_start_sw_scan(local, sdata);
@@ -896,6 +904,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
local->scan_chandef.chan = chan;
local->scan_chandef.center_freq1 = chan->center_freq;
+ local->scan_chandef.freq1_offset = chan->freq_offset;
local->scan_chandef.center_freq2 = 0;
switch (scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
@@ -1266,7 +1275,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
- ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
+ ieee80211_build_preq_ies(sdata, ie, num_bands * iebufsz,
&sched_scan_ies, req->ie,
req->ie_len, bands_used, rate_masks, &chandef,
flags);
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 5fe2b645912f..ae1cb2c68722 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2008, Intel Corporation
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2018 Intel Corporation
+ * Copyright (C) 2018, 2020 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -22,6 +22,7 @@
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
enum nl80211_band current_band,
+ u32 vht_cap_info,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie)
{
@@ -150,6 +151,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
/* ignore if parsing fails */
if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
+ vht_cap_info,
&vht_oper, &ht_oper,
&new_vht_chandef))
new_vht_chandef.chan = NULL;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 36f1abaab9ff..49728047dfad 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -3,6 +3,7 @@
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2015-2017 Intel Deutschland GmbH
+ * Copyright(c) 2020 Intel Corporation
*/
#ifndef STA_INFO_H
@@ -68,6 +69,8 @@
* @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
* @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
* until pending frames are delivered
+ * @WLAN_STA_USES_ENCRYPTION: This station was configured for encryption,
+ * so drop all packets without a key later.
*
* @NUM_WLAN_STA_FLAGS: number of defined flags
*/
@@ -116,6 +119,7 @@ enum ieee80211_sta_info_flags {
#define HT_AGG_STATE_WANT_STOP 5
#define HT_AGG_STATE_START_CB 6
#define HT_AGG_STATE_STOP_CB 7
+#define HT_AGG_STATE_SENT_ADDBA 8
DECLARE_EWMA(avg_signal, 10, 8)
enum ieee80211_agg_stop_reason {
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 22512805eafb..7b1bacac39c6 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -649,10 +649,17 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
info->status.ack_signal,
info->status.is_valid_ack_signal,
GFP_ATOMIC);
- else
+ else if (ieee80211_is_mgmt(hdr->frame_control))
cfg80211_mgmt_tx_status(&sdata->wdev, cookie,
skb->data, skb->len,
acked, GFP_ATOMIC);
+ else
+ cfg80211_control_port_tx_status(&sdata->wdev,
+ cookie,
+ skb->data,
+ skb->len,
+ acked,
+ GFP_ATOMIC);
}
rcu_read_unlock();
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index fca1f5477396..4b0cff4a07bd 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -226,12 +226,11 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
static void
ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 *pos = skb_put(skb, 4);
*pos++ = WLAN_EID_AID;
*pos++ = 2; /* len */
- put_unaligned_le16(ifmgd->aid, pos);
+ put_unaligned_le16(sdata->vif.bss_conf.aid, pos);
}
/* translate numbering in the WMM parameter IE to the mac80211 notation */
@@ -1055,7 +1054,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
/* disable bottom halves when entering the Tx path */
local_bh_disable();
- __ieee80211_subif_start_xmit(skb, dev, flags, 0);
+ __ieee80211_subif_start_xmit(skb, dev, flags, 0, NULL);
local_bh_enable();
return ret;
@@ -1567,6 +1566,10 @@ ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
u32 ch_sw_tm_ie;
int ret;
+ if (chandef->chan->freq_offset)
+ /* this may work, but is untested */
+ return -EOPNOTSUPP;
+
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, addr);
if (!sta) {
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 427f51a0a994..1b4709694d2a 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -37,32 +37,42 @@
#define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
#define CHANDEF_ENTRY __field(u32, control_freq) \
+ __field(u32, freq_offset) \
__field(u32, chan_width) \
__field(u32, center_freq1) \
+ __field(u32, freq1_offset) \
__field(u32, center_freq2)
#define CHANDEF_ASSIGN(c) \
__entry->control_freq = (c) ? ((c)->chan ? (c)->chan->center_freq : 0) : 0; \
+ __entry->freq_offset = (c) ? ((c)->chan ? (c)->chan->freq_offset : 0) : 0; \
__entry->chan_width = (c) ? (c)->width : 0; \
__entry->center_freq1 = (c) ? (c)->center_freq1 : 0; \
+ __entry->freq1_offset = (c) ? (c)->freq1_offset : 0; \
__entry->center_freq2 = (c) ? (c)->center_freq2 : 0;
-#define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz"
-#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \
- __entry->center_freq1, __entry->center_freq2
+#define CHANDEF_PR_FMT " control:%d.%03d MHz width:%d center: %d.%03d/%d MHz"
+#define CHANDEF_PR_ARG __entry->control_freq, __entry->freq_offset, __entry->chan_width, \
+ __entry->center_freq1, __entry->freq1_offset, __entry->center_freq2
#define MIN_CHANDEF_ENTRY \
__field(u32, min_control_freq) \
+ __field(u32, min_freq_offset) \
__field(u32, min_chan_width) \
__field(u32, min_center_freq1) \
+ __field(u32, min_freq1_offset) \
__field(u32, min_center_freq2)
#define MIN_CHANDEF_ASSIGN(c) \
__entry->min_control_freq = (c)->chan ? (c)->chan->center_freq : 0; \
+ __entry->min_freq_offset = (c)->chan ? (c)->chan->freq_offset : 0; \
__entry->min_chan_width = (c)->width; \
__entry->min_center_freq1 = (c)->center_freq1; \
+ __entry->freq1_offset = (c)->freq1_offset; \
__entry->min_center_freq2 = (c)->center_freq2;
-#define MIN_CHANDEF_PR_FMT " min_control:%d MHz min_width:%d min_center: %d/%d MHz"
-#define MIN_CHANDEF_PR_ARG __entry->min_control_freq, __entry->min_chan_width, \
- __entry->min_center_freq1, __entry->min_center_freq2
+#define MIN_CHANDEF_PR_FMT " min_control:%d.%03d MHz min_width:%d min_center: %d.%03d/%d MHz"
+#define MIN_CHANDEF_PR_ARG __entry->min_control_freq, __entry->min_freq_offset, \
+ __entry->min_chan_width, \
+ __entry->min_center_freq1, __entry->min_freq1_offset, \
+ __entry->min_center_freq2
#define CHANCTX_ENTRY CHANDEF_ENTRY \
MIN_CHANDEF_ENTRY \
@@ -412,6 +422,7 @@ TRACE_EVENT(drv_bss_info_changed,
__field(s32, cqm_rssi_hyst)
__field(u32, channel_width)
__field(u32, channel_cfreq1)
+ __field(u32, channel_cfreq1_offset)
__dynamic_array(u32, arp_addr_list,
info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
IEEE80211_BSS_ARP_ADDR_LIST_LEN :
@@ -452,6 +463,7 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
__entry->channel_width = info->chandef.width;
__entry->channel_cfreq1 = info->chandef.center_freq1;
+ __entry->channel_cfreq1_offset = info->chandef.freq1_offset;
__entry->arp_addr_cnt = info->arp_addr_cnt;
memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
@@ -1223,6 +1235,7 @@ TRACE_EVENT(drv_remain_on_channel,
LOCAL_ENTRY
VIF_ENTRY
__field(int, center_freq)
+ __field(int, freq_offset)
__field(unsigned int, duration)
__field(u32, type)
),
@@ -1231,14 +1244,16 @@ TRACE_EVENT(drv_remain_on_channel,
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->center_freq = chan->center_freq;
+ __entry->freq_offset = chan->freq_offset;
__entry->duration = duration;
__entry->type = type;
),
TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms type=%d",
+ LOCAL_PR_FMT VIF_PR_FMT " freq:%d.%03dMHz duration:%dms type=%d",
LOCAL_PR_ARG, VIF_PR_ARG,
- __entry->center_freq, __entry->duration, __entry->type
+ __entry->center_freq, __entry->freq_offset,
+ __entry->duration, __entry->type
)
);
@@ -1546,8 +1561,10 @@ struct trace_vif_entry {
struct trace_chandef_entry {
u32 control_freq;
+ u32 freq_offset;
u32 chan_width;
u32 center_freq1;
+ u32 freq1_offset;
u32 center_freq2;
} __packed;
@@ -1597,18 +1614,26 @@ TRACE_EVENT(drv_switch_vif_chanctx,
sizeof(local_vifs[i].vif.vif_name));
SWITCH_ENTRY_ASSIGN(old_chandef.control_freq,
old_ctx->def.chan->center_freq);
+ SWITCH_ENTRY_ASSIGN(old_chandef.freq_offset,
+ old_ctx->def.chan->freq_offset);
SWITCH_ENTRY_ASSIGN(old_chandef.chan_width,
old_ctx->def.width);
SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1,
old_ctx->def.center_freq1);
+ SWITCH_ENTRY_ASSIGN(old_chandef.freq1_offset,
+ old_ctx->def.freq1_offset);
SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2,
old_ctx->def.center_freq2);
SWITCH_ENTRY_ASSIGN(new_chandef.control_freq,
new_ctx->def.chan->center_freq);
+ SWITCH_ENTRY_ASSIGN(new_chandef.freq_offset,
+ new_ctx->def.chan->freq_offset);
SWITCH_ENTRY_ASSIGN(new_chandef.chan_width,
new_ctx->def.width);
SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1,
new_ctx->def.center_freq1);
+ SWITCH_ENTRY_ASSIGN(new_chandef.freq1_offset,
+ new_ctx->def.freq1_offset);
SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2,
new_ctx->def.center_freq2);
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 82846aca86d9..e9ce658141f5 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2144,7 +2144,7 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
/*
* Please update the file
- * Documentation/networking/mac80211-injection.txt
+ * Documentation/networking/mac80211-injection.rst
* when parsing new fields here.
*/
@@ -2436,13 +2436,19 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
return 0;
}
-static int ieee80211_store_ack_skb(struct ieee80211_local *local,
+static u16 ieee80211_store_ack_skb(struct ieee80211_local *local,
struct sk_buff *skb,
- u32 *info_flags)
+ u32 *info_flags,
+ u64 *cookie)
{
- struct sk_buff *ack_skb = skb_clone_sk(skb);
+ struct sk_buff *ack_skb;
u16 info_id = 0;
+ if (skb->sk)
+ ack_skb = skb_clone_sk(skb);
+ else
+ ack_skb = skb_clone(skb, GFP_ATOMIC);
+
if (ack_skb) {
unsigned long flags;
int id;
@@ -2455,6 +2461,10 @@ static int ieee80211_store_ack_skb(struct ieee80211_local *local,
if (id >= 0) {
info_id = id;
*info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ if (cookie) {
+ *cookie = ieee80211_mgmt_tx_cookie(local);
+ IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
+ }
} else {
kfree_skb(ack_skb);
}
@@ -2484,7 +2494,8 @@ static int ieee80211_store_ack_skb(struct ieee80211_local *local,
*/
static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u32 info_flags,
- struct sta_info *sta, u32 ctrl_flags)
+ struct sta_info *sta, u32 ctrl_flags,
+ u64 *cookie)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info;
@@ -2755,9 +2766,11 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
goto free;
}
- if (unlikely(!multicast && skb->sk &&
- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
- info_id = ieee80211_store_ack_skb(local, skb, &info_flags);
+ if (unlikely(!multicast && ((skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) ||
+ ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS)))
+ info_id = ieee80211_store_ack_skb(local, skb, &info_flags,
+ cookie);
/*
* If the skb is shared we need to obtain our own copy.
@@ -3913,7 +3926,8 @@ EXPORT_SYMBOL(ieee80211_txq_schedule_start);
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags,
- u32 ctrl_flags)
+ u32 ctrl_flags,
+ u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
@@ -3983,7 +3997,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
skb_mark_not_on_list(skb);
skb = ieee80211_build_hdr(sdata, skb, info_flags,
- sta, ctrl_flags);
+ sta, ctrl_flags, cookie);
if (IS_ERR(skb)) {
kfree_skb_list(next);
goto out;
@@ -4125,9 +4139,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
__skb_queue_head_init(&queue);
ieee80211_convert_to_unicast(skb, dev, &queue);
while ((skb = __skb_dequeue(&queue)))
- __ieee80211_subif_start_xmit(skb, dev, 0, 0);
+ __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
} else {
- __ieee80211_subif_start_xmit(skb, dev, 0, 0);
+ __ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
}
return NETDEV_TX_OK;
@@ -4215,7 +4229,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
if (unlikely(!multicast && skb->sk &&
skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
- ieee80211_store_ack_skb(local, skb, &info->flags);
+ ieee80211_store_ack_skb(local, skb, &info->flags, NULL);
memset(info, 0, sizeof(*info));
@@ -4299,7 +4313,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
goto out;
}
- skb = ieee80211_build_hdr(sdata, skb, info_flags, sta, 0);
+ skb = ieee80211_build_hdr(sdata, skb, info_flags, sta, 0, NULL);
if (IS_ERR(skb))
goto out;
@@ -4883,7 +4897,10 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
txrc.bss_conf = &sdata->vif.bss_conf;
txrc.skb = skb;
txrc.reported_rate.idx = -1;
- txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
+ if (sdata->beacon_rate_set && sdata->beacon_rateidx_mask[band])
+ txrc.rate_idx_mask = sdata->beacon_rateidx_mask[band];
+ else
+ txrc.rate_idx_mask = sdata->rc_rateidx_mask[band];
txrc.bss = true;
rate_control_get_rate(sdata, NULL, &txrc);
@@ -5006,7 +5023,7 @@ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw,
pspoll = skb_put_zero(skb, sizeof(*pspoll));
pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
IEEE80211_STYPE_PSPOLL);
- pspoll->aid = cpu_to_le16(ifmgd->aid);
+ pspoll->aid = cpu_to_le16(sdata->vif.bss_conf.aid);
/* aid in PS-Poll has its two MSBs each set to 1 */
pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14);
@@ -5336,14 +5353,15 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
const u8 *buf, size_t len,
- const u8 *dest, __be16 proto, bool unencrypted)
+ const u8 *dest, __be16 proto, bool unencrypted,
+ u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ethhdr *ehdr;
u32 ctrl_flags = 0;
- u32 flags;
+ u32 flags = 0;
/* Only accept CONTROL_PORT_PROTOCOL configured in CONNECT/ASSOCIATE
* or Pre-Authentication
@@ -5356,9 +5374,13 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
ctrl_flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
if (unencrypted)
- flags = IEEE80211_TX_INTFL_DONT_ENCRYPT;
- else
- flags = 0;
+ flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+ if (cookie)
+ ctrl_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+ flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_INJECTED;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(struct ethhdr) + len);
@@ -5379,10 +5401,15 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
skb_reset_network_header(skb);
skb_reset_mac_header(skb);
+ /* mutex lock is only needed for incrementing the cookie counter */
+ mutex_lock(&local->mtx);
+
local_bh_disable();
- __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags);
+ __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie);
local_bh_enable();
+ mutex_unlock(&local->mtx);
+
return 0;
}
@@ -5409,7 +5436,8 @@ int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
local_bh_disable();
__ieee80211_subif_start_xmit(skb, skb->dev, 0,
- IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP);
+ IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP,
+ NULL);
local_bh_enable();
return 0;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 20436c86b9bf..21c94094a699 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -936,6 +936,10 @@ static void ieee80211_parse_extension_element(u32 *crc,
len >= ieee80211_he_spr_size(data))
elems->he_spr = data;
break;
+ case WLAN_EID_EXT_HE_6GHZ_CAPA:
+ if (len == sizeof(*elems->he_6ghz_capa))
+ elems->he_6ghz_capa = data;
+ break;
}
}
@@ -1659,7 +1663,20 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
}
}
-static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
+static u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end)
+{
+ if ((end - pos) < 5)
+ return pos;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(cap);
+ *pos++ = WLAN_EID_EXT_HE_6GHZ_CAPA;
+ memcpy(pos, &cap, sizeof(cap));
+
+ return pos + 2;
+}
+
+static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata,
u8 *buffer, size_t buffer_len,
const u8 *ie, size_t ie_len,
enum nl80211_band band,
@@ -1667,6 +1684,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
struct cfg80211_chan_def *chandef,
size_t *offset, u32 flags)
{
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
const struct ieee80211_sta_he_cap *he_cap;
u8 *pos = buffer, *end = buffer + buffer_len;
@@ -1844,6 +1862,14 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
pos = ieee80211_ie_build_he_cap(pos, he_cap, end);
if (!pos)
goto out_err;
+
+ if (sband->band == NL80211_BAND_6GHZ) {
+ enum nl80211_iftype iftype =
+ ieee80211_vif_type_p2p(&sdata->vif);
+ __le16 cap = ieee80211_get_he_6ghz_capa(sband, iftype);
+
+ pos = ieee80211_write_he_6ghz_cap(pos, cap, end);
+ }
}
/*
@@ -1858,7 +1884,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
return pos - buffer;
}
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
size_t buffer_len,
struct ieee80211_scan_ies *ie_desc,
const u8 *ie, size_t ie_len,
@@ -1873,7 +1899,7 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
for (i = 0; i < NUM_NL80211_BANDS; i++) {
if (bands_used & BIT(i)) {
- pos += ieee80211_build_preq_ies_band(local,
+ pos += ieee80211_build_preq_ies_band(sdata,
buffer + pos,
buffer_len - pos,
ie, ie_len, i,
@@ -1935,7 +1961,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
return NULL;
rate_masks[chan->band] = ratemask;
- ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
+ ies_len = ieee80211_build_preq_ies(sdata, skb_tail_pointer(skb),
skb_tailroom(skb), &dummy_ie_desc,
ie, ie_len, BIT(chan->band),
rate_masks, &chandef, flags);
@@ -2835,6 +2861,50 @@ end:
return pos;
}
+void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_supported_band *sband;
+ const struct ieee80211_sband_iftype_data *iftd;
+ enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
+ u8 *pos;
+ u16 cap;
+
+ sband = ieee80211_get_sband(sdata);
+ if (!sband)
+ return;
+
+ iftd = ieee80211_get_sband_iftype_data(sband, iftype);
+ if (WARN_ON(!iftd))
+ return;
+
+ cap = le16_to_cpu(iftd->he_6ghz_capa.capa);
+ cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS;
+
+ switch (sdata->smps_mode) {
+ case IEEE80211_SMPS_AUTOMATIC:
+ case IEEE80211_SMPS_NUM_MODES:
+ WARN_ON(1);
+ /* fall through */
+ case IEEE80211_SMPS_OFF:
+ cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED,
+ IEEE80211_HE_6GHZ_CAP_SM_PS);
+ break;
+ case IEEE80211_SMPS_STATIC:
+ cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC,
+ IEEE80211_HE_6GHZ_CAP_SM_PS);
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC,
+ IEEE80211_HE_6GHZ_CAP_SM_PS);
+ break;
+ }
+
+ pos = skb_put(skb, 2 + 1 + sizeof(cap));
+ ieee80211_write_he_6ghz_cap(pos, cpu_to_le16(cap),
+ pos + 2 + 1 + sizeof(cap));
+}
+
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
const struct cfg80211_chan_def *chandef,
u16 prot_mode, bool rifs_mode)
@@ -2958,13 +3028,18 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
return pos + sizeof(struct ieee80211_vht_operation);
}
-u8 *ieee80211_ie_build_he_oper(u8 *pos)
+u8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef)
{
struct ieee80211_he_operation *he_oper;
+ struct ieee80211_he_6ghz_oper *he_6ghz_op;
u32 he_oper_params;
+ u8 ie_len = 1 + sizeof(struct ieee80211_he_operation);
+
+ if (chandef->chan->band == NL80211_BAND_6GHZ)
+ ie_len += sizeof(struct ieee80211_he_6ghz_oper);
*pos++ = WLAN_EID_EXTENSION;
- *pos++ = 1 + sizeof(struct ieee80211_he_operation);
+ *pos++ = ie_len;
*pos++ = WLAN_EID_EXT_HE_OPERATION;
he_oper_params = 0;
@@ -2974,16 +3049,68 @@ u8 *ieee80211_ie_build_he_oper(u8 *pos)
IEEE80211_HE_OPERATION_ER_SU_DISABLE);
he_oper_params |= u32_encode_bits(1,
IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
+ if (chandef->chan->band == NL80211_BAND_6GHZ)
+ he_oper_params |= u32_encode_bits(1,
+ IEEE80211_HE_OPERATION_6GHZ_OP_INFO);
he_oper = (struct ieee80211_he_operation *)pos;
he_oper->he_oper_params = cpu_to_le32(he_oper_params);
/* don't require special HE peer rates */
he_oper->he_mcs_nss_set = cpu_to_le16(0xffff);
+ pos += sizeof(struct ieee80211_he_operation);
- /* TODO add VHT operational and 6GHz operational subelement? */
+ if (chandef->chan->band != NL80211_BAND_6GHZ)
+ goto out;
- return pos + sizeof(struct ieee80211_vht_operation);
+ /* TODO add VHT operational */
+ he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos;
+ he_6ghz_op->minrate = 6; /* 6 Mbps */
+ he_6ghz_op->primary =
+ ieee80211_frequency_to_channel(chandef->chan->center_freq);
+ he_6ghz_op->ccfs0 =
+ ieee80211_frequency_to_channel(chandef->center_freq1);
+ if (chandef->center_freq2)
+ he_6ghz_op->ccfs1 =
+ ieee80211_frequency_to_channel(chandef->center_freq2);
+ else
+ he_6ghz_op->ccfs1 = 0;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_160:
+ /* Convert 160 MHz channel width to new style as interop
+ * workaround.
+ */
+ he_6ghz_op->control =
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
+ he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0;
+ if (chandef->chan->center_freq < chandef->center_freq1)
+ he_6ghz_op->ccfs0 -= 8;
+ else
+ he_6ghz_op->ccfs0 += 8;
+ fallthrough;
+ case NL80211_CHAN_WIDTH_80P80:
+ he_6ghz_op->control =
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ he_6ghz_op->control =
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ he_6ghz_op->control =
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ;
+ break;
+ default:
+ he_6ghz_op->control =
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ;
+ break;
+ }
+
+ pos += sizeof(struct ieee80211_he_6ghz_oper);
+
+out:
+ return pos;
}
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
@@ -3013,7 +3140,7 @@ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
return true;
}
-bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
+bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
const struct ieee80211_vht_operation *oper,
const struct ieee80211_ht_operation *htop,
struct cfg80211_chan_def *chandef)
@@ -3025,6 +3152,10 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
u32 vht_cap;
bool support_80_80 = false;
bool support_160 = false;
+ u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info,
+ IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
+ u8 supp_chwidth = u32_get_bits(vht_cap_info,
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
if (!oper || !htop)
return false;
@@ -3044,11 +3175,48 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
IEEE80211_HT_OP_MODE_CCFS2_MASK)
>> IEEE80211_HT_OP_MODE_CCFS2_SHIFT;
- /* when parsing (and we know how to) CCFS1 and CCFS2 are equivalent */
ccf0 = ccfs0;
- ccf1 = ccfs1;
- if (!ccfs1 && ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
+
+ /* if not supported, parse as though we didn't understand it */
+ if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
+ ext_nss_bw_supp = 0;
+
+ /*
+ * Cf. IEEE 802.11 Table 9-250
+ *
+ * We really just consider that because it's inefficient to connect
+ * at a higher bandwidth than we'll actually be able to use.
+ */
+ switch ((supp_chwidth << 4) | ext_nss_bw_supp) {
+ default:
+ case 0x00:
+ ccf1 = 0;
+ support_160 = false;
+ support_80_80 = false;
+ break;
+ case 0x01:
+ support_80_80 = false;
+ /* fall through */
+ case 0x02:
+ case 0x03:
ccf1 = ccfs2;
+ break;
+ case 0x10:
+ ccf1 = ccfs1;
+ break;
+ case 0x11:
+ case 0x12:
+ if (!ccfs1)
+ ccf1 = ccfs2;
+ else
+ ccf1 = ccfs1;
+ break;
+ case 0x13:
+ case 0x20:
+ case 0x23:
+ ccf1 = ccfs1;
+ break;
+ }
cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band);
cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band);
@@ -3096,6 +3264,112 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
return true;
}
+bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_he_operation *he_oper,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
+ const struct ieee80211_sta_he_cap *he_cap;
+ struct cfg80211_chan_def he_chandef = *chandef;
+ const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
+ bool support_80_80, support_160;
+ u8 he_phy_cap;
+ u32 freq;
+
+ if (chandef->chan->band != NL80211_BAND_6GHZ)
+ return true;
+
+ sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ];
+
+ he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
+ if (!he_cap) {
+ sdata_info(sdata, "Missing iftype sband data/HE cap");
+ return false;
+ }
+
+ he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0];
+ support_160 =
+ he_phy_cap &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ support_80_80 =
+ he_phy_cap &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+
+ if (!he_oper) {
+ sdata_info(sdata,
+ "HE is not advertised on (on %d MHz), expect issues\n",
+ chandef->chan->center_freq);
+ return false;
+ }
+
+ he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper);
+
+ if (!he_6ghz_oper) {
+ sdata_info(sdata,
+ "HE 6GHz operation missing (on %d MHz), expect issues\n",
+ chandef->chan->center_freq);
+ return false;
+ }
+
+ freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary,
+ NL80211_BAND_6GHZ);
+ he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+
+ switch (u8_get_bits(he_6ghz_oper->control,
+ IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) {
+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ:
+ he_chandef.width = NL80211_CHAN_WIDTH_20;
+ break;
+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
+ he_chandef.width = NL80211_CHAN_WIDTH_40;
+ break;
+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
+ he_chandef.width = NL80211_CHAN_WIDTH_80;
+ break;
+ case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
+ he_chandef.width = NL80211_CHAN_WIDTH_80;
+ if (!he_6ghz_oper->ccfs1)
+ break;
+ if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) {
+ if (support_160)
+ he_chandef.width = NL80211_CHAN_WIDTH_160;
+ } else {
+ if (support_80_80)
+ he_chandef.width = NL80211_CHAN_WIDTH_80P80;
+ }
+ break;
+ }
+
+ if (he_chandef.width == NL80211_CHAN_WIDTH_160) {
+ he_chandef.center_freq1 =
+ ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
+ NL80211_BAND_6GHZ);
+ } else {
+ he_chandef.center_freq1 =
+ ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0,
+ NL80211_BAND_6GHZ);
+ he_chandef.center_freq2 =
+ ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1,
+ NL80211_BAND_6GHZ);
+ }
+
+ if (!cfg80211_chandef_valid(&he_chandef)) {
+ sdata_info(sdata,
+ "HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n",
+ he_chandef.chan ? he_chandef.chan->center_freq : 0,
+ he_chandef.width,
+ he_chandef.center_freq1,
+ he_chandef.center_freq2);
+ return false;
+ }
+
+ *chandef = he_chandef;
+
+ return true;
+}
+
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates)
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 632f07401850..9c6045f9c24d 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -4,7 +4,7 @@
*
* Portions of this file
* Copyright(c) 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2020 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -575,15 +575,21 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
+ /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
break;
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
+ /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
break;
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
- sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
+ if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80)
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
+ else
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
break;
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
+ /* legacy only, no longer used by newer spec */
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
break;
}