summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-13 03:07:07 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-13 03:07:07 +0100
commit6be35c700f742e911ecedd07fcc43d4439922334 (patch)
treeca9f37214d204465fcc2d79c82efd291e357c53c /net/mac80211
parentMerge tag 'for-linus-20121212' of git://git.kernel.org/pub/scm/linux/kernel/g... (diff)
parentnet/mlx4_en: Add support for destination MAC in steering rules (diff)
downloadlinux-6be35c700f742e911ecedd07fcc43d4439922334.tar.xz
linux-6be35c700f742e911ecedd07fcc43d4439922334.zip
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking changes from David Miller: 1) Allow to dump, monitor, and change the bridge multicast database using netlink. From Cong Wang. 2) RFC 5961 TCP blind data injection attack mitigation, from Eric Dumazet. 3) Networking user namespace support from Eric W. Biederman. 4) tuntap/virtio-net multiqueue support by Jason Wang. 5) Support for checksum offload of encapsulated packets (basically, tunneled traffic can still be checksummed by HW). From Joseph Gasparakis. 6) Allow BPF filter access to VLAN tags, from Eric Dumazet and Daniel Borkmann. 7) Bridge port parameters over netlink and BPDU blocking support from Stephen Hemminger. 8) Improve data access patterns during inet socket demux by rearranging socket layout, from Eric Dumazet. 9) TIPC protocol updates and cleanups from Ying Xue, Paul Gortmaker, and Jon Maloy. 10) Update TCP socket hash sizing to be more in line with current day realities. The existing heurstics were choosen a decade ago. From Eric Dumazet. 11) Fix races, queue bloat, and excessive wakeups in ATM and associated drivers, from Krzysztof Mazur and David Woodhouse. 12) Support DOVE (Distributed Overlay Virtual Ethernet) extensions in VXLAN driver, from David Stevens. 13) Add "oops_only" mode to netconsole, from Amerigo Wang. 14) Support set and query of VEB/VEPA bridge mode via PF_BRIDGE, also allow DCB netlink to work on namespaces other than the initial namespace. From John Fastabend. 15) Support PTP in the Tigon3 driver, from Matt Carlson. 16) tun/vhost zero copy fixes and improvements, plus turn it on by default, from Michael S. Tsirkin. 17) Support per-association statistics in SCTP, from Michele Baldessari. And many, many, driver updates, cleanups, and improvements. Too numerous to mention individually. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1722 commits) net/mlx4_en: Add support for destination MAC in steering rules net/mlx4_en: Use generic etherdevice.h functions. net: ethtool: Add destination MAC address to flow steering API bridge: add support of adding and deleting mdb entries bridge: notify mdb changes via netlink ndisc: Unexport ndisc_{build,send}_skb(). uapi: add missing netconf.h to export list pkt_sched: avoid requeues if possible solos-pci: fix double-free of TX skb in DMA mode bnx2: Fix accidental reversions. bna: Driver Version Updated to 3.1.2.1 bna: Firmware update bna: Add RX State bna: Rx Page Based Allocation bna: TX Intr Coalescing Fix bna: Tx and Rx Optimizations bna: Code Cleanup and Enhancements ath9k: check pdata variable before dereferencing it ath5k: RX timestamp is reported at end of frame ath9k_htc: RX timestamp is reported at end of frame ...
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Kconfig2
-rw-r--r--net/mac80211/Makefile1
-rw-r--r--net/mac80211/aes_cmac.c18
-rw-r--r--net/mac80211/agg-rx.c2
-rw-r--r--net/mac80211/agg-tx.c14
-rw-r--r--net/mac80211/cfg.c499
-rw-r--r--net/mac80211/chan.c411
-rw-r--r--net/mac80211/debugfs.h6
-rw-r--r--net/mac80211/debugfs_key.c23
-rw-r--r--net/mac80211/debugfs_netdev.c78
-rw-r--r--net/mac80211/debugfs_sta.c59
-rw-r--r--net/mac80211/driver-ops.h140
-rw-r--r--net/mac80211/ht.c4
-rw-r--r--net/mac80211/ibss.c171
-rw-r--r--net/mac80211/ieee80211_i.h214
-rw-r--r--net/mac80211/iface.c143
-rw-r--r--net/mac80211/key.c15
-rw-r--r--net/mac80211/key.h11
-rw-r--r--net/mac80211/main.c150
-rw-r--r--net/mac80211/mesh.c82
-rw-r--r--net/mac80211/mesh.h18
-rw-r--r--net/mac80211/mesh_plink.c64
-rw-r--r--net/mac80211/mesh_sync.c105
-rw-r--r--net/mac80211/mlme.c735
-rw-r--r--net/mac80211/offchannel.c22
-rw-r--r--net/mac80211/pm.c46
-rw-r--r--net/mac80211/rate.c5
-rw-r--r--net/mac80211/rate.h12
-rw-r--r--net/mac80211/rc80211_minstrel.c9
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c8
-rw-r--r--net/mac80211/rx.c250
-rw-r--r--net/mac80211/scan.c61
-rw-r--r--net/mac80211/sta_info.c69
-rw-r--r--net/mac80211/sta_info.h31
-rw-r--r--net/mac80211/status.c169
-rw-r--r--net/mac80211/trace.h246
-rw-r--r--net/mac80211/tx.c291
-rw-r--r--net/mac80211/util.c363
-rw-r--r--net/mac80211/vht.c35
-rw-r--r--net/mac80211/wme.c40
-rw-r--r--net/mac80211/wpa.c5
41 files changed, 3179 insertions, 1448 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 63af25458fda..b4ecf267a34b 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -248,7 +248,7 @@ config MAC80211_MHWMP_DEBUG
Do not select this option.
config MAC80211_MESH_SYNC_DEBUG
- bool "Verbose mesh mesh synchronization debugging"
+ bool "Verbose mesh synchronization debugging"
depends on MAC80211_DEBUG_MENU
depends on MAC80211_MESH
---help---
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index a7dd110faafa..4911202334d9 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -8,6 +8,7 @@ mac80211-y := \
wpa.o \
scan.o offchannel.o \
ht.o agg-tx.o agg-rx.o \
+ vht.o \
ibss.o \
iface.o \
rate.o \
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
index a04752e91023..537488cbf941 100644
--- a/net/mac80211/aes_cmac.c
+++ b/net/mac80211/aes_cmac.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/crypto.h>
+#include <linux/export.h>
#include <linux/err.h>
#include <crypto/aes.h>
@@ -126,3 +127,20 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
{
crypto_free_cipher(tfm);
}
+
+void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
+ u8 *k1, u8 *k2)
+{
+ u8 l[AES_BLOCK_SIZE] = {};
+ struct ieee80211_key *key =
+ container_of(keyconf, struct ieee80211_key, conf);
+
+ crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l);
+
+ memcpy(k1, l, AES_BLOCK_SIZE);
+ gf_mulx(k1);
+
+ memcpy(k2, k1, AES_BLOCK_SIZE);
+ gf_mulx(k2);
+}
+EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2);
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 186d9919b043..808338a1bce5 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -118,7 +118,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
return;
}
- for (i = 0; i < STA_TID_NUM; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
if (ba_rx_bitmap & BIT(i))
set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 3195a6307f50..eb9df22418f0 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -445,10 +445,10 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
trace_api_start_tx_ba_session(pubsta, tid);
- if (WARN_ON(!local->ops->ampdu_action))
+ if (WARN_ON_ONCE(!local->ops->ampdu_action))
return -EINVAL;
- if ((tid >= STA_TID_NUM) ||
+ if ((tid >= IEEE80211_NUM_TIDS) ||
!(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) ||
(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW))
return -EINVAL;
@@ -605,9 +605,9 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
trace_api_start_tx_ba_cb(sdata, ra, tid);
- if (tid >= STA_TID_NUM) {
+ if (tid >= IEEE80211_NUM_TIDS) {
ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
+ tid, IEEE80211_NUM_TIDS);
return;
}
@@ -687,7 +687,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
if (!local->ops->ampdu_action)
return -EINVAL;
- if (tid >= STA_TID_NUM)
+ if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL;
spin_lock_bh(&sta->lock);
@@ -722,9 +722,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
trace_api_stop_tx_ba_cb(sdata, ra, tid);
- if (tid >= STA_TID_NUM) {
+ if (tid >= IEEE80211_NUM_TIDS) {
ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
+ tid, IEEE80211_NUM_TIDS);
return;
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7371f676cf41..5c61677487cf 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -370,29 +370,64 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
return 0;
}
-static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
-{
- if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
- struct ieee80211_supported_band *sband;
- sband = sta->local->hw.wiphy->bands[
- sta->local->oper_channel->band];
- rate->legacy = sband->bitrates[idx].bitrate;
- } else
- rate->mcs = idx;
-}
-
void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo)
{
rinfo->flags = 0;
- if (rate->flags & IEEE80211_TX_RC_MCS)
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
rinfo->flags |= RATE_INFO_FLAGS_MCS;
+ rinfo->mcs = rate->idx;
+ } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+ rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
+ rinfo->nss = ieee80211_rate_get_vht_nss(rate);
+ } else {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ ieee80211_get_sdata_band(sta->sdata)];
+ rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+ }
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
- rate_idx_to_bitrate(rinfo, sta, rate->idx);
+}
+
+void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
+{
+ rinfo->flags = 0;
+
+ if (sta->last_rx_rate_flag & RX_FLAG_HT) {
+ rinfo->flags |= RATE_INFO_FLAGS_MCS;
+ rinfo->mcs = sta->last_rx_rate_idx;
+ } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
+ rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rinfo->nss = sta->last_rx_rate_vht_nss;
+ rinfo->mcs = sta->last_rx_rate_idx;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+ sband = sta->local->hw.wiphy->bands[
+ ieee80211_get_sdata_band(sta->sdata)];
+ rinfo->legacy =
+ sband->bitrates[sta->last_rx_rate_idx].bitrate;
+ }
+
+ if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
+ rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
+ if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
}
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
@@ -441,15 +476,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
}
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-
- sinfo->rxrate.flags = 0;
- if (sta->last_rx_rate_flag & RX_FLAG_HT)
- sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
- if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
- sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
- if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
- sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
- rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx);
+ sta_set_rate_info_rx(sta, &sinfo->rxrate);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
@@ -532,6 +559,8 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
u64 *data)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *channel;
struct sta_info *sta;
struct ieee80211_local *local = sdata->local;
struct station_info sinfo;
@@ -607,19 +636,26 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
do_survey:
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
/* Get survey stats for current channel */
- q = 0;
- while (true) {
- survey.filled = 0;
- if (drv_get_survey(local, q, &survey) != 0) {
- survey.filled = 0;
- break;
- }
+ survey.filled = 0;
- if (survey.channel &&
- (local->oper_channel->center_freq ==
- survey.channel->center_freq))
- break;
- q++;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (chanctx_conf)
+ channel = chanctx_conf->def.chan;
+ else
+ channel = NULL;
+ rcu_read_unlock();
+
+ if (channel) {
+ q = 0;
+ do {
+ survey.filled = 0;
+ if (drv_get_survey(local, q, &survey) != 0) {
+ survey.filled = 0;
+ break;
+ }
+ q++;
+ } while (channel != survey.channel);
}
if (survey.filled)
@@ -724,47 +760,37 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
-static int ieee80211_set_channel(struct wiphy *wiphy,
- struct net_device *netdev,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = NULL;
-
- if (netdev)
- sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
-
- switch (ieee80211_get_channel_mode(local, NULL)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (local->oper_channel != chan ||
- (!sdata && local->_oper_channel_type != channel_type))
- return -EBUSY;
- if (!sdata && local->_oper_channel_type == channel_type)
- return 0;
- break;
- case CHAN_MODE_UNDEFINED:
- break;
- }
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 0;
- if (!ieee80211_set_channel_type(local, sdata, channel_type))
- return -EBUSY;
+ if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
+ return 0;
- local->oper_channel = chan;
+ mutex_lock(&local->iflist_mtx);
+ if (local->use_chanctx) {
+ sdata = rcu_dereference_protected(
+ local->monitor_sdata,
+ lockdep_is_held(&local->iflist_mtx));
+ if (sdata) {
+ ieee80211_vif_release_channel(sdata);
+ ret = ieee80211_vif_use_channel(sdata, chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ }
+ } else if (local->open_count == local->monitors) {
+ local->_oper_channel = chandef->chan;
+ local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
+ ieee80211_hw_config(local, 0);
+ }
- /* auto-detects changes */
- ieee80211_hw_config(local, 0);
+ if (ret == 0)
+ local->monitor_chandef = *chandef;
+ mutex_unlock(&local->iflist_mtx);
- return 0;
-}
-
-static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
-{
- return ieee80211_set_channel(wiphy, NULL, chan, channel_type);
+ return ret;
}
static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
@@ -872,15 +898,20 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
u32 changed = BSS_CHANGED_BEACON_INT |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON |
- BSS_CHANGED_SSID;
+ BSS_CHANGED_SSID |
+ BSS_CHANGED_P2P_PS;
int err;
old = rtnl_dereference(sdata->u.ap.beacon);
if (old)
return -EALREADY;
- err = ieee80211_set_channel(wiphy, dev, params->channel,
- params->channel_type);
+ /* TODO: make hostapd tell us what it wants */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ err = ieee80211_vif_use_channel(sdata, &params->chandef,
+ IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -907,11 +938,23 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->vif.bss_conf.hidden_ssid =
(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
+ sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
+ sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
+
err = ieee80211_assign_beacon(sdata, &params->beacon);
if (err < 0)
return err;
changed |= err;
+ err = drv_start_ap(sdata->local, sdata);
+ if (err) {
+ old = rtnl_dereference(sdata->u.ap.beacon);
+ if (old)
+ kfree_rcu(old, rcu_head);
+ RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ return err;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
netif_carrier_on(dev);
@@ -943,26 +986,40 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata, *vlan;
- struct beacon_data *old;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *vlan;
+ struct ieee80211_local *local = sdata->local;
+ struct beacon_data *old_beacon;
+ struct probe_resp *old_probe_resp;
- old = rtnl_dereference(sdata->u.ap.beacon);
- if (!old)
+ old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+ if (!old_beacon)
return -ENOENT;
+ old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+ /* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev);
netif_carrier_off(dev);
+ /* remove beacon and probe response */
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
+ kfree_rcu(old_beacon, rcu_head);
+ if (old_probe_resp)
+ kfree_rcu(old_probe_resp, rcu_head);
- kfree_rcu(old, rcu_head);
-
- sta_info_flush(sdata->local, sdata);
+ sta_info_flush(local, sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
+ drv_stop_ap(sdata->local, sdata);
+
+ /* free all potentially still buffered bcast frames */
+ local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
+ skb_queue_purge(&sdata->u.ap.ps.bc_buf);
+
+ ieee80211_vif_release_channel(sdata);
+
return 0;
}
@@ -1019,9 +1076,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
int i, j;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
u32 mask, set;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[band];
mask = params->sta_flags_mask;
set = params->sta_flags_set;
@@ -1136,7 +1194,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
rates |= BIT(j);
}
}
- sta->sta.supp_rates[local->oper_channel->band] = rates;
+ sta->sta.supp_rates[band] = rates;
}
if (params->ht_capa)
@@ -1144,6 +1202,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
params->ht_capa,
&sta->sta.ht_cap);
+ if (params->vht_capa)
+ ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ params->vht_capa,
+ &sta->sta.vht_cap);
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
@@ -1664,8 +1727,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
if (err)
return err;
- err = ieee80211_set_channel(wiphy, dev, setup->channel,
- setup->channel_type);
+ /* can mesh use other SMPS modes? */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ err = ieee80211_vif_use_channel(sdata, &setup->chandef,
+ IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -1679,6 +1746,7 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
ieee80211_stop_mesh(sdata);
+ ieee80211_vif_release_channel(sdata);
return 0;
}
@@ -1688,10 +1756,14 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ enum ieee80211_band band;
u32 changed = 0;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (!rtnl_dereference(sdata->u.ap.beacon))
+ return -ENOENT;
+
+ band = ieee80211_get_sdata_band(sdata);
if (params->use_cts_prot >= 0) {
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
@@ -1704,7 +1776,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
}
if (!sdata->vif.bss_conf.use_short_slot &&
- sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) {
+ band == IEEE80211_BAND_5GHZ) {
sdata->vif.bss_conf.use_short_slot = true;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -1718,9 +1790,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
if (params->basic_rates) {
int i, j;
u32 rates = 0;
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_supported_band *sband =
- wiphy->bands[local->oper_channel->band];
+ struct ieee80211_supported_band *sband = wiphy->bands[band];
for (i = 0; i < params->basic_rates_len; i++) {
int rate = (params->basic_rates[i] & 0x7f) * 5;
@@ -1746,6 +1816,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
changed |= BSS_CHANGED_HT;
}
+ if (params->p2p_ctwindow >= 0) {
+ sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
+ changed |= BSS_CHANGED_P2P_PS;
+ }
+
+ if (params->p2p_opp_ps >= 0) {
+ sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
+ changed |= BSS_CHANGED_P2P_PS;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
@@ -1829,7 +1909,16 @@ static int ieee80211_scan(struct wiphy *wiphy,
* beaconing hasn't been configured yet
*/
case NL80211_IFTYPE_AP:
- if (sdata->u.ap.beacon)
+ /*
+ * If the scan has been forced (and the driver supports
+ * forcing), don't care about being beaconing already.
+ * This will create problems to the attached stations (e.g. all
+ * the frames sent while scanning on other channel will be
+ * lost)
+ */
+ if (sdata->u.ap.beacon &&
+ (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
+ !(req->flags & NL80211_SCAN_FLAG_AP)))
return -EOPNOTSUPP;
break;
default:
@@ -1872,20 +1961,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (ieee80211_get_channel_mode(local, sdata)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (local->oper_channel == req->bss->channel)
- break;
- return -EBUSY;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
}
@@ -1904,30 +1979,22 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (ieee80211_get_channel_mode(local, sdata)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (!params->channel_fixed)
- return -EBUSY;
- if (local->oper_channel == params->channel)
- break;
- return -EBUSY;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
- return ieee80211_ibss_join(sdata, params);
+ return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
}
static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
{
+ return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
+}
+
+static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
+ int rate[IEEE80211_NUM_BANDS])
+{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- return ieee80211_ibss_leave(sdata);
+ memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(rate));
+
+ return 0;
}
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
@@ -1956,10 +2023,16 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
return err;
}
- if (changed & WIPHY_PARAM_RETRY_SHORT)
+ if (changed & WIPHY_PARAM_RETRY_SHORT) {
+ if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
+ return -EINVAL;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
- if (changed & WIPHY_PARAM_RETRY_LONG)
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG) {
+ if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
+ return -EINVAL;
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
+ }
if (changed &
(WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
@@ -1968,41 +2041,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
}
static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_channel *chan = local->oper_channel;
- u32 changes = 0;
+ struct ieee80211_sub_if_data *sdata;
+
+ if (wdev) {
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ case NL80211_TX_POWER_FIXED:
+ if (mbm < 0 || (mbm % 100))
+ return -EOPNOTSUPP;
+ sdata->user_power_level = MBM_TO_DBM(mbm);
+ break;
+ }
+
+ ieee80211_recalc_txpower(sdata);
+
+ return 0;
+ }
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
- local->user_power_level = -1;
+ local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
break;
case NL80211_TX_POWER_LIMITED:
- if (mbm < 0 || (mbm % 100))
- return -EOPNOTSUPP;
- local->user_power_level = MBM_TO_DBM(mbm);
- break;
case NL80211_TX_POWER_FIXED:
if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP;
- /* TODO: move to cfg80211 when it knows the channel */
- if (MBM_TO_DBM(mbm) > chan->max_power)
- return -EINVAL;
local->user_power_level = MBM_TO_DBM(mbm);
break;
}
- ieee80211_hw_config(local, changes);
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list)
+ sdata->user_power_level = local->user_power_level;
+ list_for_each_entry(sdata, &local->interfaces, list)
+ ieee80211_recalc_txpower(sdata);
+ mutex_unlock(&local->iflist_mtx);
return 0;
}
-static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
+static int ieee80211_get_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ int *dbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- *dbm = local->hw.conf.power_level;
+ if (!local->use_chanctx)
+ *dbm = local->hw.conf.power_level;
+ else
+ *dbm = sdata->vif.bss_conf.txpower;
return 0;
}
@@ -2067,13 +2164,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
/*
* If not associated, or current association is not an HT
- * association, there's no need to send an action frame.
+ * association, there's no need to do anything, just store
+ * the new value until we associate.
*/
if (!sdata->u.mgd.associated ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
- ieee80211_recalc_smps(sdata->local);
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
- }
ap = sdata->u.mgd.associated->bssid;
@@ -2179,7 +2275,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
static int ieee80211_start_roc_work(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
unsigned int duration, u64 *cookie,
struct sk_buff *txskb)
{
@@ -2189,12 +2284,14 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
lockdep_assert_held(&local->mtx);
+ if (local->use_chanctx && !local->ops->remain_on_channel)
+ return -EOPNOTSUPP;
+
roc = kzalloc(sizeof(*roc), GFP_KERNEL);
if (!roc)
return -ENOMEM;
roc->chan = channel;
- roc->chan_type = channel_type;
roc->duration = duration;
roc->req_duration = duration;
roc->frame = txskb;
@@ -2227,7 +2324,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, channel, channel_type, duration);
+ ret = drv_remain_on_channel(local, sdata, channel, duration);
if (ret) {
kfree(roc);
return ret;
@@ -2238,7 +2335,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
out_check_combine:
list_for_each_entry(tmp, &local->roc_list, list) {
- if (tmp->chan != channel || tmp->chan_type != channel_type)
+ if (tmp->chan != channel || tmp->sdata != sdata)
continue;
/*
@@ -2332,13 +2429,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
list_add_tail(&roc->list, &local->roc_list);
/*
- * cookie is either the roc (for normal roc)
+ * cookie is either the roc cookie (for normal roc)
* or the SKB (for mgmt TX)
*/
- if (txskb)
+ if (!txskb) {
+ /* local->mtx protects this */
+ local->roc_cookie_counter++;
+ roc->cookie = local->roc_cookie_counter;
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(roc->cookie == 0)) {
+ roc->cookie = 1;
+ local->roc_cookie_counter++;
+ }
+ *cookie = roc->cookie;
+ } else {
*cookie = (unsigned long)txskb;
- else
- *cookie = (unsigned long)roc;
+ }
return 0;
}
@@ -2346,7 +2452,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
static int ieee80211_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
@@ -2355,7 +2460,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
int ret;
mutex_lock(&local->mtx);
- ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+ ret = ieee80211_start_roc_work(local, sdata, chan,
duration, cookie, NULL);
mutex_unlock(&local->mtx);
@@ -2373,7 +2478,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
struct ieee80211_roc_work *dep, *tmp2;
list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
- if (!mgmt_tx && (unsigned long)dep != cookie)
+ if (!mgmt_tx && dep->cookie != cookie)
continue;
else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
continue;
@@ -2385,7 +2490,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
return 0;
}
- if (!mgmt_tx && (unsigned long)roc != cookie)
+ if (!mgmt_tx && roc->cookie != cookie)
continue;
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
continue;
@@ -2448,10 +2553,8 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie)
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_local *local = sdata->local;
@@ -2515,10 +2618,16 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
/* Check if the operating channel is the requested channel */
if (!need_offchan) {
- need_offchan = chan != local->oper_channel;
- if (channel_type_valid &&
- channel_type != local->_oper_channel_type)
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf)
+ need_offchan = chan != chanctx_conf->def.chan;
+ else
need_offchan = true;
+ rcu_read_unlock();
}
if (need_offchan && !offchan) {
@@ -2552,7 +2661,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
local->hw.offchannel_tx_hw_queue;
/* This will handle all kinds of coalescing and immediate TX */
- ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+ ret = ieee80211_start_roc_work(local, sdata, chan,
wait, cookie, skb);
if (ret)
kfree_skb(skb);
@@ -2670,7 +2779,7 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
u16 capab;
capab = 0;
- if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
+ if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
return capab;
if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
@@ -2702,7 +2811,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_tdls_data *tf;
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -2722,10 +2831,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_req.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_RESPONSE:
@@ -2738,10 +2845,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_CONFIRM:
@@ -2779,7 +2884,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_mgmt *mgmt;
mgmt = (void *)skb_put(skb, 24);
@@ -2802,10 +2907,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt->u.action.u.tdls_discover_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
default:
@@ -2822,7 +2925,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct ieee80211_tx_info *info;
struct sk_buff *skb = NULL;
bool send_direct;
int ret;
@@ -2848,7 +2950,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
if (!skb)
return -ENOMEM;
- info = IEEE80211_SKB_CB(skb);
skb_reserve(skb, local->hw.extra_tx_headroom);
switch (action_code) {
@@ -2985,12 +3086,19 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
bool qos;
struct ieee80211_tx_info *info;
struct sta_info *sta;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_band band;
rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ band = chanctx_conf->def.chan->band;
sta = sta_info_get(sdata, peer);
if (sta) {
qos = test_sta_flag(sta, WLAN_STA_WME);
- rcu_read_unlock();
} else {
rcu_read_unlock();
return -ENOLINK;
@@ -3008,8 +3116,10 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
- if (!skb)
+ if (!skb) {
+ rcu_read_unlock();
return -ENOMEM;
+ }
skb->dev = dev;
@@ -3034,21 +3144,31 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
local_bh_enable();
+ rcu_read_unlock();
*cookie = (unsigned long) skb;
return 0;
}
-static struct ieee80211_channel *
-ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
- enum nl80211_channel_type *type)
+static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int ret = -ENODATA;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (chanctx_conf) {
+ *chandef = chanctx_conf->def;
+ ret = 0;
+ }
+ rcu_read_unlock();
- *type = local->_oper_channel_type;
- return local->oper_channel;
+ return ret;
}
#ifdef CONFIG_PM
@@ -3103,6 +3223,7 @@ struct cfg80211_ops mac80211_config_ops = {
.disassoc = ieee80211_disassoc,
.join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss,
+ .set_mcast_rate = ieee80211_set_mcast_rate,
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 0bfc914ddd15..53f03120db55 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -3,168 +3,347 @@
*/
#include <linux/nl80211.h>
+#include <linux/export.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"
+#include "driver-ops.h"
-static enum ieee80211_chan_mode
-__ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore)
+static void ieee80211_change_chandef(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ const struct cfg80211_chan_def *chandef)
{
+ if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
+ return;
+
+ WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
+
+ ctx->conf.def = *chandef;
+ drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
+ ieee80211_hw_config(local, 0);
+ }
+}
+
+static struct ieee80211_chanctx *
+ieee80211_find_chanctx(struct ieee80211_local *local,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode)
+{
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
+ return NULL;
+
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
+ const struct cfg80211_chan_def *compat;
+
+ if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
+ continue;
+
+ compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
+ if (!compat)
+ continue;
+
+ ieee80211_change_chandef(local, ctx, compat);
+
+ return ctx;
+ }
+
+ return NULL;
+}
+
+static struct ieee80211_chanctx *
+ieee80211_new_chanctx(struct ieee80211_local *local,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode)
+{
+ struct ieee80211_chanctx *ctx;
+ int err;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->conf.def = *chandef;
+ ctx->conf.rx_chains_static = 1;
+ ctx->conf.rx_chains_dynamic = 1;
+ ctx->mode = mode;
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type =
+ cfg80211_get_chandef_type(chandef);
+ local->_oper_channel = chandef->chan;
+ ieee80211_hw_config(local, 0);
+ } else {
+ err = drv_add_chanctx(local, ctx);
+ if (err) {
+ kfree(ctx);
+ return ERR_PTR(err);
+ }
+ }
+
+ list_add_rcu(&ctx->list, &local->chanctx_list);
+
+ return ctx;
+}
+
+static void ieee80211_free_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ WARN_ON_ONCE(ctx->refcount != 0);
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = NL80211_CHAN_NO_HT;
+ ieee80211_hw_config(local, 0);
+ } else {
+ drv_remove_chanctx(local, ctx);
+ }
+
+ list_del_rcu(&ctx->list);
+ kfree_rcu(ctx, rcu_head);
+}
+
+static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ret = drv_assign_vif_chanctx(local, sdata, ctx);
+ if (ret)
+ return ret;
+
+ rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
+ ctx->refcount++;
+
+ ieee80211_recalc_txpower(sdata);
+
+ return 0;
+}
+
+static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ struct ieee80211_chanctx_conf *conf = &ctx->conf;
struct ieee80211_sub_if_data *sdata;
+ const struct cfg80211_chan_def *compat = NULL;
- lockdep_assert_held(&local->iflist_mtx);
+ lockdep_assert_held(&local->chanctx_mtx);
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata == ignore)
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+ if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
continue;
+ if (!compat)
+ compat = &sdata->vif.bss_conf.chandef;
+
+ compat = cfg80211_chandef_compatible(
+ &sdata->vif.bss_conf.chandef, compat);
+ if (!compat)
+ break;
+ }
+ rcu_read_unlock();
+
+ if (WARN_ON_ONCE(!compat))
+ return;
+
+ ieee80211_change_chandef(local, ctx, compat);
+}
+
+static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ctx->refcount--;
+ rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+
+ if (ctx->refcount > 0) {
+ ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ }
+}
+
+static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (!conf)
+ return;
+
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+ ieee80211_unassign_vif_chanctx(sdata, ctx);
+ if (ctx->refcount == 0)
+ ieee80211_free_chanctx(local, ctx);
+}
+
+void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx)
+{
+ struct ieee80211_sub_if_data *sdata;
+ u8 rx_chains_static, rx_chains_dynamic;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ rx_chains_static = 1;
+ rx_chains_dynamic = 1;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ u8 needed_static, needed_dynamic;
+
if (!ieee80211_sdata_running(sdata))
continue;
+ if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
+ &chanctx->conf)
+ continue;
+
switch (sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_DEVICE:
continue;
case NL80211_IFTYPE_STATION:
if (!sdata->u.mgd.associated)
continue;
break;
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->u.ibss.ssid_len)
- continue;
- if (!sdata->u.ibss.fixed_channel)
- return CHAN_MODE_HOPPING;
- break;
case NL80211_IFTYPE_AP_VLAN:
- /* will also have _AP interface */
continue;
case NL80211_IFTYPE_AP:
- if (!sdata->u.ap.beacon)
- continue;
- break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
- if (!sdata->wdev.mesh_id_len)
- continue;
break;
default:
+ WARN_ON_ONCE(1);
+ }
+
+ switch (sdata->smps_mode) {
+ default:
+ WARN_ONCE(1, "Invalid SMPS mode %d\n",
+ sdata->smps_mode);
+ /* fall through */
+ case IEEE80211_SMPS_OFF:
+ needed_static = sdata->needed_rx_chains;
+ needed_dynamic = sdata->needed_rx_chains;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ needed_static = 1;
+ needed_dynamic = sdata->needed_rx_chains;
+ break;
+ case IEEE80211_SMPS_STATIC:
+ needed_static = 1;
+ needed_dynamic = 1;
break;
}
- return CHAN_MODE_FIXED;
+ rx_chains_static = max(rx_chains_static, needed_static);
+ rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
}
+ rcu_read_unlock();
- return CHAN_MODE_UNDEFINED;
-}
-
-enum ieee80211_chan_mode
-ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore)
-{
- enum ieee80211_chan_mode mode;
+ if (!local->use_chanctx) {
+ if (rx_chains_static > 1)
+ local->smps_mode = IEEE80211_SMPS_OFF;
+ else if (rx_chains_dynamic > 1)
+ local->smps_mode = IEEE80211_SMPS_DYNAMIC;
+ else
+ local->smps_mode = IEEE80211_SMPS_STATIC;
+ ieee80211_hw_config(local, 0);
+ }
- mutex_lock(&local->iflist_mtx);
- mode = __ieee80211_get_channel_mode(local, ignore);
- mutex_unlock(&local->iflist_mtx);
+ if (rx_chains_static == chanctx->conf.rx_chains_static &&
+ rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
+ return;
- return mode;
+ chanctx->conf.rx_chains_static = rx_chains_static;
+ chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
+ drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
}
-static enum nl80211_channel_type
-ieee80211_get_superchan(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode)
{
- enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
- struct ieee80211_sub_if_data *tmp;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx *ctx;
+ int ret;
- mutex_lock(&local->iflist_mtx);
- list_for_each_entry(tmp, &local->interfaces, list) {
- if (tmp == sdata)
- continue;
-
- if (!ieee80211_sdata_running(tmp))
- continue;
+ WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
- switch (tmp->vif.bss_conf.channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
- if (superchan > tmp->vif.bss_conf.channel_type)
- break;
+ mutex_lock(&local->chanctx_mtx);
+ __ieee80211_vif_release_channel(sdata);
- superchan = tmp->vif.bss_conf.channel_type;
- break;
- case NL80211_CHAN_HT40PLUS:
- WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
- superchan = NL80211_CHAN_HT40PLUS;
- break;
- case NL80211_CHAN_HT40MINUS:
- WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
- superchan = NL80211_CHAN_HT40MINUS;
- break;
- }
+ ctx = ieee80211_find_chanctx(local, chandef, mode);
+ if (!ctx)
+ ctx = ieee80211_new_chanctx(local, chandef, mode);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto out;
}
- mutex_unlock(&local->iflist_mtx);
- return superchan;
-}
+ sdata->vif.bss_conf.chandef = *chandef;
-static bool
-ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
- enum nl80211_channel_type chantype2,
- enum nl80211_channel_type *compat)
-{
- /*
- * start out with chantype1 being the result,
- * overwriting later if needed
- */
- if (compat)
- *compat = chantype1;
-
- switch (chantype1) {
- case NL80211_CHAN_NO_HT:
- if (compat)
- *compat = chantype2;
- break;
- case NL80211_CHAN_HT20:
- /*
- * allow any change that doesn't go to no-HT
- * (if it already is no-HT no change is needed)
- */
- if (chantype2 == NL80211_CHAN_NO_HT)
- break;
- if (compat)
- *compat = chantype2;
- break;
- case NL80211_CHAN_HT40PLUS:
- case NL80211_CHAN_HT40MINUS:
- /* allow smaller bandwidth and same */
- if (chantype2 == NL80211_CHAN_NO_HT)
- break;
- if (chantype2 == NL80211_CHAN_HT20)
- break;
- if (chantype2 == chantype1)
- break;
- return false;
+ ret = ieee80211_assign_vif_chanctx(sdata, ctx);
+ if (ret) {
+ /* if assign fails refcount stays the same */
+ if (ctx->refcount == 0)
+ ieee80211_free_chanctx(local, ctx);
+ goto out;
}
- return true;
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ out:
+ mutex_unlock(&local->chanctx_mtx);
+ return ret;
}
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype)
+void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
{
- enum nl80211_channel_type superchan;
- enum nl80211_channel_type compatchan;
-
- superchan = ieee80211_get_superchan(local, sdata);
- if (!ieee80211_channel_types_are_compatible(superchan, chantype,
- &compatchan))
- return false;
+ WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
- local->_oper_channel_type = compatchan;
-
- if (sdata)
- sdata->vif.bss_conf.channel_type = chantype;
+ mutex_lock(&sdata->local->chanctx_mtx);
+ __ieee80211_vif_release_channel(sdata);
+ mutex_unlock(&sdata->local->chanctx_mtx);
+}
- return true;
+void ieee80211_iter_chan_contexts_atomic(
+ struct ieee80211_hw *hw,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_chanctx *ctx;
+ rcu_read_lock();
+ list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
+ iter(hw, &ctx->conf, iter_data);
+ rcu_read_unlock();
}
+EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h
index 9be4e6d71d00..214ed4ecd739 100644
--- a/net/mac80211/debugfs.h
+++ b/net/mac80211/debugfs.h
@@ -2,9 +2,9 @@
#define __MAC80211_DEBUGFS_H
#ifdef CONFIG_MAC80211_DEBUGFS
-extern void debugfs_hw_add(struct ieee80211_local *local);
-extern int mac80211_format_buffer(char __user *userbuf, size_t count,
- loff_t *ppos, char *fmt, ...);
+void debugfs_hw_add(struct ieee80211_local *local);
+int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count,
+ loff_t *ppos, char *fmt, ...);
#else
static inline void debugfs_hw_add(struct ieee80211_local *local)
{
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 090d08ff22c4..c3a3082b72e5 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -116,7 +116,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
- char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf;
+ char buf[14*IEEE80211_NUM_TIDS+1], *p = buf;
int i, len;
const u8 *rpn;
@@ -126,7 +126,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
len = scnprintf(buf, sizeof(buf), "\n");
break;
case WLAN_CIPHER_SUITE_TKIP:
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
p += scnprintf(p, sizeof(buf)+buf-p,
"%08x %04x\n",
key->u.tkip.rx[i].iv32,
@@ -134,7 +134,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
len = p - buf;
break;
case WLAN_CIPHER_SUITE_CCMP:
- for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
rpn = key->u.ccmp.rx_pn[i];
p += scnprintf(p, sizeof(buf)+buf-p,
"%02x%02x%02x%02x%02x%02x\n",
@@ -199,6 +199,22 @@ static ssize_t key_icverrors_read(struct file *file, char __user *userbuf,
}
KEY_OPS(icverrors);
+static ssize_t key_mic_failures_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_key *key = file->private_data;
+ char buf[20];
+ int len;
+
+ if (key->conf.cipher != WLAN_CIPHER_SUITE_TKIP)
+ return -EINVAL;
+
+ len = scnprintf(buf, sizeof(buf), "%u\n", key->u.tkip.mic_failures);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+KEY_OPS(mic_failures);
+
static ssize_t key_key_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
@@ -260,6 +276,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
DEBUGFS_ADD(rx_spec);
DEBUGFS_ADD(replays);
DEBUGFS_ADD(icverrors);
+ DEBUGFS_ADD(mic_failures);
DEBUGFS_ADD(key);
DEBUGFS_ADD(ifindex);
};
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 6d5aec9418ee..cbde5cc49a40 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/if.h>
+#include <linux/if_ether.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
@@ -167,7 +168,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
IEEE80211_IF_FILE(flags, flags, HEX);
IEEE80211_IF_FILE(state, state, LHEX);
-IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC);
+IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
+IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC);
+IEEE80211_IF_FILE(user_power_level, user_power_level, DEC);
+
+static ssize_t
+ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ int len;
+
+ len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n",
+ sdata->vif.hw_queue[IEEE80211_AC_VO],
+ sdata->vif.hw_queue[IEEE80211_AC_VI],
+ sdata->vif.hw_queue[IEEE80211_AC_BE],
+ sdata->vif.hw_queue[IEEE80211_AC_BK]);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ len += scnprintf(buf + len, buflen - len, "cab queue: %d\n",
+ sdata->vif.cab_queue);
+
+ return len;
+}
+__IEEE80211_IF_FILE(hw_queues, NULL);
/* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
@@ -217,7 +240,7 @@ static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
return snprintf(buf, buflen, "request: %s\nused: %s\n",
smps_modes[sdata->u.mgd.req_smps],
- smps_modes[sdata->u.mgd.ap_smps]);
+ smps_modes[sdata->smps_mode]);
}
static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
@@ -245,27 +268,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test(
return -EOPNOTSUPP;
}
-static int hwaddr_aton(const char *txt, u8 *addr)
-{
- int i;
-
- for (i = 0; i < ETH_ALEN; i++) {
- int a, b;
-
- a = hex_to_bin(*txt++);
- if (a < 0)
- return -1;
- b = hex_to_bin(*txt++);
- if (b < 0)
- return -1;
- *addr++ = (a << 4) | b;
- if (i < 5 && *txt++ != ':')
- return -1;
- }
-
- return 0;
-}
-
static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
{
@@ -275,13 +277,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_hdr *hdr;
__le16 fc;
- /*
- * Assume colon-delimited MAC address with possible white space
- * following.
- */
- if (buflen < 3 * ETH_ALEN - 1)
- return -EINVAL;
- if (hwaddr_aton(buf, addr) < 0)
+ if (!mac_pton(buf, addr))
return -EINVAL;
if (!ieee80211_sdata_running(sdata))
@@ -307,13 +303,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
- if (sdata->vif.bss_conf.bssid == NULL) {
+ mutex_lock(&sdata->u.mgd.mtx);
+ if (!sdata->u.mgd.associated) {
+ mutex_unlock(&sdata->u.mgd.mtx);
dev_kfree_skb(skb);
return -ENOTCONN;
}
- memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN);
+ memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr->addr3, addr, ETH_ALEN);
+ mutex_unlock(&sdata->u.mgd.mtx);
break;
default:
dev_kfree_skb(skb);
@@ -395,14 +394,14 @@ __IEEE80211_IF_FILE_W(uapsd_max_sp_len);
/* AP attributes */
IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC);
-IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
-IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
+IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC);
+IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC);
static ssize_t ieee80211_if_fmt_num_buffered_multicast(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
return scnprintf(buf, buflen, "%u\n",
- skb_queue_len(&sdata->u.ap.ps_bc_buf));
+ skb_queue_len(&sdata->u.ap.ps.bc_buf));
}
__IEEE80211_IF_FILE(num_buffered_multicast, NULL);
@@ -443,7 +442,7 @@ static ssize_t ieee80211_if_parse_tsf(
}
ret = kstrtoull(buf, 10, &tsf);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (tsf_is_delta)
tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
if (local->ops->set_tsf) {
@@ -471,7 +470,7 @@ IEEE80211_IF_FILE(dropped_frames_congestion,
u.mesh.mshstats.dropped_frames_congestion, DEC);
IEEE80211_IF_FILE(dropped_frames_no_route,
u.mesh.mshstats.dropped_frames_no_route, DEC);
-IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
+IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
/* Mesh parameters */
IEEE80211_IF_FILE(dot11MeshMaxRetries,
@@ -531,6 +530,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+ DEBUGFS_ADD(hw_queues);
}
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
@@ -631,7 +631,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(flags);
DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
+ DEBUGFS_ADD(txpower);
+ DEBUGFS_ADD(user_power_level);
+ DEBUGFS_ADD(ap_power_level);
if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
add_common_files(sdata);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 5ccec2c1e9f6..6fb1168b9f16 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -14,6 +14,7 @@
#include "debugfs.h"
#include "debugfs_sta.h"
#include "sta_info.h"
+#include "driver-ops.h"
/* sta attributtes */
@@ -52,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_FILE(aid, sta.aid, D);
STA_FILE(dev, sdata->name, S);
STA_FILE(last_signal, last_signal, D);
+STA_FILE(last_ack_signal, last_ack_signal, D);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
@@ -131,10 +133,10 @@ STA_OPS(connected_time);
static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[15*NUM_RX_DATA_QUEUES], *p = buf;
+ char buf[15*IEEE80211_NUM_TIDS], *p = buf;
int i;
struct sta_info *sta = file->private_data;
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%x ",
le16_to_cpu(sta->last_seq_ctrl[i]));
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
@@ -145,7 +147,7 @@ STA_OPS(last_seq_ctrl);
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[71 + STA_TID_NUM * 40], *p = buf;
+ char buf[71 + IEEE80211_NUM_TIDS * 40], *p = buf;
int i;
struct sta_info *sta = file->private_data;
struct tid_ampdu_rx *tid_rx;
@@ -158,7 +160,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf) + buf - p,
"TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]);
tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]);
@@ -218,9 +220,11 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
} else
return -EINVAL;
- tid = simple_strtoul(buf, NULL, 0);
+ ret = kstrtoul(buf, 0, &tid);
+ if (ret)
+ return ret;
- if (tid >= STA_TID_NUM)
+ if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL;
if (tx) {
@@ -320,6 +324,38 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
}
STA_OPS(ht_capa);
+static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct rate_info rinfo;
+ u16 rate;
+ sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
+ rate = cfg80211_calculate_bitrate(&rinfo);
+
+ return mac80211_format_buffer(userbuf, count, ppos,
+ "%d.%d MBit/s\n",
+ rate/10, rate%10);
+}
+STA_OPS(current_tx_rate);
+
+static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct rate_info rinfo;
+ u16 rate;
+
+ sta_set_rate_info_rx(sta, &rinfo);
+
+ rate = cfg80211_calculate_bitrate(&rinfo);
+
+ return mac80211_format_buffer(userbuf, count, ppos,
+ "%d.%d MBit/s\n",
+ rate/10, rate%10);
+}
+STA_OPS(last_rx_rate);
+
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -334,6 +370,8 @@ STA_OPS(ht_capa);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
u8 mac[3*ETH_ALEN];
@@ -366,6 +404,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(dev);
DEBUGFS_ADD(last_signal);
DEBUGFS_ADD(ht_capa);
+ DEBUGFS_ADD(last_ack_signal);
+ DEBUGFS_ADD(current_tx_rate);
+ DEBUGFS_ADD(last_rx_rate);
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
@@ -379,10 +420,16 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);
DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
+
+ drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
}
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
debugfs_remove_recursive(sta->debugfs.dir);
sta->debugfs.dir = NULL;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index da9003b20004..c6560cc7a9d6 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -490,6 +490,38 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+#ifdef CONFIG_MAC80211_DEBUGFS
+static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct dentry *dir)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ if (local->ops->sta_add_debugfs)
+ local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
+ sta, dir);
+}
+
+static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct dentry *dir)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ if (local->ops->sta_remove_debugfs)
+ local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
+ sta, dir);
+}
+#endif
+
static inline __must_check
int drv_sta_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -704,17 +736,17 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
}
static inline int drv_remain_on_channel(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *chan,
- enum nl80211_channel_type chantype,
unsigned int duration)
{
int ret;
might_sleep();
- trace_drv_remain_on_channel(local, chan, chantype, duration);
- ret = local->ops->remain_on_channel(&local->hw, chan, chantype,
- duration);
+ trace_drv_remain_on_channel(local, sdata, chan, duration);
+ ret = local->ops->remain_on_channel(&local->hw, &sdata->vif,
+ chan, duration);
trace_drv_return_int(local, ret);
return ret;
@@ -871,4 +903,104 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
local->ops->mgd_prepare_tx(&local->hw, &sdata->vif);
trace_drv_return_void(local);
}
+
+static inline int drv_add_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_drv_add_chanctx(local, ctx);
+ if (local->ops->add_chanctx)
+ ret = local->ops->add_chanctx(&local->hw, &ctx->conf);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline void drv_remove_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ trace_drv_remove_chanctx(local, ctx);
+ if (local->ops->remove_chanctx)
+ local->ops->remove_chanctx(&local->hw, &ctx->conf);
+ trace_drv_return_void(local);
+}
+
+static inline void drv_change_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ u32 changed)
+{
+ trace_drv_change_chanctx(local, ctx, changed);
+ if (local->ops->change_chanctx)
+ local->ops->change_chanctx(&local->hw, &ctx->conf, changed);
+ trace_drv_return_void(local);
+}
+
+static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ int ret = 0;
+
+ check_sdata_in_driver(sdata);
+
+ trace_drv_assign_vif_chanctx(local, sdata, ctx);
+ if (local->ops->assign_vif_chanctx)
+ ret = local->ops->assign_vif_chanctx(&local->hw,
+ &sdata->vif,
+ &ctx->conf);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ check_sdata_in_driver(sdata);
+
+ trace_drv_unassign_vif_chanctx(local, sdata, ctx);
+ if (local->ops->unassign_vif_chanctx)
+ local->ops->unassign_vif_chanctx(&local->hw,
+ &sdata->vif,
+ &ctx->conf);
+ trace_drv_return_void(local);
+}
+
+static inline int drv_start_ap(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ int ret = 0;
+
+ check_sdata_in_driver(sdata);
+
+ trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
+ if (local->ops->start_ap)
+ ret = local->ops->start_ap(&local->hw, &sdata->vif);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void drv_stop_ap(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ check_sdata_in_driver(sdata);
+
+ trace_drv_stop_ap(local, sdata);
+ if (local->ops->stop_ap)
+ local->ops->stop_ap(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
+static inline void drv_restart_complete(struct ieee80211_local *local)
+{
+ might_sleep();
+
+ trace_drv_restart_complete(local);
+ if (local->ops->restart_complete)
+ local->ops->restart_complete(&local->hw);
+ trace_drv_return_void(local);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 4b4538d63925..a71d891794a4 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -185,7 +185,7 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
cancel_work_sync(&sta->ampdu_mlme.work);
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
__ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_LEAVE_QBSS, tx);
@@ -209,7 +209,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
return;
mutex_lock(&sta->ampdu_mlme.mtx);
- for (tid = 0; tid < STA_TID_NUM; tid++) {
+ for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
___ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index c21e33d1abd0..8881fc77fb13 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -26,7 +26,6 @@
#include "rate.h"
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
@@ -39,7 +38,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const int beacon_int,
struct ieee80211_channel *chan,
const u32 basic_rates,
- const u16 capability, u64 tsf)
+ const u16 capability, u64 tsf,
+ bool creator)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
@@ -51,7 +51,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *bss;
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- enum nl80211_channel_type channel_type;
+ struct cfg80211_chan_def chandef;
lockdep_assert_held(&ifibss->mtx);
@@ -72,25 +72,29 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
/* if merging, indicate to driver that we leave the old IBSS */
if (sdata->vif.bss_conf.ibss_joined) {
sdata->vif.bss_conf.ibss_joined = false;
+ sdata->vif.bss_conf.ibss_creator = false;
netif_carrier_off(sdata->dev);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
}
- memcpy(ifibss->bssid, bssid, ETH_ALEN);
-
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- local->oper_channel = chan;
- channel_type = ifibss->channel_type;
- if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
- channel_type = NL80211_CHAN_HT20;
- if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- NL80211_CHAN_HT20));
+ cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
+ if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+ chandef.width = NL80211_CHAN_WIDTH_20;
+ chandef.center_freq1 = chan->center_freq;
+ }
+
+ ieee80211_vif_release_channel(sdata);
+ if (ieee80211_vif_use_channel(sdata, &chandef,
+ ifibss->fixed_channel ?
+ IEEE80211_CHANCTX_SHARED :
+ IEEE80211_CHANCTX_EXCLUSIVE)) {
+ sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+ return;
}
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ memcpy(ifibss->bssid, bssid, ETH_ALEN);
sband = local->hw.wiphy->bands[chan->band];
@@ -156,7 +160,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
ifibss->ie, ifibss->ie_len);
/* add HT capability and information IEs */
- if (channel_type && sband->ht_cap.ht_supported) {
+ if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ sband->ht_cap.ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
sizeof(struct ieee80211_ht_operation));
@@ -168,7 +173,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
* keep them at 0
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
- chan, channel_type, 0);
+ &chandef, 0);
}
if (local->hw.queues >= IEEE80211_NUM_ACS) {
@@ -196,7 +201,22 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
bss_change |= BSS_CHANGED_BASIC_RATES;
bss_change |= BSS_CHANGED_HT;
bss_change |= BSS_CHANGED_IBSS;
+
+ /*
+ * In 5 GHz/802.11a, we can always use short slot time.
+ * (IEEE 802.11-2012 18.3.8.7)
+ *
+ * In 2.4GHz, we must always use long slots in IBSS for compatibility
+ * reasons.
+ * (IEEE 802.11-2012 19.4.5)
+ *
+ * HT follows these specifications (IEEE 802.11-2012 20.3.18)
+ */
+ sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
+ bss_change |= BSS_CHANGED_ERP_SLOT;
+
sdata->vif.bss_conf.ibss_joined = true;
+ sdata->vif.bss_conf.ibss_creator = creator;
ieee80211_bss_info_change_notify(sdata, bss_change);
ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
@@ -249,7 +269,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
cbss->channel,
basic_rates,
cbss->capability,
- cbss->tsf);
+ cbss->tsf,
+ false);
}
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
@@ -279,7 +300,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
ibss_dbg(sdata,
"TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
sdata->vif.addr, addr, sdata->u.ibss.bssid);
- ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0,
+ ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
addr, sdata->u.ibss.bssid, NULL, 0, 0);
}
return sta;
@@ -294,7 +315,8 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int band = local->oper_channel->band;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int band;
/*
* XXX: Consider removing the least recently used entry and
@@ -317,6 +339,13 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return NULL;
}
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf))
+ return NULL;
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
if (!sta) {
rcu_read_lock();
@@ -362,11 +391,13 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
- if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
- return;
ibss_dbg(sdata,
"RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
+
+ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
+ return;
+
sta_info_destroy_addr(sdata, mgmt->sa);
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
rcu_read_unlock();
@@ -389,7 +420,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
* However, try to reply to authentication attempts if someone
* has actually implemented this.
*/
- ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0,
+ ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
}
@@ -461,9 +492,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
/* we both use HT */
struct ieee80211_sta_ht_cap sta_ht_cap_new;
- enum nl80211_channel_type channel_type =
- ieee80211_ht_oper_to_channel_type(
- elems->ht_operation);
+ struct cfg80211_chan_def chandef;
+
+ ieee80211_ht_oper_to_chandef(channel,
+ elems->ht_operation,
+ &chandef);
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
@@ -473,9 +506,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* fall back to HT20 if we don't use or use
* the other extension channel
*/
- if (!(channel_type == NL80211_CHAN_HT40MINUS ||
- channel_type == NL80211_CHAN_HT40PLUS) ||
- channel_type != sdata->u.ibss.channel_type)
+ if (chandef.width != NL80211_CHAN_WIDTH_40 ||
+ cfg80211_get_chandef_type(&chandef) !=
+ sdata->u.ibss.channel_type)
sta_ht_cap_new.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -517,7 +550,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
goto put_bss;
/* different channel */
- if (cbss->channel != local->oper_channel)
+ if (sdata->u.ibss.fixed_channel &&
+ sdata->u.ibss.channel != cbss->channel)
goto put_bss;
/* different SSID */
@@ -530,30 +564,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
goto put_bss;
- if (rx_status->flag & RX_FLAG_MACTIME_MPDU) {
- /*
- * For correct IBSS merging we need mactime; since mactime is
- * defined as the time the first data symbol of the frame hits
- * the PHY, and the timestamp of the beacon is defined as "the
- * time that the data symbol containing the first bit of the
- * timestamp is transmitted to the PHY plus the transmitting
- * STA's delays through its local PHY from the MAC-PHY
- * interface to its interface with the WM" (802.11 11.1.2)
- * - equals the time this bit arrives at the receiver - we have
- * to take into account the offset between the two.
- *
- * E.g. at 1 MBit that means mactime is 192 usec earlier
- * (=24 bytes * 8 usecs/byte) than the beacon timestamp.
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT)
- rate = 65; /* TODO: HT rates */
- else
- rate = local->hw.wiphy->bands[band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
+ if (ieee80211_have_rx_timestamp(rx_status)) {
+ /* time when timestamp field was received */
+ rx_timestamp =
+ ieee80211_calculate_rx_timestamp(local, rx_status,
+ len + FCS_LEN, 24);
} else {
/*
* second best option: get current TSF
@@ -592,7 +607,8 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int band = local->oper_channel->band;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int band;
/*
* XXX: Consider removing the least recently used entry and
@@ -610,6 +626,15 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
if (!ether_addr_equal(bssid, sdata->u.ibss.bssid))
return;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
if (!sta)
return;
@@ -715,7 +740,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
ifibss->channel, ifibss->basic_rates,
- capability, 0);
+ capability, 0, true);
}
/*
@@ -784,18 +809,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
int interval = IEEE80211_SCAN_INTERVAL;
if (time_after(jiffies, ifibss->ibss_join_req +
- IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) {
- ieee80211_sta_create_ibss(sdata);
- return;
- }
- sdata_info(sdata, "IBSS not allowed on %d MHz\n",
- local->oper_channel->center_freq);
-
- /* No IBSS found - decrease scan interval and continue
- * scanning. */
- interval = IEEE80211_SCAN_INTERVAL_SLOW;
- }
+ IEEE80211_IBSS_JOIN_TIMEOUT))
+ ieee80211_sta_create_ibss(sdata);
mod_timer(&ifibss->timer,
round_jiffies(jiffies + interval));
@@ -1082,21 +1097,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
- sdata->u.ibss.channel = params->channel;
- sdata->u.ibss.channel_type = params->channel_type;
+ sdata->u.ibss.channel = params->chandef.chan;
+ sdata->u.ibss.channel_type =
+ cfg80211_get_chandef_type(&params->chandef);
sdata->u.ibss.fixed_channel = params->channel_fixed;
- /* fix ourselves to that channel now already */
- if (params->channel_fixed) {
- sdata->local->oper_channel = params->channel;
- if (!ieee80211_set_channel_type(sdata->local, sdata,
- params->channel_type)) {
- mutex_unlock(&sdata->u.ibss.mtx);
- kfree_skb(skb);
- return -EINVAL;
- }
- }
-
if (params->ie) {
sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
GFP_KERNEL);
@@ -1134,6 +1139,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
return 0;
@@ -1197,6 +1205,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
lockdep_is_held(&sdata->u.ibss.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
sdata->vif.bss_conf.ibss_joined = false;
+ sdata->vif.bss_conf.ibss_creator = false;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
synchronize_rcu();
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 156e5835e37f..42d0d0267730 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -56,6 +56,9 @@ struct ieee80211_local;
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
+/* power level hasn't been configured (or set to automatic) */
+#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
+
/*
* Some APs experience problems when working with U-APSD. Decrease the
* probability of that happening by using legacy mode for all ACs but VO.
@@ -280,23 +283,27 @@ struct probe_resp {
u8 data[0];
};
-struct ieee80211_if_ap {
- struct beacon_data __rcu *beacon;
- struct probe_resp __rcu *probe_resp;
-
- struct list_head vlans;
-
+struct ps_data {
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
- struct sk_buff_head ps_bc_buf;
+ struct sk_buff_head bc_buf;
atomic_t num_sta_ps; /* number of stations in PS mode */
- atomic_t num_mcast_sta; /* number of stations receiving multicast */
int dtim_count;
bool dtim_bc_mc;
};
+struct ieee80211_if_ap {
+ struct beacon_data __rcu *beacon;
+ struct probe_resp __rcu *probe_resp;
+
+ struct list_head vlans;
+
+ struct ps_data ps;
+ atomic_t num_mcast_sta; /* number of stations receiving multicast */
+};
+
struct ieee80211_if_wds {
struct sta_info *sta;
u8 remote_addr[ETH_ALEN];
@@ -316,7 +323,6 @@ struct mesh_stats {
__u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/
__u32 dropped_frames_no_route; /* Not transmitted, no route found */
__u32 dropped_frames_congestion;/* Not forwarded due to congestion */
- atomic_t estab_plinks;
};
#define PREQ_Q_F_START 0x1
@@ -342,7 +348,6 @@ struct ieee80211_roc_work {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
- enum nl80211_channel_type chan_type;
bool started, abort, hw_begun, notified;
@@ -350,7 +355,7 @@ struct ieee80211_roc_work {
u32 duration, req_duration;
struct sk_buff *frame;
- u64 mgmt_tx_cookie;
+ u64 cookie, mgmt_tx_cookie;
};
/* flags used in struct ieee80211_if_managed.flags */
@@ -358,7 +363,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_BEACON_POLL = BIT(0),
IEEE80211_STA_CONNECTION_POLL = BIT(1),
IEEE80211_STA_CONTROL_PORT = BIT(2),
- IEEE80211_STA_DISABLE_11N = BIT(4),
+ IEEE80211_STA_DISABLE_HT = BIT(4),
IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
@@ -366,6 +371,8 @@ enum ieee80211_sta_flags {
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
IEEE80211_STA_DISABLE_40MHZ = BIT(10),
IEEE80211_STA_DISABLE_VHT = BIT(11),
+ IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
+ IEEE80211_STA_DISABLE_160MHZ = BIT(13),
};
struct ieee80211_mgd_auth_data {
@@ -378,8 +385,9 @@ struct ieee80211_mgd_auth_data {
u8 key_len, key_idx;
bool done;
- size_t ie_len;
- u8 ie[];
+ u16 sae_trans, sae_status;
+ size_t data_len;
+ u8 data[];
};
struct ieee80211_mgd_assoc_data {
@@ -433,7 +441,6 @@ struct ieee80211_if_managed {
bool powersave; /* powersave requested for this iface */
bool broken_ap; /* AP is broken -- turn off powersave */
enum ieee80211_smps_mode req_smps, /* requested smps mode */
- ap_smps, /* smps mode AP thinks we're in */
driver_smps_mode; /* smps mode request */
struct work_struct request_smps_work;
@@ -467,6 +474,8 @@ struct ieee80211_if_managed {
u8 use_4addr;
+ u8 p2p_noa_index;
+
/* Signal strength from the last Beacon frame in the current BSS. */
int last_beacon_signal;
@@ -599,6 +608,7 @@ struct ieee80211_if_mesh {
int preq_queue_len;
struct mesh_stats mshstats;
struct mesh_config mshcfg;
+ atomic_t estab_plinks;
u32 mesh_seqnum;
bool accepting_plinks;
int num_gates;
@@ -610,7 +620,7 @@ struct ieee80211_if_mesh {
IEEE80211_MESH_SEC_SECURED = 0x2,
} security;
/* Extensible Synchronization Framework */
- struct ieee80211_mesh_sync_ops *sync_ops;
+ const struct ieee80211_mesh_sync_ops *sync_ops;
s64 sync_offset_clockdrift_max;
spinlock_t sync_offset_lock;
bool adjusting_tbtt;
@@ -658,6 +668,30 @@ enum ieee80211_sdata_state_bits {
SDATA_STATE_OFFCHANNEL,
};
+/**
+ * enum ieee80211_chanctx_mode - channel context configuration mode
+ *
+ * @IEEE80211_CHANCTX_SHARED: channel context may be used by
+ * multiple interfaces
+ * @IEEE80211_CHANCTX_EXCLUSIVE: channel context can be used
+ * only by a single interface. This can be used for example for
+ * non-fixed channel IBSS.
+ */
+enum ieee80211_chanctx_mode {
+ IEEE80211_CHANCTX_SHARED,
+ IEEE80211_CHANCTX_EXCLUSIVE
+};
+
+struct ieee80211_chanctx {
+ struct list_head list;
+ struct rcu_head rcu_head;
+
+ enum ieee80211_chanctx_mode mode;
+ int refcount;
+
+ struct ieee80211_chanctx_conf conf;
+};
+
struct ieee80211_sub_if_data {
struct list_head list;
@@ -704,11 +738,20 @@ struct ieee80211_sub_if_data {
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
+ /* used to reconfigure hardware SM PS */
+ struct work_struct recalc_smps;
+
struct work_struct work;
struct sk_buff_head skb_queue;
bool arp_filter_state;
+ u8 needed_rx_chains;
+ enum ieee80211_smps_mode smps_mode;
+
+ int user_power_level; /* in dBm */
+ int ap_power_level; /* in dBm */
+
/*
* AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for
@@ -749,6 +792,21 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
return container_of(p, struct ieee80211_sub_if_data, vif);
}
+static inline enum ieee80211_band
+ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
+{
+ enum ieee80211_band band = IEEE80211_BAND_2GHZ;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!WARN_ON(!chanctx_conf))
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
+ return band;
+}
+
enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
IEEE80211_SDATA_QUEUE_AGG_START = 1,
@@ -821,6 +879,7 @@ enum {
* @SCAN_SUSPEND: Suspend the scan and go back to operating channel to
* send out data
* @SCAN_RESUME: Resume the scan and scan the next channel
+ * @SCAN_ABORT: Abort the scan and go back to operating channel
*/
enum mac80211_scan_state {
SCAN_DECISION,
@@ -828,6 +887,7 @@ enum mac80211_scan_state {
SCAN_SEND_PROBE,
SCAN_SUSPEND,
SCAN_RESUME,
+ SCAN_ABORT,
};
struct ieee80211_local {
@@ -858,15 +918,14 @@ struct ieee80211_local {
bool wiphy_ciphers_allocated;
+ bool use_chanctx;
+
/* protects the aggregated multicast list and filter calls */
spinlock_t filter_lock;
/* used for uploading changed mc list */
struct work_struct reconfig_filter;
- /* used to reconfigure hardware SM PS */
- struct work_struct recalc_smps;
-
/* aggregated multicast list */
struct netdev_hw_addr_list mc_list;
@@ -903,6 +962,9 @@ struct ieee80211_local {
/* wowlan is enabled -- don't reconfig on resume */
bool wowlan;
+ /* number of RX chains the hardware has */
+ u8 rx_chains;
+
int tx_headroom; /* required headroom for hardware/radiotap */
/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -972,6 +1034,7 @@ struct ieee80211_local {
enum ieee80211_band hw_scan_band;
int scan_channel_idx;
int scan_ies_len;
+ int hw_scan_ies_bufsize;
struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
@@ -980,12 +1043,17 @@ struct ieee80211_local {
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data __rcu *scan_sdata;
+ struct ieee80211_channel *csa_channel;
+ /* For backward compatibility only -- do not use */
+ struct ieee80211_channel *_oper_channel;
enum nl80211_channel_type _oper_channel_type;
- struct ieee80211_channel *oper_channel, *csa_channel;
/* Temporary remain-on-channel for off-channel operations */
struct ieee80211_channel *tmp_channel;
- enum nl80211_channel_type tmp_channel_type;
+
+ /* channel contexts */
+ struct list_head chanctx_list;
+ struct mutex chanctx_mtx;
/* SNMP counters */
/* dot11CountersTable */
@@ -1058,8 +1126,7 @@ struct ieee80211_local {
int dynamic_ps_user_timeout;
bool disable_dynamic_ps;
- int user_power_level; /* in dBm */
- int ap_power_level; /* in dBm */
+ int user_power_level; /* in dBm, for all interfaces */
enum ieee80211_smps_mode smps_mode;
@@ -1078,6 +1145,7 @@ struct ieee80211_local {
struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time;
+ u64 roc_cookie_counter;
struct idr ack_status_frames;
spinlock_t ack_status_lock;
@@ -1091,6 +1159,7 @@ struct ieee80211_local {
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
+ struct cfg80211_chan_def monitor_chandef;
};
static inline struct ieee80211_sub_if_data *
@@ -1133,6 +1202,8 @@ struct ieee802_11_elems {
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
struct ieee80211_ht_operation *ht_operation;
+ struct ieee80211_vht_cap *vht_cap_elem;
+ struct ieee80211_vht_operation *vht_operation;
struct ieee80211_meshconf_ie *mesh_config;
u8 *mesh_id;
u8 *peering;
@@ -1188,7 +1259,18 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
is_broadcast_ether_addr(raddr);
}
+static inline bool
+ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status)
+{
+ WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START &&
+ status->flag & RX_FLAG_MACTIME_END);
+ return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END);
+}
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ unsigned int mpdu_len,
+ unsigned int mpdu_offset);
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -1302,6 +1384,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);
void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -1361,6 +1446,13 @@ void ieee80211_ba_session_work(struct work_struct *work);
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
+u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs);
+
+/* VHT */
+void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_vht_cap *vht_cap_ie,
+ struct ieee80211_sta_vht_cap *vht_cap);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -1395,11 +1487,42 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ enum ieee80211_band band);
+
+void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band);
-void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid);
-static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
+static inline void
+ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band)
+{
+ rcu_read_lock();
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid, band);
+ rcu_read_unlock();
+}
+
+static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid,
+ chanctx_conf->def.chan->band);
+ rcu_read_unlock();
+}
+
+static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
@@ -1446,14 +1569,14 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,
}
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
- u16 transaction, u16 auth_alg,
+ u16 transaction, u16 auth_alg, u16 status,
u8 *extra, size_t extra_len, const u8 *bssid,
const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, u16 stype, u16 reason,
bool send_frame, u8 *frame_buf);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
- const u8 *ie, size_t ie_len,
+ size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
@@ -1466,7 +1589,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, bool no_cck,
- struct ieee80211_channel *channel);
+ struct ieee80211_channel *channel, bool scan);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
@@ -1476,7 +1599,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
enum ieee80211_band band, u32 *basic_rates);
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode);
-void ieee80211_recalc_smps(struct ieee80211_local *local);
+void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
const u8 *ids, int n_ids, size_t offset);
@@ -1484,8 +1607,7 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
@@ -1497,20 +1619,18 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band);
/* channel management */
-enum ieee80211_chan_mode {
- CHAN_MODE_UNDEFINED,
- CHAN_MODE_HOPPING,
- CHAN_MODE_FIXED,
-};
-
-enum ieee80211_chan_mode
-ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore);
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype);
-enum nl80211_channel_type
-ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper);
+void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef);
+
+int __must_check
+ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode);
+void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
+
+void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7de7717ad67d..09a80b55cf5a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -42,6 +42,41 @@
* by either the RTNL, the iflist_mtx or RCU.
*/
+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int power;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return false;
+ }
+
+ power = chanctx_conf->def.chan->max_power;
+ rcu_read_unlock();
+
+ if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->user_power_level);
+
+ if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->ap_power_level);
+
+ if (power != sdata->vif.bss_conf.txpower) {
+ sdata->vif.bss_conf.txpower = power;
+ ieee80211_hw_config(sdata->local, 0);
+ return true;
+ }
+
+ return false;
+}
+
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+{
+ if (__ieee80211_recalc_txpower(sdata))
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
+}
static u32 ieee80211_idle_off(struct ieee80211_local *local,
const char *reason)
@@ -188,6 +223,47 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata;
+ u64 new, mask, tmp;
+ u8 *m;
+ int ret = 0;
+
+ if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
+ return 0;
+
+ m = addr;
+ new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+ ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+ ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+ m = local->hw.wiphy->addr_mask;
+ mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+ ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+ ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ continue;
+
+ m = sdata->vif.addr;
+ tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+ ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+ ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+ if ((new & ~mask) != (tmp & ~mask)) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ return ret;
+}
+
static int ieee80211_change_mac(struct net_device *dev, void *addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -197,6 +273,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
if (ieee80211_sdata_running(sdata))
return -EBUSY;
+ ret = ieee80211_verify_mac(sdata->local, sa->sa_data);
+ if (ret)
+ return ret;
+
ret = eth_mac_addr(dev, sa);
if (ret == 0)
@@ -380,6 +460,14 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
goto out_unlock;
}
+ ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ if (ret) {
+ drv_remove_interface(local, sdata);
+ kfree(sdata);
+ goto out_unlock;
+ }
+
rcu_assign_pointer(local->monitor_sdata, sdata);
out_unlock:
mutex_unlock(&local->iflist_mtx);
@@ -403,6 +491,8 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
rcu_assign_pointer(local->monitor_sdata, NULL);
synchronize_net();
+ ieee80211_vif_release_channel(sdata);
+
drv_remove_interface(local, sdata);
kfree(sdata);
@@ -665,7 +755,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
int i;
- enum nl80211_channel_type orig_ct;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -729,34 +818,17 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
+ cancel_work_sync(&sdata->recalc_smps);
+
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata;
- struct beacon_data *old_beacon =
- rtnl_dereference(sdata->u.ap.beacon);
- struct probe_resp *old_probe_resp =
- rtnl_dereference(sdata->u.ap.probe_resp);
-
- /* sdata_running will return false, so this will disable */
- ieee80211_bss_info_change_notify(sdata,
- BSS_CHANGED_BEACON_ENABLED);
-
- /* remove beacon and probe response */
- RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
- RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
- synchronize_rcu();
- kfree(old_beacon);
- kfree(old_probe_resp);
/* down all dependent devices, that is VLANs */
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
u.vlan.list)
dev_close(vlan->dev);
WARN_ON(!list_empty(&sdata->u.ap.vlans));
-
- /* free all potentially still buffered bcast frames */
- local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf);
- skb_queue_purge(&sdata->u.ap.ps_bc_buf);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata);
}
@@ -790,7 +862,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
rcu_assign_pointer(local->p2p_sdata, NULL);
/* fall through */
default:
- flush_work(&sdata->work);
+ cancel_work_sync(&sdata->work);
/*
* When we get here, the interface is marked down.
* Call rcu_barrier() to wait both for the RX path
@@ -837,14 +909,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
hw_reconf_flags = 0;
}
- /* Re-calculate channel-type, in case there are multiple vifs
- * on different channel types.
- */
- orig_ct = local->_oper_channel_type;
- ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT);
-
/* do after stop to avoid reconfiguring when we stop anyway */
- if (hw_reconf_flags || (orig_ct != local->_oper_channel_type))
+ if (hw_reconf_flags)
ieee80211_hw_config(local, hw_reconf_flags);
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
@@ -1121,6 +1187,13 @@ static void ieee80211_iface_work(struct work_struct *work)
}
}
+static void ieee80211_recalc_smps_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, recalc_smps);
+
+ ieee80211_recalc_smps(sdata);
+}
/*
* Helper function to initialise an interface to a specific type.
@@ -1149,6 +1222,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
+ INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
@@ -1157,7 +1231,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.p2p = true;
/* fall through */
case NL80211_IFTYPE_AP:
- skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
+ skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
case NL80211_IFTYPE_P2P_CLIENT:
@@ -1282,11 +1356,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
if (type == ieee80211_vif_type_p2p(&sdata->vif))
return 0;
- /* Setting ad-hoc mode on non-IBSS channel is not supported. */
- if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
- type == NL80211_IFTYPE_ADHOC)
- return -EOPNOTSUPP;
-
if (ieee80211_sdata_running(sdata)) {
ret = ieee80211_runtime_change_iftype(sdata, type);
if (ret)
@@ -1298,9 +1367,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
}
/* reset some values that shouldn't be kept across type changes */
- sdata->vif.bss_conf.basic_rates =
- ieee80211_mandatory_rates(sdata->local,
- sdata->local->oper_channel->band);
sdata->drop_unencrypted = 0;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false;
@@ -1523,6 +1589,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_set_default_queues(sdata);
+ sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ sdata->user_power_level = local->user_power_level;
+
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index d27e61aaa71b..619c5d697999 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -339,7 +339,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = TKIP_IV_LEN;
key->conf.icv_len = TKIP_ICV_LEN;
if (seq) {
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
key->u.tkip.rx[i].iv32 =
get_unaligned_le32(&seq[2]);
key->u.tkip.rx[i].iv16 =
@@ -352,7 +352,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = CCMP_HDR_LEN;
key->conf.icv_len = CCMP_MIC_LEN;
if (seq) {
- for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
for (j = 0; j < CCMP_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
seq[CCMP_PN_LEN - j - 1];
@@ -372,8 +372,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = 0;
key->conf.icv_len = sizeof(struct ieee80211_mmie);
if (seq)
- for (j = 0; j < 6; j++)
- key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1];
+ for (j = 0; j < CMAC_PN_LEN; j++)
+ key->u.aes_cmac.rx_pn[j] =
+ seq[CMAC_PN_LEN - j - 1];
/*
* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
@@ -654,16 +655,16 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES))
+ if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
return;
seq->tkip.iv32 = key->u.tkip.rx[tid].iv32;
seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
- if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES))
+ if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
return;
if (tid < 0)
- pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES];
+ pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
else
pn = key->u.ccmp.rx_pn[tid];
memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 7d4e31f037d7..382dc44ed330 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -30,8 +30,6 @@
#define TKIP_ICV_LEN 4
#define CMAC_PN_LEN 6
-#define NUM_RX_DATA_QUEUES 16
-
struct ieee80211_local;
struct ieee80211_sub_if_data;
struct sta_info;
@@ -82,17 +80,20 @@ struct ieee80211_key {
struct tkip_ctx tx;
/* last received RSC */
- struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
+ struct tkip_ctx rx[IEEE80211_NUM_TIDS];
+
+ /* number of mic failures */
+ u32 mic_failures;
} tkip;
struct {
atomic64_t tx_pn;
/*
* Last received packet number. The first
- * NUM_RX_DATA_QUEUES counters are used with Data
+ * IEEE80211_NUM_TIDS counters are used with Data
* frames and the last counter is used with Robust
* Management frames.
*/
- u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN];
+ u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */
} ccmp;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index f57f597972f8..1b087fff93e7 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -93,15 +93,15 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
ieee80211_configure_filter(local);
}
-int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
+static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
{
+ struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
- int ret = 0;
+ u32 changed = 0;
int power;
enum nl80211_channel_type channel_type;
u32 offchannel_flag;
-
- might_sleep();
+ bool scanning = false;
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) {
@@ -109,19 +109,19 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
- if (chan == local->oper_channel)
+ if (chan == local->_oper_channel)
channel_type = local->_oper_channel_type;
else
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
chan = local->tmp_channel;
- channel_type = local->tmp_channel_type;
+ channel_type = NL80211_CHAN_NO_HT;
} else {
- chan = local->oper_channel;
+ chan = local->_oper_channel;
channel_type = local->_oper_channel_type;
}
- if (chan != local->oper_channel ||
+ if (chan != local->_oper_channel ||
channel_type != local->_oper_channel_type)
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
else
@@ -148,22 +148,39 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
changed |= IEEE80211_CONF_CHANGE_SMPS;
}
- if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
- test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
- test_bit(SCAN_HW_SCANNING, &local->scanning) ||
- !local->ap_power_level)
- power = chan->max_power;
- else
- power = min(chan->max_power, local->ap_power_level);
+ scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
+ test_bit(SCAN_HW_SCANNING, &local->scanning);
+ power = chan->max_power;
- if (local->user_power_level >= 0)
- power = min(power, local->user_power_level);
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!rcu_access_pointer(sdata->vif.chanctx_conf))
+ continue;
+ power = min(power, sdata->vif.bss_conf.txpower);
+ }
+ rcu_read_unlock();
if (local->hw.conf.power_level != power) {
changed |= IEEE80211_CONF_CHANGE_POWER;
local->hw.conf.power_level = power;
}
+ return changed;
+}
+
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
+{
+ int ret = 0;
+
+ might_sleep();
+
+ if (!local->use_chanctx)
+ changed |= ieee80211_hw_conf_chan(local);
+ else
+ changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL |
+ IEEE80211_CONF_CHANGE_POWER);
+
if (changed && local->open_count) {
ret = drv_config(local, changed);
/*
@@ -359,14 +376,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_restart_hw);
-static void ieee80211_recalc_smps_work(struct work_struct *work)
-{
- struct ieee80211_local *local =
- container_of(work, struct ieee80211_local, recalc_smps);
-
- ieee80211_recalc_smps(local);
-}
-
#ifdef CONFIG_INET
static int ieee80211_ifa_changed(struct notifier_block *nb,
unsigned long data, void *arg)
@@ -465,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
- BIT(IEEE80211_STYPE_DEAUTH >> 4),
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
@@ -540,6 +550,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
struct ieee80211_local *local;
int priv_size, i;
struct wiphy *wiphy;
+ bool use_chanctx;
if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
!ops->add_interface || !ops->remove_interface ||
@@ -549,6 +560,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
return NULL;
+ /* check all or no channel context operations exist */
+ i = !!ops->add_chanctx + !!ops->remove_chanctx +
+ !!ops->change_chanctx + !!ops->assign_vif_chanctx +
+ !!ops->unassign_vif_chanctx;
+ if (WARN_ON(i != 0 && i != 5))
+ return NULL;
+ use_chanctx = i == 5;
+
/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wiphy priv data for both our ieee80211_local and for
* the driver's private data
@@ -584,8 +603,15 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (ops->remain_on_channel)
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
- wiphy->features = NL80211_FEATURE_SK_TX_STATUS |
- NL80211_FEATURE_HT_IBSS;
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
+ NL80211_FEATURE_SAE |
+ NL80211_FEATURE_HT_IBSS |
+ NL80211_FEATURE_VIF_TXPOWER;
+
+ if (!ops->hw_scan)
+ wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
+ NL80211_FEATURE_AP_SCAN;
+
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -599,6 +625,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
local->ops = ops;
+ local->use_chanctx = use_chanctx;
/* set up some defaults */
local->hw.queues = 1;
@@ -612,7 +639,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW;
- local->user_power_level = -1;
+ local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces);
@@ -626,6 +655,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
spin_lock_init(&local->filter_lock);
spin_lock_init(&local->queue_stop_reason_lock);
+ INIT_LIST_HEAD(&local->chanctx_list);
+ mutex_init(&local->chanctx_mtx);
+
/*
* The rx_skb_queue is only accessed from tasklets,
* but other SKB queues are used from within IRQ
@@ -641,7 +673,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
INIT_WORK(&local->restart_work, ieee80211_restart_work);
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
- INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
local->smps_mode = IEEE80211_SMPS_OFF;
INIT_WORK(&local->dynamic_ps_enable_work,
@@ -719,6 +750,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan)
return -EINVAL;
+ if (!local->use_chanctx) {
+ for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
+ const struct ieee80211_iface_combination *comb;
+
+ comb = &local->hw.wiphy->iface_combinations[i];
+
+ if (comb->num_different_channels > 1)
+ return -EINVAL;
+ }
+ } else {
+ /*
+ * WDS is currently prohibited when channel contexts are used
+ * because there's no clear definition of which channel WDS
+ * type interfaces use
+ */
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
+ return -EINVAL;
+ }
+
/* Only HW csum features are currently compatible with mac80211 */
feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_HW_CSUM;
@@ -728,6 +778,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (hw->max_report_rates == 0)
hw->max_report_rates = hw->max_rates;
+ local->rx_chains = 1;
+
/*
* generic code guarantees at least one band,
* set this very early because much code assumes
@@ -743,18 +795,28 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
- if (!local->oper_channel) {
+ if (!local->use_chanctx && !local->_oper_channel) {
/* init channel we're on */
local->hw.conf.channel =
- local->oper_channel = &sband->channels[0];
+ local->_oper_channel = &sband->channels[0];
local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
}
+ cfg80211_chandef_create(&local->monitor_chandef,
+ &sband->channels[0],
+ NL80211_CHAN_NO_HT);
channels += sband->n_channels;
if (max_bitrates < sband->n_bitrates)
max_bitrates = sband->n_bitrates;
supp_ht = supp_ht || sband->ht_cap.ht_supported;
supp_vht = supp_vht || sband->vht_cap.vht_supported;
+
+ if (sband->ht_cap.ht_supported)
+ local->rx_chains =
+ max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs),
+ local->rx_chains);
+
+ /* TODO: consider VHT for RX chains, hopefully it's the same */
}
local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
@@ -778,19 +840,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
- /*
- * mac80211 doesn't support more than 1 channel, and also not more
- * than one IBSS interface
- */
+ /* mac80211 doesn't support more than one IBSS interface right now */
for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *c;
int j;
c = &hw->wiphy->iface_combinations[i];
- if (c->num_different_channels > 1)
- return -EINVAL;
-
for (j = 0; j < c->n_limits; j++)
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
c->limits[j].max > 1)
@@ -830,9 +886,21 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (supp_ht)
local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap);
- if (supp_vht)
+ if (supp_vht) {
local->scan_ies_len +=
- 2 + sizeof(struct ieee80211_vht_capabilities);
+ 2 + sizeof(struct ieee80211_vht_cap);
+
+ /*
+ * (for now at least), drivers wanting to use VHT must
+ * support channel contexts, as they contain all the
+ * necessary VHT information and the global hw config
+ * doesn't (yet)
+ */
+ if (WARN_ON(!local->use_chanctx)) {
+ result = -EINVAL;
+ goto fail_wiphy_register;
+ }
+ }
if (!local->ops->hw_scan) {
/* For hw_scan, driver needs to set these up. */
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index ff0296c7bab8..1bf03f9ff3ba 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -76,7 +76,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
u32 basic_rates = 0;
- enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT;
+ struct cfg80211_chan_def sta_chan_def;
/*
* As support for each feature is added, check for matching
@@ -97,23 +97,17 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
goto mismatch;
- ieee80211_sta_get_rates(local, ie, local->oper_channel->band,
+ ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
&basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates)
goto mismatch;
- if (ie->ht_operation)
- sta_channel_type =
- ieee80211_ht_oper_to_channel_type(ie->ht_operation);
-
- /* Disallow HT40+/- mismatch */
- if (ie->ht_operation &&
- (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) &&
- (sta_channel_type == NL80211_CHAN_HT40MINUS ||
- sta_channel_type == NL80211_CHAN_HT40PLUS) &&
- sdata->vif.bss_conf.channel_type != sta_channel_type)
+ ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+ ie->ht_operation, &sta_chan_def);
+
+ if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
+ &sta_chan_def))
goto mismatch;
return true;
@@ -129,7 +123,7 @@ mismatch:
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
{
return (ie->mesh_config->meshconf_cap &
- MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
}
/**
@@ -264,16 +258,16 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
/* Authentication Protocol identifier */
*pos++ = ifmsh->mesh_auth_id;
/* Mesh Formation Info - number of neighbors */
- neighbors = atomic_read(&ifmsh->mshstats.estab_plinks);
+ neighbors = atomic_read(&ifmsh->estab_plinks);
/* Number of neighbor mesh STAs or 15 whichever is smaller */
neighbors = (neighbors > 15) ? 15 : neighbors;
*pos++ = neighbors << 1;
/* Mesh capability */
- *pos = MESHCONF_CAPAB_FORWARDING;
+ *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
*pos |= ifmsh->accepting_plinks ?
- MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
- MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
return 0;
@@ -355,12 +349,22 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- struct ieee80211_channel *chan = local->oper_channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
u8 *pos;
if (skb_tailroom(skb) < 3)
return -ENOMEM;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
+
sband = local->hw.wiphy->bands[chan->band];
if (sband->band == IEEE80211_BAND_2GHZ) {
pos = skb_put(skb, 2 + 1);
@@ -376,12 +380,13 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u8 *pos;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[band];
if (!sband->ht_cap.ht_supported ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
@@ -397,14 +402,26 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_channel *channel = local->oper_channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type =
- sdata->vif.bss_conf.channel_type;
- struct ieee80211_supported_band *sband =
- local->hw.wiphy->bands[channel->band];
- struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
+ cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef);
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap *ht_cap;
u8 *pos;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ channel = chanctx_conf->def.chan;
+ rcu_read_unlock();
+
+ sband = local->hw.wiphy->bands[channel->band];
+ ht_cap = &sband->ht_cap;
+
if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
return 0;
@@ -412,7 +429,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
return -ENOMEM;
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
- ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type,
+ ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
sdata->vif.bss_conf.ht_operation_mode);
return 0;
@@ -610,7 +627,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
- sdata->local->oper_channel->band);
+ ieee80211_get_sdata_band(sdata));
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_HT |
@@ -680,8 +697,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems);
- /* ignore beacons from secure mesh peers if our security is off */
- if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE)
+ /* ignore non-mesh or secure / unsecure mismatch */
+ if ((!elems.mesh_id || !elems.mesh_config) ||
+ (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
+ (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
return;
if (elems.ds_params && elems.ds_params_len == 1)
@@ -694,8 +713,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
- if (elems.mesh_id && elems.mesh_config &&
- mesh_matches_local(sdata, &elems))
+ if (mesh_matches_local(sdata, &elems))
mesh_neighbour_update(sdata, mgmt->sa, &elems);
if (ifmsh->sync_ops)
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 25d0f17dec71..7c9215fb2ac8 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -19,20 +19,6 @@
/* Data structures */
/**
- * enum mesh_config_capab_flags - mesh config IE capability flags
- *
- * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish
- * additional mesh peerings with other mesh STAs
- * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
- * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing
- */
-enum mesh_config_capab_flags {
- MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
- MESHCONF_CAPAB_FORWARDING = BIT(3),
- MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
-};
-
-/**
* enum mesh_path_flags - mac80211 mesh path flags
*
*
@@ -256,7 +242,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
-struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
+const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
@@ -324,7 +310,7 @@ extern int mesh_allocated;
static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
{
return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks -
- atomic_read(&sdata->u.mesh.mshstats.estab_plinks);
+ atomic_read(&sdata->u.mesh.estab_plinks);
}
static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 3ab34d816897..4b274e9c91a5 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -19,12 +19,6 @@
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
-#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries)
-#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout)
-#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
-#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
-#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
-
/* We only need a valid sta if user configured a minimum rssi_threshold. */
#define rssi_threshold_check(sta, sdata) \
(sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\
@@ -50,14 +44,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
static inline
u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{
- atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
+ atomic_inc(&sdata->u.mesh.estab_plinks);
return mesh_accept_plinks_update(sdata);
}
static inline
u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{
- atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
+ atomic_dec(&sdata->u.mesh.estab_plinks);
return mesh_accept_plinks_update(sdata);
}
@@ -117,7 +111,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
u16 ht_opmode;
bool non_ht_sta = false, ht20_sta = false;
- if (sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
rcu_read_lock();
@@ -126,14 +120,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
sta->plink_state != NL80211_PLINK_ESTAB)
continue;
- switch (sta->ch_type) {
- case NL80211_CHAN_NO_HT:
+ switch (sta->ch_width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
mpl_dbg(sdata,
"mesh_plink %pM: nonHT sta (%pM) is present\n",
sdata->vif.addr, sta->sta.addr);
non_ht_sta = true;
goto out;
- case NL80211_CHAN_HT20:
+ case NL80211_CHAN_WIDTH_20:
mpl_dbg(sdata,
"mesh_plink %pM: HT20 sta (%pM) is present\n",
sdata->vif.addr, sta->sta.addr);
@@ -148,7 +142,7 @@ out:
if (non_ht_sta)
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED;
else if (ht20_sta &&
- sdata->vif.bss_conf.channel_type > NL80211_CHAN_HT20)
+ sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20)
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ;
else
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
@@ -252,6 +246,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.self_prot.action_code = action;
if (action != WLAN_SP_MESH_PEERING_CLOSE) {
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+
/* capability info */
pos = skb_put(skb, 2);
memset(pos, 0, 2);
@@ -260,10 +256,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, 2);
memcpy(pos + 2, &plid, 2);
}
- if (ieee80211_add_srates_ie(sdata, skb, true,
- local->oper_channel->band) ||
- ieee80211_add_ext_srates_ie(sdata, skb, true,
- local->oper_channel->band) ||
+ if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
+ ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata))
@@ -343,7 +337,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
- enum ieee80211_band band = local->oper_channel->band;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0;
struct sta_info *sta;
@@ -378,7 +372,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = rates;
if (elems->ht_cap_elem &&
- sdata->vif.bss_conf.channel_type != NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
&sta->sta.ht_cap);
@@ -386,15 +380,19 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
if (elems->ht_operation) {
+ struct cfg80211_chan_def chandef;
+
if (!(elems->ht_operation->ht_param &
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
sta->sta.ht_cap.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- sta->ch_type =
- ieee80211_ht_oper_to_channel_type(elems->ht_operation);
+ ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+ elems->ht_operation, &chandef);
+ sta->ch_width = chandef.width;
}
- rate_control_rate_init(sta);
+ if (insert)
+ rate_control_rate_init(sta);
spin_unlock_bh(&sta->lock);
if (insert && sta_info_insert(sta))
@@ -430,6 +428,7 @@ static void mesh_plink_timer(unsigned long data)
struct sta_info *sta;
__le16 llid, plid, reason;
struct ieee80211_sub_if_data *sdata;
+ struct mesh_config *mshcfg;
/*
* This STA is valid because sta_info_destroy() will
@@ -456,12 +455,13 @@ static void mesh_plink_timer(unsigned long data)
llid = sta->llid;
plid = sta->plid;
sdata = sta->sdata;
+ mshcfg = &sdata->u.mesh.mshcfg;
switch (sta->plink_state) {
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_OPN_SNT:
/* retry timer */
- if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
+ if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {
u32 rand;
mpl_dbg(sta->sdata,
"Mesh plink for %pM (retry, timeout): %d %d\n",
@@ -484,7 +484,7 @@ static void mesh_plink_timer(unsigned long data)
if (!reason)
reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
sta->plink_state = NL80211_PLINK_HOLDING;
- mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, llid, plid, reason);
@@ -543,7 +543,7 @@ int mesh_plink_open(struct sta_info *sta)
return -EBUSY;
}
sta->plink_state = NL80211_PLINK_OPN_SNT;
- mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+ mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock);
mpl_dbg(sdata,
"Mesh plink: starting establishment with %pM\n",
@@ -570,6 +570,7 @@ void mesh_plink_block(struct sta_info *sta)
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
size_t len, struct ieee80211_rx_status *rx_status)
{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
struct ieee802_11_elems elems;
struct sta_info *sta;
enum plink_event event;
@@ -777,7 +778,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plid = plid;
get_random_bytes(&llid, 2);
sta->llid = llid;
- mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+ mesh_plink_timer_set(sta,
+ mshcfg->dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_OPEN,
@@ -803,7 +805,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -825,7 +827,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
- dot11MeshConfirmTimeout(sdata)))
+ mshcfg->dot11MeshConfirmTimeout))
sta->ignore_plink_timer = true;
spin_unlock_bh(&sta->lock);
@@ -847,7 +849,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -888,7 +890,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -923,7 +925,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
changed |= __mesh_plink_deactivate(sta);
sta->plink_state = NL80211_PLINK_HOLDING;
llid = sta->llid;
- mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
changed |= mesh_set_ht_prot_mode(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
index a16b7b4b1e02..aa8d1e437385 100644
--- a/net/mac80211/mesh_sync.c
+++ b/net/mac80211/mesh_sync.c
@@ -43,7 +43,7 @@ struct sync_method {
static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
{
return (ie->mesh_config->meshconf_cap &
- MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
}
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
@@ -116,43 +116,13 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
goto no_sync;
}
- if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) {
- /*
- * The mactime is defined as the time the first data symbol
- * of the frame hits the PHY, and the timestamp of the beacon
- * is defined as "the time that the data symbol containing the
- * first bit of the timestamp is transmitted to the PHY plus
- * the transmitting STA's delays through its local PHY from the
- * MAC-PHY interface to its interface with the WM" (802.11
- * 11.1.2)
- *
- * T_r, in 13.13.2.2.2, is just defined as "the frame reception
- * time" but we unless we interpret that time to be the same
- * time of the beacon timestamp, the offset calculation will be
- * off. Below we adjust t_r to be "the time at which the first
- * symbol of the timestamp element in the beacon is received".
- * This correction depends on the rate.
- *
- * Based on similar code in ibss.c
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT) {
- /* TODO:
- * In principle there could be HT-beacons (Dual Beacon
- * HT Operation options), but for now ignore them and
- * just use the primary (i.e. non-HT) beacons for
- * synchronization.
- * */
- goto no_sync;
- } else
- rate = local->hw.wiphy->bands[rx_status->band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- /* 24 bytes of header * 8 bits/byte *
- * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/
- t_r = rx_status->mactime + (24 * 8 * 10 / rate);
- }
+ if (ieee80211_have_rx_timestamp(rx_status))
+ /* time when timestamp field was received */
+ t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
+ 24 + 12 +
+ elems->total_len +
+ FCS_LEN,
+ 24);
/* Timing offset calculation (see 13.13.2.2.2) */
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
@@ -225,58 +195,20 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
ifmsh->sync_offset_clockdrift_max);
set_bit(MESH_WORK_DRIFT_ADJUST,
&ifmsh->wrkq_flags);
+
+ ifmsh->adjusting_tbtt = true;
} else {
msync_dbg(sdata,
"TBTT : max clockdrift=%lld; too small to adjust\n",
(long long)ifmsh->sync_offset_clockdrift_max);
ifmsh->sync_offset_clockdrift_max = 0;
+
+ ifmsh->adjusting_tbtt = false;
}
spin_unlock_bh(&ifmsh->sync_offset_lock);
}
-static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u8 offset;
-
- if (!ifmsh->ie || !ifmsh->ie_len)
- return NULL;
-
- offset = ieee80211_ie_split_vendor(ifmsh->ie,
- ifmsh->ie_len, 0);
-
- if (!offset)
- return NULL;
-
- return ifmsh->ie + offset + 2;
-}
-
-static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
- u16 stype,
- struct ieee80211_mgmt *mgmt,
- struct ieee802_11_elems *elems,
- struct ieee80211_rx_status *rx_status)
-{
- const u8 *oui;
-
- WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
- msync_dbg(sdata, "called mesh_sync_vendor_rx_bcn_presp\n");
- oui = mesh_get_vendor_oui(sdata);
- /* here you would implement the vendor offset tracking for this oui */
-}
-
-static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
-{
- const u8 *oui;
-
- WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
- msync_dbg(sdata, "called mesh_sync_vendor_adjust_tbtt\n");
- oui = mesh_get_vendor_oui(sdata);
- /* here you would implement the vendor tsf adjustment for this oui */
-}
-
-/* global variable */
-static struct sync_method sync_methods[] = {
+static const struct sync_method sync_methods[] = {
{
.method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.ops = {
@@ -284,18 +216,11 @@ static struct sync_method sync_methods[] = {
.adjust_tbtt = &mesh_sync_offset_adjust_tbtt,
}
},
- {
- .method = IEEE80211_SYNC_METHOD_VENDOR,
- .ops = {
- .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp,
- .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt,
- }
- },
};
-struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
+const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
{
- struct ieee80211_mesh_sync_ops *ops = NULL;
+ const struct ieee80211_mesh_sync_ops *ops = NULL;
u8 i;
for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 1b7eed252fe9..7753a9ca98a6 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -178,20 +178,32 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
struct sta_info *sta;
u32 changed = 0;
u16 ht_opmode;
bool disable_40 = false;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return 0;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
+ sband = local->hw.wiphy->bands[chan->band];
- switch (sdata->vif.bss_conf.channel_type) {
- case NL80211_CHAN_HT40PLUS:
- if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ switch (sdata->vif.bss_conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_40:
+ if (sdata->vif.bss_conf.chandef.chan->center_freq >
+ sdata->vif.bss_conf.chandef.center_freq1 &&
+ chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
disable_40 = true;
- break;
- case NL80211_CHAN_HT40MINUS:
- if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ if (sdata->vif.bss_conf.chandef.chan->center_freq <
+ sdata->vif.bss_conf.chandef.center_freq1 &&
+ chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
disable_40 = true;
break;
default:
@@ -342,8 +354,18 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
/* determine capability flags */
cap = vht_cap.cap;
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
+ cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
/* reserve and fill IE */
- pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2);
+ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
}
@@ -359,11 +381,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
int i, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_supported_band *sband;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
u32 rates = 0;
lockdep_assert_held(&ifmgd->mtx);
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
+ sband = local->hw.wiphy->bands[chan->band];
if (assoc_data->supp_rates_len) {
/*
@@ -392,7 +424,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
4 + /* power capability */
2 + 2 * sband->n_channels + /* supported channels */
2 + sizeof(struct ieee80211_ht_cap) + /* HT */
- 2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */
+ 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */
assoc_data->ie_len + /* extra IEs */
9, /* WMM */
GFP_KERNEL);
@@ -485,7 +517,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2;
*pos++ = 0; /* min tx power */
- *pos++ = local->oper_channel->max_power; /* max tx power */
+ *pos++ = chan->max_power; /* max tx power */
/* 2. supported channels */
/* TODO: get this in reg domain format */
@@ -521,9 +553,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
- sband, local->oper_channel, ifmgd->ap_smps);
+ sband, chan, sdata->smps_mode);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
ieee80211_add_vht_ie(sdata, skb, sband);
@@ -657,18 +693,18 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated)
goto out;
- sdata->local->oper_channel = sdata->local->csa_channel;
+ sdata->local->_oper_channel = sdata->local->csa_channel;
if (!sdata->local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
ieee80211_hw_config(sdata->local,
IEEE80211_CONF_CHANGE_CHANNEL);
} else {
/* update the device channel directly */
- sdata->local->hw.conf.channel = sdata->local->oper_channel;
+ sdata->local->hw.conf.channel = sdata->local->_oper_channel;
}
/* XXX: shouldn't really modify cfg80211-owned data! */
- ifmgd->associated->channel = sdata->local->oper_channel;
+ ifmgd->associated->channel = sdata->local->_oper_channel;
/* XXX: wait for a beacon first? */
ieee80211_wake_queues_by_reason(&sdata->local->hw,
@@ -680,11 +716,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_if_managed *ifmgd;
-
- sdata = vif_to_sdata(vif);
- ifmgd = &sdata->u.mgd;
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
trace_api_chswitch_done(sdata, success);
if (!success) {
@@ -723,6 +756,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
cbss->channel->band);
+ struct ieee80211_chanctx *chanctx;
ASSERT_MGD_MTX(ifmgd);
@@ -748,10 +782,35 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;
}
- sdata->local->csa_channel = new_ch;
-
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ if (sdata->local->use_chanctx) {
+ sdata_info(sdata,
+ "not handling channel switch with channel contexts\n");
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
+ return;
+ }
+
+ mutex_lock(&sdata->local->chanctx_mtx);
+ if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
+ mutex_unlock(&sdata->local->chanctx_mtx);
+ return;
+ }
+ chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
+ struct ieee80211_chanctx, conf);
+ if (chanctx->refcount > 1) {
+ sdata_info(sdata,
+ "channel switch with multiple interfaces on the same channel, disconnecting\n");
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&sdata->local->chanctx_mtx);
+ return;
+ }
+ mutex_unlock(&sdata->local->chanctx_mtx);
+
+ sdata->local->csa_channel = new_ch;
+
if (sw_elem->mode)
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -778,10 +837,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
cbss->beacon_interval));
}
-static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- const u8 *country_ie, u8 country_ie_len,
- const u8 *pwr_constr_elem)
+static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *channel,
+ const u8 *country_ie, u8 country_ie_len,
+ const u8 *pwr_constr_elem)
{
struct ieee80211_country_ie_triplet *triplet;
int chan = ieee80211_frequency_to_channel(channel->center_freq);
@@ -790,7 +849,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
/* Invalid IE */
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
- return;
+ return 0;
triplet = (void *)(country_ie + 3);
country_ie_len -= 3;
@@ -831,19 +890,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
}
if (!have_chan_pwr)
- return;
+ return 0;
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
- if (sdata->local->ap_power_level == new_ap_level)
- return;
+ if (sdata->ap_power_level == new_ap_level)
+ return 0;
sdata_info(sdata,
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
new_ap_level, chan_pwr, *pwr_constr_elem,
sdata->u.mgd.bssid);
- sdata->local->ap_power_level = new_ap_level;
- ieee80211_hw_config(sdata->local, 0);
+ sdata->ap_power_level = new_ap_level;
+ if (__ieee80211_recalc_txpower(sdata))
+ return BSS_CHANGED_TXPOWER;
+ return 0;
}
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
@@ -1280,7 +1341,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
}
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
- if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ)
+ if (ieee80211_get_sdata_band(sdata) == IEEE80211_BAND_5GHZ)
use_short_slot = true;
if (use_protection != bss_conf->use_cts_prot) {
@@ -1321,6 +1382,29 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
+ if (sdata->vif.p2p) {
+ const struct cfg80211_bss_ies *ies;
+
+ rcu_read_lock();
+ ies = rcu_dereference(cbss->ies);
+ if (ies) {
+ u8 noa[2];
+ int ret;
+
+ ret = cfg80211_get_p2p_attr(
+ ies->data, ies->len,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ noa, sizeof(noa));
+ if (ret >= 2) {
+ bss_conf->p2p_oppps = noa[1] & 0x80;
+ bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+ bss_info_changed |= BSS_CHANGED_P2P_PS;
+ sdata->u.mgd.p2p_noa_index = noa[0];
+ }
+ }
+ rcu_read_unlock();
+ }
+
/* just to be sure */
ieee80211_stop_poll(sdata);
@@ -1350,7 +1434,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_ps(local, -1);
mutex_unlock(&local->iflist_mtx);
- ieee80211_recalc_smps(local);
+ ieee80211_recalc_smps(sdata);
ieee80211_recalc_ps_vif(sdata);
netif_tx_start_all_queues(sdata->dev);
@@ -1443,11 +1527,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
+ sdata->vif.bss_conf.p2p_ctwindow = 0;
+ sdata->vif.bss_conf.p2p_oppps = false;
+
/* on the next assoc, re-program HT parameters */
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
- local->ap_power_level = 0;
+ sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -1465,10 +1552,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
- /* channel(_type) changes are handled by ieee80211_hw_config */
- WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
- ieee80211_hw_config(local, 0);
-
/* disassociated - set to defaults now */
ieee80211_set_wmm_default(sdata, false);
@@ -1478,6 +1561,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.chswitch_timer);
sdata->u.mgd.timers_running = 0;
+
+ ifmgd->flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1581,6 +1667,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
} else {
int ssid_len;
+ rcu_read_lock();
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0;
@@ -1589,7 +1676,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
0, (u32) -1, true, false,
- ifmgd->associated->channel);
+ ifmgd->associated->channel, false);
+ rcu_read_unlock();
}
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
@@ -1685,6 +1773,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
else
return NULL;
+ rcu_read_lock();
ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0;
@@ -1692,10 +1781,10 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
ssid_len = ssid[1];
skb = ieee80211_build_probe_req(sdata, cbss->bssid,
- (u32) -1,
- sdata->local->oper_channel,
+ (u32) -1, cbss->channel,
ssid + 2, ssid_len,
NULL, 0, true);
+ rcu_read_unlock();
return skb;
}
@@ -1804,6 +1893,8 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+ sdata->u.mgd.flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
cfg80211_put_bss(auth_data->bss);
@@ -1824,7 +1915,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
return;
auth_data->expected_transaction = 4;
drv_mgd_prepare_tx(sdata->local, sdata);
- ieee80211_send_auth(sdata, 3, auth_data->algorithm,
+ ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
elems.challenge - 2, elems.challenge_len + 2,
auth_data->bss->bssid, auth_data->bss->bssid,
auth_data->key, auth_data->key_len,
@@ -1858,8 +1949,13 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
status_code = le16_to_cpu(mgmt->u.auth.status_code);
if (auth_alg != ifmgd->auth_data->algorithm ||
- auth_transaction != ifmgd->auth_data->expected_transaction)
+ auth_transaction != ifmgd->auth_data->expected_transaction) {
+ sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n",
+ mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
+ auth_transaction,
+ ifmgd->auth_data->expected_transaction);
return RX_MGMT_NONE;
+ }
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied authentication (status %d)\n",
@@ -1872,6 +1968,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
case WLAN_AUTH_FT:
+ case WLAN_AUTH_SAE:
break;
case WLAN_AUTH_SHARED_KEY:
if (ifmgd->auth_data->expected_transaction != 4) {
@@ -1891,6 +1988,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
run_again(ifmgd, ifmgd->auth_data->timeout);
+ if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
+ ifmgd->auth_data->expected_transaction != 2) {
+ /*
+ * Report auth frame to user space for processing since another
+ * round of Authentication frames is still needed.
+ */
+ return RX_MGMT_CFG80211_RX_AUTH;
+ }
+
/* move station state to auth */
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get(sdata, bssid);
@@ -2030,6 +2136,8 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+ sdata->u.mgd.flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
kfree(assoc_data);
@@ -2091,15 +2199,20 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false;
}
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
- if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
sta->supports_40mhz =
sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
+ ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ elems.vht_cap_elem,
+ &sta->sta.vht_cap);
+
rate_control_rate_init(sta);
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
@@ -2140,7 +2253,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_QOS;
if (elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
cbss->bssid, false);
@@ -2247,9 +2360,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return RX_MGMT_CFG80211_RX_ASSOC;
}
+
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
+ struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status,
struct ieee802_11_elems *elems,
bool beacon)
@@ -2369,8 +2482,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
u32 changed = 0;
- bool erp_valid, directed_tim = false;
+ bool erp_valid;
u8 erp_value = 0;
u32 ncrc;
u8 *bssid;
@@ -2382,8 +2497,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
- if (rx_status->freq != local->oper_channel->center_freq)
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
+ rcu_read_unlock();
return;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
@@ -2490,11 +2616,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
len - baselen, &elems,
care_about_ies, ncrc);
- if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
- directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
- ifmgd->aid);
-
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
+ bool directed_tim = ieee80211_check_tim(elems.tim,
+ elems.tim_len,
+ ifmgd->aid);
if (directed_tim) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
@@ -2519,6 +2644,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (sdata->vif.p2p) {
+ u8 noa[2];
+ int ret;
+
+ ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
+ len - baselen,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ noa, sizeof(noa));
+ if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) {
+ bss_conf->p2p_oppps = noa[1] & 0x80;
+ bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+ changed |= BSS_CHANGED_P2P_PS;
+ sdata->u.mgd.p2p_noa_index = noa[0];
+ /*
+ * make sure we update all information, the CRC
+ * mechanism doesn't look at P2P attributes.
+ */
+ ifmgd->beacon_crc_valid = false;
+ }
+ }
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -2543,22 +2689,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
- struct ieee80211_supported_band *sband;
-
- sband = local->hw.wiphy->bands[local->oper_channel->band];
-
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
bssid, true);
- }
if (elems.country_elem && elems.pwr_constr_elem &&
mgmt->u.probe_resp.capab_info &
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
- ieee80211_handle_pwr_constr(sdata, local->oper_channel,
- elems.country_elem,
- elems.country_elem_len,
- elems.pwr_constr_elem);
+ changed |= ieee80211_handle_pwr_constr(sdata, chan,
+ elems.country_elem,
+ elems.country_elem_len,
+ elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed);
}
@@ -2703,13 +2844,23 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
drv_mgd_prepare_tx(local, sdata);
if (auth_data->bss->proberesp_ies) {
+ u16 trans = 1;
+ u16 status = 0;
+
sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
auth_data->bss->bssid, auth_data->tries,
IEEE80211_AUTH_MAX_TRIES);
auth_data->expected_transaction = 2;
- ieee80211_send_auth(sdata, 1, auth_data->algorithm,
- auth_data->ie, auth_data->ie_len,
+
+ if (auth_data->algorithm == WLAN_AUTH_SAE) {
+ trans = auth_data->sae_trans;
+ status = auth_data->sae_status;
+ auth_data->expected_transaction = trans;
+ }
+
+ ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
+ auth_data->data, auth_data->data_len,
auth_data->bss->bssid,
auth_data->bss->bssid, NULL, 0, 0);
} else {
@@ -2719,16 +2870,20 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
auth_data->bss->bssid, auth_data->tries,
IEEE80211_AUTH_MAX_TRIES);
+ rcu_read_lock();
ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
- if (!ssidie)
+ if (!ssidie) {
+ rcu_read_unlock();
return -EINVAL;
+ }
/*
* Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
NULL, 0, (u32) -1, true, false,
- auth_data->bss->channel);
+ auth_data->bss->channel, false);
+ rcu_read_unlock();
}
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -3058,90 +3213,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
return 0;
}
+static u32 chandef_downgrade(struct cfg80211_chan_def *c)
+{
+ u32 ret;
+ int tmp;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_20:
+ c->width = NL80211_CHAN_WIDTH_20_NOHT;
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ c->width = NL80211_CHAN_WIDTH_20;
+ c->center_freq1 = c->chan->center_freq;
+ ret = IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
+ c->width = NL80211_CHAN_WIDTH_40;
+ ret = IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ c->center_freq2 = 0;
+ c->width = NL80211_CHAN_WIDTH_80;
+ ret = IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P80 */
+ tmp /= 4;
+ c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
+ c->width = NL80211_CHAN_WIDTH_80;
+ ret = IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ break;
+ default:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ WARN_ON_ONCE(1);
+ c->width = NL80211_CHAN_WIDTH_20_NOHT;
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
+ }
+
+ WARN_ON_ONCE(!cfg80211_chandef_valid(c));
+
+ return ret;
+}
+
+static u32
+ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_channel *channel,
+ const struct ieee80211_ht_operation *ht_oper,
+ const struct ieee80211_vht_operation *vht_oper,
+ struct cfg80211_chan_def *chandef)
+{
+ struct cfg80211_chan_def vht_chandef;
+ u32 ht_cfreq, ret;
+
+ chandef->chan = channel;
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = channel->center_freq;
+ chandef->center_freq2 = 0;
+
+ if (!ht_oper || !sband->ht_cap.ht_supported) {
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ chandef->width = NL80211_CHAN_WIDTH_20;
+
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
+ channel->band);
+ /* check that channel matches the right operating channel */
+ if (channel->center_freq != ht_cfreq) {
+ /*
+ * It's possible that some APs are confused here;
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel in association responses, but
+ * since we look at probe response/beacon data here
+ * it should be OK.
+ */
+ sdata_info(sdata,
+ "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
+ channel->center_freq, ht_cfreq,
+ ht_oper->primary_chan, channel->band);
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ /* check 40 MHz support, if we have it */
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 += 10;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 -= 10;
+ break;
+ }
+ } else {
+ /* 40 MHz (and 80 MHz) must be supported for VHT */
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (!vht_oper || !sband->vht_cap.vht_supported) {
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ vht_chandef.chan = channel;
+ vht_chandef.center_freq1 =
+ ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
+ channel->band);
+ vht_chandef.center_freq2 = 0;
+
+ if (vht_oper->center_freq_seg2_idx)
+ vht_chandef.center_freq2 =
+ ieee80211_channel_to_frequency(
+ vht_oper->center_freq_seg2_idx,
+ channel->band);
+
+ switch (vht_oper->chan_width) {
+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
+ vht_chandef.width = chandef->width;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_80;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_160;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+ break;
+ default:
+ sdata_info(sdata,
+ "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
+ vht_oper->chan_width);
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (!cfg80211_chandef_valid(&vht_chandef)) {
+ sdata_info(sdata,
+ "AP VHT information is invalid, disable VHT\n");
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
+ sdata_info(sdata,
+ "AP VHT information doesn't match HT, disable VHT\n");
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ *chandef = vht_chandef;
+
+ ret = 0;
+
+ while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED)) {
+ if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
+ ret = IEEE80211_STA_DISABLE_HT |
+ IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ ret = chandef_downgrade(chandef);
+ }
+
+ if (chandef->width != vht_chandef.width)
+ sdata_info(sdata,
+ "local regulatory prevented using AP HT/VHT configuration, downgraded\n");
+
+out:
+ WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
+ return ret;
+}
+
+static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_bss *cbss)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *ht_cap_ie, *vht_cap_ie;
+ const struct ieee80211_ht_cap *ht_cap;
+ const struct ieee80211_vht_cap *vht_cap;
+ u8 chains = 1;
+
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
+ return chains;
+
+ ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
+ if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
+ ht_cap = (void *)(ht_cap_ie + 2);
+ chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
+ /*
+ * TODO: use "Tx Maximum Number Spatial Streams Supported" and
+ * "Tx Unequal Modulation Supported" fields.
+ */
+ }
+
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
+ return chains;
+
+ vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
+ if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
+ u8 nss;
+ u16 tx_mcs_map;
+
+ vht_cap = (void *)(vht_cap_ie + 2);
+ tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
+ for (nss = 8; nss > 0; nss--) {
+ if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
+ IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ break;
+ }
+ /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
+ chains = max(chains, nss);
+ }
+
+ return chains;
+}
+
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- int ht_cfreq;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- const u8 *ht_oper_ie;
const struct ieee80211_ht_operation *ht_oper = NULL;
+ const struct ieee80211_vht_operation *vht_oper = NULL;
struct ieee80211_supported_band *sband;
+ struct cfg80211_chan_def chandef;
+ int ret;
sband = local->hw.wiphy->bands[cbss->channel->band];
- ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
+ ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ);
+
+ rcu_read_lock();
+
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ sband->ht_cap.ht_supported) {
+ const u8 *ht_oper_ie;
- if (sband->ht_cap.ht_supported) {
- ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
- cbss->information_elements,
- cbss->len_information_elements);
+ ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
ht_oper = (void *)(ht_oper_ie + 2);
}
- if (ht_oper) {
- ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
- cbss->channel->band);
- /* check that channel matches the right operating channel */
- if (cbss->channel->center_freq != ht_cfreq) {
- /*
- * It's possible that some APs are confused here;
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel in association responses, but
- * since we look at probe response/beacon data here
- * it should be OK.
- */
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+ sband->vht_cap.vht_supported) {
+ const u8 *vht_oper_ie;
+
+ vht_oper_ie = ieee80211_bss_get_ie(cbss,
+ WLAN_EID_VHT_OPERATION);
+ if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
+ vht_oper = (void *)(vht_oper_ie + 2);
+ if (vht_oper && !ht_oper) {
+ vht_oper = NULL;
sdata_info(sdata,
- "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
- cbss->channel->center_freq,
- ht_cfreq, ht_oper->primary_chan,
- cbss->channel->band);
- ht_oper = NULL;
- } else {
- channel_type = NL80211_CHAN_HT20;
+ "AP advertised VHT without HT, disabling both\n");
+ sdata->flags |= IEEE80211_STA_DISABLE_HT;
+ sdata->flags |= IEEE80211_STA_DISABLE_VHT;
}
}
- if (ht_oper && sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
- /*
- * cfg80211 already verified that the channel itself can
- * be used, but it didn't check that we can do the right
- * HT type, so do that here as well. If HT40 isn't allowed
- * on this channel, disable 40 MHz operation.
- */
+ ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
+ cbss->channel,
+ ht_oper, vht_oper,
+ &chandef);
- switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
+ sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
+ local->rx_chains);
- if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- channel_type = NL80211_CHAN_HT20;
- sdata_info(sdata,
- "disabling 40 MHz due to multi-vif mismatch\n");
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- channel_type));
- }
+ rcu_read_unlock();
- local->oper_channel = cbss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ /* will change later if needed */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
- return 0;
+ /*
+ * If this fails (possibly due to channel context sharing
+ * on incompatible channels, e.g. 80+80 and 160 sharing the
+ * same control channel) try to use a smaller bandwidth.
+ */
+ ret = ieee80211_vif_use_channel(sdata, &chandef,
+ IEEE80211_CHANCTX_SHARED);
+ while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
+ ifmgd->flags |= chandef_downgrade(&chandef);
+ return ret;
}
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
@@ -3211,7 +3589,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
- if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+ if (cbss->channel->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
@@ -3273,19 +3651,33 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
case NL80211_AUTHTYPE_NETWORK_EAP:
auth_alg = WLAN_AUTH_LEAP;
break;
+ case NL80211_AUTHTYPE_SAE:
+ auth_alg = WLAN_AUTH_SAE;
+ break;
default:
return -EOPNOTSUPP;
}
- auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL);
+ auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len +
+ req->ie_len, GFP_KERNEL);
if (!auth_data)
return -ENOMEM;
auth_data->bss = req->bss;
+ if (req->sae_data_len >= 4) {
+ __le16 *pos = (__le16 *) req->sae_data;
+ auth_data->sae_trans = le16_to_cpu(pos[0]);
+ auth_data->sae_status = le16_to_cpu(pos[1]);
+ memcpy(auth_data->data, req->sae_data + 4,
+ req->sae_data_len - 4);
+ auth_data->data_len += req->sae_data_len - 4;
+ }
+
if (req->ie && req->ie_len) {
- memcpy(auth_data->ie, req->ie, req->ie_len);
- auth_data->ie_len = req->ie_len;
+ memcpy(&auth_data->data[auth_data->data_len],
+ req->ie, req->ie_len);
+ auth_data->data_len += req->ie_len;
}
if (req->key && req->key_len) {
@@ -3355,14 +3747,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
const u8 *ssidie, *ht_ie;
int i, err;
- ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
- if (!ssidie)
- return -EINVAL;
-
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
if (!assoc_data)
return -ENOMEM;
+ rcu_read_lock();
+ ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+ if (!ssidie) {
+ rcu_read_unlock();
+ kfree(assoc_data);
+ return -EINVAL;
+ }
+ memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
+ assoc_data->ssid_len = ssidie[1];
+ rcu_read_unlock();
+
mutex_lock(&ifmgd->mtx);
if (ifmgd->associated)
@@ -3388,13 +3787,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* prepare assoc data */
- /*
- * keep only the 40 MHz disable bit set as it might have
- * been set during authentication already, all other bits
- * should be reset for a new connection
- */
- ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ;
-
ifmgd->beacon_crc_valid = false;
/*
@@ -3408,7 +3800,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
netdev_info(sdata->dev,
"disabling HT/VHT due to WEP/TKIP use\n");
@@ -3416,7 +3808,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
if (req->flags & ASSOC_REQ_DISABLE_HT) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
}
@@ -3424,7 +3816,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
if (!bss->wmm_used)
netdev_info(sdata->dev,
"disabling HT as WMM/QoS is not supported by the AP\n");
@@ -3452,11 +3844,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
if (ifmgd->powersave)
- ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
+ sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
else
- ifmgd->ap_smps = IEEE80211_SMPS_OFF;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
} else
- ifmgd->ap_smps = ifmgd->req_smps;
+ sdata->smps_mode = ifmgd->req_smps;
assoc_data->capability = req->bss->capability;
assoc_data->wmm = bss->wmm_used &&
@@ -3464,12 +3856,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
+ rcu_read_lock();
ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
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
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+ rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@@ -3480,9 +3874,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
}
- memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
- assoc_data->ssid_len = ssidie[1];
-
if (req->prev_bssid)
memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);
@@ -3560,40 +3951,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change;
+ bool sent_frame = false;
mutex_lock(&ifmgd->mtx);
- if (ifmgd->auth_data) {
- ieee80211_destroy_auth_data(sdata, false);
- mutex_unlock(&ifmgd->mtx);
- return 0;
- }
-
sdata_info(sdata,
"deauthenticating from %pM by local choice (reason=%d)\n",
req->bssid, req->reason_code);
- if (ifmgd->associated &&
- ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
- ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
- req->reason_code, tx, frame_buf);
- } else {
+ if (ifmgd->auth_data) {
drv_mgd_prepare_tx(sdata->local, sdata);
ieee80211_send_deauth_disassoc(sdata, req->bssid,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
frame_buf);
+ ieee80211_destroy_auth_data(sdata, false);
+ mutex_unlock(&ifmgd->mtx);
+
+ sent_frame = tx;
+ goto out;
}
+ if (ifmgd->associated &&
+ ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+ req->reason_code, tx, frame_buf);
+ sent_frame = tx;
+ }
mutex_unlock(&ifmgd->mtx);
- __cfg80211_send_deauth(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
-
+ out:
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
+ if (sent_frame)
+ __cfg80211_send_deauth(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
+
return 0;
}
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 2c84185dfdb0..a5379aea7d09 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -107,6 +107,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
{
struct ieee80211_sub_if_data *sdata;
+ if (WARN_ON(local->use_chanctx))
+ return;
+
/*
* notify the AP about us leaving the channel and stop all
* STA interfaces.
@@ -145,6 +148,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
{
struct ieee80211_sub_if_data *sdata;
+ if (WARN_ON(local->use_chanctx))
+ return;
+
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
@@ -193,13 +199,14 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
if (roc->mgmt_tx_cookie) {
if (!WARN_ON(!roc->frame)) {
- ieee80211_tx_skb(roc->sdata, roc->frame);
+ ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7,
+ roc->chan->band);
roc->frame = NULL;
}
} else {
- cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc,
- roc->chan, roc->chan_type,
- roc->req_duration, GFP_KERNEL);
+ cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie,
+ roc->chan, roc->req_duration,
+ GFP_KERNEL);
}
roc->notified = true;
@@ -276,8 +283,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, roc->chan,
- roc->chan_type,
+ ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
duration);
roc->started = true;
@@ -313,8 +319,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
if (!roc->mgmt_tx_cookie)
cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
- (unsigned long)roc,
- roc->chan, roc->chan_type,
+ roc->cookie, roc->chan,
GFP_KERNEL);
list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
@@ -353,7 +358,6 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_recalc_idle(local);
local->tmp_channel = roc->chan;
- local->tmp_channel_type = roc->chan_type;
ieee80211_hw_config(local, 0);
/* tell userspace or send frame */
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 5c572e7a1a71..79a48f37d409 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -33,6 +33,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
+ struct ieee80211_chanctx *ctx;
if (!local->open_count)
goto suspend;
@@ -135,12 +136,55 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED);
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ rcu_access_pointer(sdata->u.ap.beacon))
+ drv_stop_ap(local, sdata);
+
+ if (local->use_chanctx) {
+ struct ieee80211_chanctx_conf *conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(
+ sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf,
+ struct ieee80211_chanctx,
+ conf);
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+ }
+
+ mutex_unlock(&local->chanctx_mtx);
+ }
drv_remove_interface(local, sdata);
}
sdata = rtnl_dereference(local->monitor_sdata);
- if (sdata)
+ if (sdata) {
+ if (local->use_chanctx) {
+ struct ieee80211_chanctx_conf *conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(
+ sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf,
+ struct ieee80211_chanctx,
+ conf);
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+ }
+
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
drv_remove_interface(local, sdata);
+ }
+
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ drv_remove_chanctx(local, ctx);
+ mutex_unlock(&local->chanctx_mtx);
/* stop hardware - this must stop RX */
if (local->open_count)
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 3313c117b322..dd88381c53b7 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -391,7 +391,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
return;
/* if HT BSS, and we handle a data frame, also try HT rates */
- if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT)
+ if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return;
fc = hdr->frame_control;
@@ -408,8 +408,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
alt_rate.flags |= IEEE80211_TX_RC_MCS;
- if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) ||
- (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS))
+ if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_40)
alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 10de668eb9f6..301386dabf88 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -52,11 +52,21 @@ static inline void rate_control_rate_init(struct sta_info *sta)
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
+ struct ieee80211_chanctx_conf *chanctx_conf;
if (!ref)
return;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ rcu_read_lock();
+
+ chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
+ rcu_read_unlock();
ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 79633ae06fd6..8c5acdc06226 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -154,6 +154,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
+ struct minstrel_priv *mp = priv;
struct minstrel_sta_info *mi = priv_sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *ar = info->status.rates;
@@ -181,6 +182,10 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (mi->sample_deferred > 0)
mi->sample_deferred--;
+
+ if (time_after(jiffies, mi->stats_update +
+ (mp->update_interval * HZ) / 1000))
+ minstrel_update_stats(mp, mi);
}
@@ -235,10 +240,6 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
- if (time_after(jiffies, mi->stats_update + (mp->update_interval *
- HZ) / 1000))
- minstrel_update_stats(mp, mi);
-
ndx = mi->max_tp_rate;
if (mrr)
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index fb1d4aa65e8c..9f9c453bc45d 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -389,9 +389,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_tx_rate *ar = info->status.rates;
struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
- bool last = false;
+ bool last;
int group;
- int i = 0;
+ int i;
if (!msp->is_ht)
return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
@@ -419,13 +419,11 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
mi->sample_packets += info->status.ampdu_len;
+ last = !minstrel_ht_txstat_valid(&ar[0]);
for (i = 0; !last; i++) {
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
!minstrel_ht_txstat_valid(&ar[i + 1]);
- if (!minstrel_ht_txstat_valid(&ar[i]))
- break;
-
group = minstrel_ht_get_group_idx(&ar[i]);
rate = &mi->groups[group].rates[ar[i].idx % 8];
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 00ade7feb2e3..580704eba8b8 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -40,6 +40,8 @@
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
struct sk_buff *skb)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
__pskb_trim(skb, skb->len - FCS_LEN);
@@ -47,24 +49,29 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
/* driver bug */
WARN_ON(1);
dev_kfree_skb(skb);
- skb = NULL;
+ return NULL;
}
}
+ if (status->vendor_radiotap_len)
+ __pskb_pull(skb, status->vendor_radiotap_len);
+
return skb;
}
-static inline int should_drop_frame(struct sk_buff *skb,
- int present_fcs_len)
+static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (void *)(skb->data + status->vendor_radiotap_len);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
return 1;
- if (unlikely(skb->len < 16 + present_fcs_len))
+ if (unlikely(skb->len < 16 + present_fcs_len +
+ status->vendor_radiotap_len))
return 1;
if (ieee80211_is_ctl(hdr->frame_control) &&
!ieee80211_is_pspoll(hdr->frame_control) &&
@@ -74,32 +81,53 @@ static inline int should_drop_frame(struct sk_buff *skb,
}
static int
-ieee80211_rx_radiotap_len(struct ieee80211_local *local,
- struct ieee80211_rx_status *status)
+ieee80211_rx_radiotap_space(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status)
{
int len;
/* always present fields */
len = sizeof(struct ieee80211_radiotap_header) + 9;
- if (status->flag & RX_FLAG_MACTIME_MPDU)
+ /* allocate extra bitmap */
+ if (status->vendor_radiotap_len)
+ len += 4;
+
+ if (ieee80211_have_rx_timestamp(status)) {
+ len = ALIGN(len, 8);
len += 8;
+ }
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
len += 1;
- if (len & 1) /* padding for RX_FLAGS if necessary */
- len++;
+ /* padding for RX_FLAGS if necessary */
+ len = ALIGN(len, 2);
if (status->flag & RX_FLAG_HT) /* HT info */
len += 3;
if (status->flag & RX_FLAG_AMPDU_DETAILS) {
- /* padding */
- while (len & 3)
- len++;
+ len = ALIGN(len, 4);
len += 8;
}
+ if (status->flag & RX_FLAG_VHT) {
+ len = ALIGN(len, 2);
+ len += 12;
+ }
+
+ if (status->vendor_radiotap_len) {
+ if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
+ status->vendor_radiotap_align = 1;
+ /* align standard part of vendor namespace */
+ len = ALIGN(len, 2);
+ /* allocate standard part of vendor namespace */
+ len += 6;
+ /* align vendor-defined part */
+ len = ALIGN(len, status->vendor_radiotap_align);
+ /* vendor-defined part is already in skb */
+ }
+
return len;
}
@@ -118,6 +146,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
u16 rx_flags = 0;
+ int mpdulen;
+
+ mpdulen = skb->len;
+ if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
+ mpdulen += FCS_LEN;
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len);
@@ -128,17 +161,30 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
- rthdr->it_len = cpu_to_le16(rtap_len);
+ rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
- pos = (unsigned char *)(rthdr+1);
+ pos = (unsigned char *)(rthdr + 1);
+
+ if (status->vendor_radiotap_len) {
+ rthdr->it_present |=
+ cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
+ cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
+ put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
+ pos += 4;
+ }
/* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */
- if (status->flag & RX_FLAG_MACTIME_MPDU) {
- put_unaligned_le64(status->mactime, pos);
- rthdr->it_present |=
- cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ if (ieee80211_have_rx_timestamp(status)) {
+ /* padding */
+ while ((pos - (u8 *)rthdr) & 7)
+ *pos++ = 0;
+ put_unaligned_le64(
+ ieee80211_calculate_rx_timestamp(local, status,
+ mpdulen, 0),
+ pos);
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
pos += 8;
}
@@ -152,7 +198,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- if (!rate || status->flag & RX_FLAG_HT) {
+ if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) {
/*
* Without rate information don't add it. If we have,
* MCS information is a separate field in radiotap,
@@ -172,7 +218,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
if (status->band == IEEE80211_BAND_5GHZ)
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
pos);
- else if (status->flag & RX_FLAG_HT)
+ else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
pos);
else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
@@ -205,7 +251,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_RX_FLAGS */
/* ensure 2 byte alignment for the 2 byte field as required */
if ((pos - (u8 *)rthdr) & 1)
- pos++;
+ *pos++ = 0;
if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;
put_unaligned_le16(rx_flags, pos);
@@ -255,6 +301,56 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = 0;
*pos++ = 0;
}
+
+ if (status->flag & RX_FLAG_VHT) {
+ u16 known = local->hw.radiotap_vht_details;
+
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+ /* known field - how to handle 80+80? */
+ if (status->flag & RX_FLAG_80P80MHZ)
+ known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ put_unaligned_le16(known, pos);
+ pos += 2;
+ /* flags */
+ if (status->flag & RX_FLAG_SHORT_GI)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ pos++;
+ /* bandwidth */
+ if (status->flag & RX_FLAG_80MHZ)
+ *pos++ = 4;
+ else if (status->flag & RX_FLAG_80P80MHZ)
+ *pos++ = 0; /* marked not known above */
+ else if (status->flag & RX_FLAG_160MHZ)
+ *pos++ = 11;
+ else if (status->flag & RX_FLAG_40MHZ)
+ *pos++ = 1;
+ else /* 20 MHz */
+ *pos++ = 0;
+ /* MCS/NSS */
+ *pos = (status->rate_idx << 4) | status->vht_nss;
+ pos += 4;
+ /* coding field */
+ pos++;
+ /* group ID */
+ pos++;
+ /* partial_aid */
+ pos += 2;
+ }
+
+ if (status->vendor_radiotap_len) {
+ /* ensure 2 byte alignment for the vendor field as required */
+ if ((pos - (u8 *)rthdr) & 1)
+ *pos++ = 0;
+ *pos++ = status->vendor_radiotap_oui[0];
+ *pos++ = status->vendor_radiotap_oui[1];
+ *pos++ = status->vendor_radiotap_oui[2];
+ *pos++ = status->vendor_radiotap_subns;
+ put_unaligned_le16(status->vendor_radiotap_len, pos);
+ pos += 2;
+ /* align the actual payload as requested */
+ while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1))
+ *pos++ = 0;
+ }
}
/*
@@ -282,14 +378,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
* the SKB because it has a bad FCS/PLCP checksum.
*/
- /* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
-
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
- /* make sure hdr->frame_control is on the linear part */
- if (!pskb_may_pull(origskb, 2)) {
+ /* ensure hdr->frame_control and vendor radiotap data are in skb head */
+ if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) {
dev_kfree_skb(origskb);
return NULL;
}
@@ -303,6 +396,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return remove_monitor_info(local, origskb);
}
+ /* room for the radiotap header based on driver features */
+ needed_headroom = ieee80211_rx_radiotap_space(local, status);
+
if (should_drop_frame(origskb, present_fcs_len)) {
/* only need to expand headroom if necessary */
skb = origskb;
@@ -374,7 +470,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return origskb;
}
-
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -403,10 +498,10 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
*
* We also use that counter for non-QoS STAs.
*/
- seqno_idx = NUM_RX_DATA_QUEUES;
+ seqno_idx = IEEE80211_NUM_TIDS;
security_idx = 0;
if (ieee80211_is_mgmt(hdr->frame_control))
- security_idx = NUM_RX_DATA_QUEUES;
+ security_idx = IEEE80211_NUM_TIDS;
tid = 0;
}
@@ -481,8 +576,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
struct ieee80211_mmie *mmie;
- if (skb->len < 24 + sizeof(*mmie) ||
- !is_multicast_ether_addr(hdr->da))
+ if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
return -1;
if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
@@ -497,9 +591,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return le16_to_cpu(mmie->key_id);
}
-
-static ieee80211_rx_result
-ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
+static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
char *dev_addr = rx->sdata->vif.addr;
@@ -507,7 +599,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr1)) {
if (ieee80211_has_tods(hdr->frame_control) ||
- !ieee80211_has_fromds(hdr->frame_control))
+ !ieee80211_has_fromds(hdr->frame_control))
return RX_DROP_MONITOR;
if (ether_addr_equal(hdr->addr3, dev_addr))
return RX_DROP_MONITOR;
@@ -539,7 +631,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
mgmt = (struct ieee80211_mgmt *)hdr;
category = mgmt->u.action.category;
if (category != WLAN_CATEGORY_MESH_ACTION &&
- category != WLAN_CATEGORY_SELF_PROTECTED)
+ category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
@@ -551,7 +643,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
return RX_DROP_MONITOR;
-
}
return RX_CONTINUE;
@@ -575,7 +666,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
return (sq1 - sq2) & SEQ_MASK;
}
-
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
int index)
@@ -1148,12 +1238,19 @@ ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static void ap_sta_ps_start(struct sta_info *sta)
+static void sta_ps_start(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
+ struct ps_data *ps;
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
- atomic_inc(&sdata->bss->num_sta_ps);
+ atomic_inc(&ps->num_sta_ps);
set_sta_flag(sta, WLAN_STA_PS_STA);
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
@@ -1161,7 +1258,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
sta->sta.addr, sta->sta.aid);
}
-static void ap_sta_ps_end(struct sta_info *sta)
+static void sta_ps_end(struct sta_info *sta)
{
ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n",
sta->sta.addr, sta->sta.aid);
@@ -1188,9 +1285,9 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
return -EINVAL;
if (start)
- ap_sta_ps_start(sta_inf);
+ sta_ps_start(sta_inf);
else
- ap_sta_ps_end(sta_inf);
+ sta_ps_end(sta_inf);
return 0;
}
@@ -1284,17 +1381,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
/*
* Update last_rx only for IBSS packets which are for the current
- * BSSID to avoid keeping the current IBSS network alive in cases
- * where other STAs start using different BSSID.
+ * BSSID and for station already AUTHORIZED to avoid keeping the
+ * current IBSS network alive in cases where other STAs start
+ * using different BSSID. This will also give the station another
+ * chance to restart the authentication/authorization in case
+ * something went wrong the first time.
*/
if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
NL80211_IFTYPE_ADHOC);
- if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid)) {
+ if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&
+ test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
sta->last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
} else if (!is_multicast_ether_addr(hdr->addr1)) {
@@ -1306,6 +1408,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
@@ -1342,10 +1445,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
*/
if (ieee80211_is_data(hdr->frame_control) &&
!ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_end(sta);
+ sta_ps_end(sta);
} else {
if (ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_start(sta);
+ sta_ps_start(sta);
}
}
@@ -1391,9 +1494,7 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
struct sk_buff **skb)
{
struct ieee80211_fragment_entry *entry;
- int idx;
- idx = sdata->fragment_next;
entry = &sdata->fragments[sdata->fragment_next++];
if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
sdata->fragment_next = 0;
@@ -1580,18 +1681,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static int
-ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
+static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
- if (unlikely(!rx->sta ||
- !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
+ if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
return -EACCES;
return 0;
}
-static int
-ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
+static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -1613,8 +1711,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
return 0;
}
-static int
-ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
+static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
@@ -1998,7 +2095,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
} else {
/* unable to resolve next hop */
mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3,
- 0, reason, fwd_hdr->addr2, sdata);
+ 0, reason, fwd_hdr->addr2, sdata);
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
kfree_skb(fwd_skb);
return RX_DROP_MONITOR;
@@ -2207,7 +2304,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
cfg80211_report_obss_beacon(rx->local->hw.wiphy,
rx->skb->data, rx->skb->len,
- status->freq, sig, GFP_ATOMIC);
+ status->freq, sig);
rx->flags |= IEEE80211_RX_BEACON_REPORTED;
}
@@ -2236,7 +2333,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (len < IEEE80211_MIN_ACTION_SIZE)
return RX_DROP_UNUSABLE;
- if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
+ if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
+ mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_UNUSABLE;
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
@@ -2407,7 +2505,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
if (mesh_action_is_path_sel(mgmt) &&
- (!mesh_path_sel_is_hwmp(sdata)))
+ !mesh_path_sel_is_hwmp(sdata))
break;
goto queue;
}
@@ -2463,7 +2561,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
-
return RX_CONTINUE;
}
@@ -2593,7 +2690,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
goto out_free_skb;
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
+ needed_headroom = ieee80211_rx_radiotap_space(local, status);
if (skb_headroom(skb) < needed_headroom &&
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
@@ -2656,7 +2753,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
status = IEEE80211_SKB_RXCB((rx->skb));
sband = rx->local->hw.wiphy->bands[status->band];
- if (!(status->flag & RX_FLAG_HT))
+ if (!(status->flag & RX_FLAG_HT) &&
+ !(status->flag & RX_FLAG_VHT))
rate = &sband->bitrates[status->rate_idx];
ieee80211_rx_cooked_monitor(rx, rate);
@@ -2823,8 +2921,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta) {
int rate_idx;
- if (status->flag & RX_FLAG_HT)
- rate_idx = 0; /* TODO: HT rates */
+ if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
+ rate_idx = 0; /* TODO: HT/VHT rates */
else
rate_idx = status->rate_idx;
ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2,
@@ -3048,8 +3146,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
WARN_ON_ONCE(softirq_count() == 0);
- if (WARN_ON(status->band < 0 ||
- status->band >= IEEE80211_NUM_BANDS))
+ if (WARN_ON(status->band >= IEEE80211_NUM_BANDS))
goto drop;
sband = local->hw.wiphy->bands[status->band];
@@ -3094,17 +3191,22 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
* hardware error. The driver should catch hardware
* errors.
*/
- if (WARN((status->rate_idx < 0 ||
- status->rate_idx > 76),
+ if (WARN(status->rate_idx > 76,
"Rate marked as an HT rate but passed "
"status->rate_idx is not "
"an MCS index [0-76]: %d (0x%02x)\n",
status->rate_idx,
status->rate_idx))
goto drop;
+ } else if (status->flag & RX_FLAG_VHT) {
+ if (WARN_ONCE(status->rate_idx > 9 ||
+ !status->vht_nss ||
+ status->vht_nss > 8,
+ "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n",
+ status->rate_idx, status->vht_nss))
+ goto drop;
} else {
- if (WARN_ON(status->rate_idx < 0 ||
- status->rate_idx >= sband->n_bitrates))
+ if (WARN_ON(status->rate_idx >= sband->n_bitrates))
goto drop;
rate = &sband->bitrates[status->rate_idx];
}
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 43e60b5a7546..8ed83dcc149f 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -118,7 +118,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
struct ieee80211_tim_ie *tim_ie = elems->tim;
bss->dtim_period = tim_ie->dtim_period;
if (!elems->parse_error)
- bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
+ bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
}
/* If the beacon had no TIM IE, or it was invalid, use 1 */
@@ -174,7 +174,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
u8 *elements;
struct ieee80211_channel *channel;
size_t baselen;
- int freq;
bool beacon;
struct ieee802_11_elems elems;
@@ -209,13 +208,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
- if (elems.ds_params && elems.ds_params_len == 1)
- freq = ieee80211_channel_to_frequency(elems.ds_params[0],
- rx_status->band);
- else
- freq = rx_status->freq;
-
- channel = ieee80211_get_channel(local->hw.wiphy, freq);
+ channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
@@ -254,6 +247,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
local->hw_scan_req->n_channels = n_chans;
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+ local->hw_scan_ies_bufsize,
req->ie, req->ie_len, band,
req->rates[band], 0);
local->hw_scan_req->ie_len = ielen;
@@ -336,6 +330,10 @@ EXPORT_SYMBOL(ieee80211_scan_completed);
static int ieee80211_start_sw_scan(struct ieee80211_local *local)
{
+ /* Software scan is not supported in multi-channel cases */
+ if (local->use_chanctx)
+ return -EOPNOTSUPP;
+
/*
* Hardware/driver doesn't support hw_scan, so use software
* scanning instead. First send a nullfunc frame with power save
@@ -417,7 +415,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
local->scan_req->ie, local->scan_req->ie_len,
local->scan_req->rates[band], false,
local->scan_req->no_cck,
- local->hw.conf.channel);
+ local->hw.conf.channel, true);
/*
* After sending probe requests, wait for probe responses
@@ -448,11 +446,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
u8 *ies;
+ local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len +
+ req->ie_len;
local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) +
- 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
- req->ie_len, GFP_KERNEL);
+ local->hw_scan_ies_bufsize, GFP_KERNEL);
if (!local->hw_scan_req)
return -ENOMEM;
@@ -462,6 +462,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]);
local->hw_scan_req->ie = ies;
+ local->hw_scan_req->flags = req->flags;
local->hw_scan_band = 0;
@@ -480,7 +481,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
__set_bit(SCAN_HW_SCANNING, &local->scanning);
} else if ((req->n_channels == 1) &&
- (req->channels[0] == local->oper_channel)) {
+ (req->channels[0] == local->_oper_channel)) {
/*
* If we are scanning only on the operating channel
* then we do not need to stop normal activities
@@ -562,6 +563,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
unsigned long min_beacon_int = 0;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
+ enum mac80211_scan_state next_scan_state;
/*
* check if at least one STA interface is associated,
@@ -620,10 +622,18 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
usecs_to_jiffies(min_beacon_int * 1024) *
local->hw.conf.listen_interval);
- if (associated && (!tx_empty || bad_latency || listen_int_exceeded))
- local->next_scan_state = SCAN_SUSPEND;
- else
- local->next_scan_state = SCAN_SET_CHANNEL;
+ if (associated && !tx_empty) {
+ if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ next_scan_state = SCAN_ABORT;
+ else
+ next_scan_state = SCAN_SUSPEND;
+ } else if (associated && (bad_latency || listen_int_exceeded)) {
+ next_scan_state = SCAN_SUSPEND;
+ } else {
+ next_scan_state = SCAN_SET_CHANNEL;
+ }
+
+ local->next_scan_state = next_scan_state;
*next_delay = 0;
}
@@ -794,6 +804,9 @@ void ieee80211_scan_work(struct work_struct *work)
case SCAN_RESUME:
ieee80211_scan_state_resume(local, &next_delay);
break;
+ case SCAN_ABORT:
+ aborted = true;
+ goto out_complete;
}
} while (next_delay == 0);
@@ -918,7 +931,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sched_scan_ies sched_scan_ies = {};
- int ret, i;
+ int ret, i, iebufsz;
+
+ iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len + req->ie_len;
mutex_lock(&local->mtx);
@@ -936,10 +952,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (!local->hw.wiphy->bands[i])
continue;
- sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len +
- req->ie_len,
- GFP_KERNEL);
+ sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
if (!sched_scan_ies.ie[i]) {
ret = -ENOMEM;
goto out_free;
@@ -947,8 +960,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
sched_scan_ies.len[i] =
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
- req->ie, req->ie_len, i,
- (u32) -1, 0);
+ iebufsz, req->ie, req->ie_len,
+ i, (u32) -1, 0);
}
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d2eb64e12353..f3e502502fee 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -98,6 +98,7 @@ static void free_sta_work(struct work_struct *wk)
struct tid_ampdu_tx *tid_tx;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
+ struct ps_data *ps;
/*
* At this point, when being called as call_rcu callback,
@@ -107,11 +108,15 @@ static void free_sta_work(struct work_struct *wk)
*/
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
- BUG_ON(!sdata->bss);
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
clear_sta_flag(sta, WLAN_STA_PS_STA);
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&ps->num_sta_ps);
sta_info_recalc_tim(sta);
}
@@ -137,7 +142,7 @@ static void free_sta_work(struct work_struct *wk)
* drivers have to handle aggregation stop being requested, followed
* directly by station destruction.
*/
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx)
continue;
@@ -325,7 +330,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return NULL;
}
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
* timer_to_tid must be initialized with identity mapping
* to enable session_timer's data differentiation. See
@@ -338,7 +343,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered[i]);
}
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
@@ -502,22 +507,22 @@ int sta_info_insert(struct sta_info *sta)
return err;
}
-static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_set(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __set_bit() format.
*/
- bss->tim[aid / 8] |= (1 << (aid % 8));
+ tim[id / 8] |= (1 << (id % 8));
}
-static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_clear(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __clear_bit() format.
*/
- bss->tim[aid / 8] &= ~(1 << (aid % 8));
+ tim[id / 8] &= ~(1 << (id % 8));
}
static unsigned long ieee80211_tids_for_ac(int ac)
@@ -541,14 +546,23 @@ static unsigned long ieee80211_tids_for_ac(int ac)
void sta_info_recalc_tim(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
- struct ieee80211_if_ap *bss = sta->sdata->bss;
+ struct ps_data *ps;
unsigned long flags;
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
+ u16 id;
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ if (WARN_ON_ONCE(!sta->sdata->bss))
+ return;
- if (WARN_ON_ONCE(!sta->sdata->bss))
+ ps = &sta->sdata->bss->ps;
+ id = sta->sta.aid;
+ } else {
return;
+ }
/* No need to do anything if the driver does all */
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
@@ -587,9 +601,9 @@ void sta_info_recalc_tim(struct sta_info *sta)
spin_lock_irqsave(&local->tim_lock, flags);
if (indicate_tim)
- __bss_tim_set(bss, sta->sta.aid);
+ __bss_tim_set(ps->tim, id);
else
- __bss_tim_clear(bss, sta->sta.aid);
+ __bss_tim_clear(ps->tim, id);
if (local->ops->set_tim) {
local->tim_in_locked_section = true;
@@ -893,8 +907,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
continue;
if (time_after(jiffies, sta->last_rx + exp_time)) {
- ibss_dbg(sdata, "expiring inactive STA %pM\n",
- sta->sta.addr);
+ sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
+ sta->sta.addr);
WARN_ON(__sta_info_destroy(sta));
}
}
@@ -948,10 +962,17 @@ static void clear_sta_ps_flags(void *_sta)
{
struct sta_info *sta = _sta;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ps_data *ps;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&ps->num_sta_ps);
}
/* powersave support code */
@@ -965,7 +986,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
clear_sta_flag(sta, WLAN_STA_SP);
- BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+ BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
sta->driver_buffered_tids = 0;
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
@@ -1013,6 +1034,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
__le16 fc;
bool qos = test_sta_flag(sta, WLAN_STA_WME);
struct ieee80211_tx_info *info;
+ struct ieee80211_chanctx_conf *chanctx_conf;
if (qos) {
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
@@ -1062,7 +1084,16 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
- ieee80211_xmit(sdata, skb);
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+
+ ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
+ rcu_read_unlock();
}
static void
@@ -1343,7 +1374,7 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
- if (WARN_ON(tid >= STA_TID_NUM))
+ if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
return;
if (buffered)
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index c88f161f8118..1489bca9ea97 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -80,7 +80,6 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TOFFSET_KNOWN,
};
-#define STA_TID_NUM 16
#define ADDBA_RESP_INTERVAL HZ
#define HT_AGG_MAX_RETRIES 15
#define HT_AGG_BURST_RETRIES 3
@@ -197,15 +196,15 @@ struct tid_ampdu_rx {
struct sta_ampdu_mlme {
struct mutex mtx;
/* rx */
- struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM];
- unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)];
- unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)];
+ struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS];
+ unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
+ unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
/* tx */
struct work_struct work;
- struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM];
- struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM];
- unsigned long last_addba_req_time[STA_TID_NUM];
- u8 addba_req_num[STA_TID_NUM];
+ struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
+ struct tid_ampdu_tx *tid_start_tx[IEEE80211_NUM_TIDS];
+ unsigned long last_addba_req_time[IEEE80211_NUM_TIDS];
+ u8 addba_req_num[IEEE80211_NUM_TIDS];
u8 dialog_token_allocator;
};
@@ -228,6 +227,7 @@ struct sta_ampdu_mlme {
* "the" transmit rate
* @last_rx_rate_idx: rx status rate index of the last data packet
* @last_rx_rate_flag: rx status flag of the last data packet
+ * @last_rx_rate_vht_nss: rx status nss of last data packet
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @drv_unblock_wk: used for driver PS unblocking
@@ -250,6 +250,7 @@ struct sta_ampdu_mlme {
* @rx_dropped: number of dropped MPDUs from this STA
* @last_signal: signal of last received frame from this STA
* @avg_signal: moving average of signal of received frames from this STA
+ * @last_ack_signal: signal of last received Ack frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
* @tx_filtered_count: number of frames the hardware filtered for this STA
* @tx_retry_failed: number of frames that failed retry
@@ -273,7 +274,7 @@ struct sta_ampdu_mlme {
* @t_offset: timing offset relative to this host
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
- * @ch_type: peer's channel type
+ * @ch_width: peer's channel width
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -329,8 +330,9 @@ struct sta_info {
unsigned long rx_dropped;
int last_signal;
struct ewma avg_signal;
+ int last_ack_signal;
/* Plus 1 for non-QoS frames */
- __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1];
+ __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count;
@@ -344,14 +346,15 @@ struct sta_info {
unsigned long tx_fragments;
struct ieee80211_tx_rate last_tx_rate;
int last_rx_rate_idx;
- int last_rx_rate_flag;
+ u32 last_rx_rate_flag;
+ u8 last_rx_rate_vht_nss;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
* Aggregation information, locked with lock.
*/
struct sta_ampdu_mlme ampdu_mlme;
- u8 timer_to_tid[STA_TID_NUM];
+ u8 timer_to_tid[IEEE80211_NUM_TIDS];
#ifdef CONFIG_MAC80211_MESH
/*
@@ -369,7 +372,7 @@ struct sta_info {
struct timer_list plink_timer;
s64 t_offset;
s64 t_offset_setpoint;
- enum nl80211_channel_type ch_type;
+ enum nl80211_chan_width ch_width;
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -551,6 +554,8 @@ int sta_info_flush(struct ieee80211_local *local,
void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo);
+void sta_set_rate_info_rx(struct sta_info *sta,
+ struct rate_info *rinfo);
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time);
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 101eb88a2b78..07d99578a2b1 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -189,30 +189,31 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
}
if (ieee80211_is_action(mgmt->frame_control) &&
- sdata->vif.type == NL80211_IFTYPE_STATION &&
mgmt->u.action.category == WLAN_CATEGORY_HT &&
- mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
+ mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
+ sdata->vif.type == NL80211_IFTYPE_STATION &&
+ ieee80211_sdata_running(sdata)) {
/*
* This update looks racy, but isn't -- if we come
* here we've definitely got a station that we're
* talking to, and on a managed interface that can
* only be the AP. And the only other place updating
- * this variable is before we're associated.
+ * this variable in managed mode is before association.
*/
switch (mgmt->u.action.u.ht_smps.smps_control) {
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
- sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC;
+ sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
break;
case WLAN_HT_SMPS_CONTROL_STATIC:
- sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC;
+ sdata->smps_mode = IEEE80211_SMPS_STATIC;
break;
case WLAN_HT_SMPS_CONTROL_DISABLED:
default: /* shouldn't happen since we don't send that */
- sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
break;
}
- ieee80211_queue_work(&local->hw, &local->recalc_smps);
+ ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
}
}
@@ -324,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
}
+static void ieee80211_report_used_skb(struct ieee80211_local *local,
+ struct sk_buff *skb, bool dropped)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+
+ if (dropped)
+ acked = false;
+
+ if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+ struct ieee80211_sub_if_data *sdata = NULL;
+ struct ieee80211_sub_if_data *iter_sdata;
+ u64 cookie = (unsigned long)skb;
+
+ rcu_read_lock();
+
+ if (skb->dev) {
+ list_for_each_entry_rcu(iter_sdata, &local->interfaces,
+ list) {
+ if (!iter_sdata->dev)
+ continue;
+
+ if (skb->dev == iter_sdata->dev) {
+ sdata = iter_sdata;
+ break;
+ }
+ }
+ } else {
+ sdata = rcu_dereference(local->p2p_sdata);
+ }
+
+ if (!sdata)
+ skb->dev = NULL;
+ else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+ cfg80211_probe_status(sdata->dev, hdr->addr1,
+ cookie, acked, GFP_ATOMIC);
+ } else {
+ cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
+ skb->len, acked, GFP_ATOMIC);
+ }
+
+ rcu_read_unlock();
+ }
+
+ if (unlikely(info->ack_frame_id)) {
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_find(&local->ack_status_frames,
+ info->ack_frame_id);
+ if (ack_skb)
+ idr_remove(&local->ack_status_frames,
+ info->ack_frame_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (ack_skb) {
+ if (!dropped) {
+ /* consumes ack_skb */
+ skb_complete_wifi_ack(ack_skb, acked);
+ } else {
+ dev_kfree_skb_any(ack_skb);
+ }
+ }
+ }
+}
+
/*
* Use a static threshold for now, best value to be determined
* by testing ...
@@ -432,7 +502,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
- ieee80211_set_bar_pending(sta, tid, ssn);
+ if (local->hw.flags &
+ IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL)
+ ieee80211_stop_tx_ba_session(&sta->sta, tid);
+ else
+ ieee80211_set_bar_pending(sta, tid, ssn);
}
}
@@ -469,6 +543,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
sta->lost_packets = 0;
}
}
+
+ if (acked)
+ sta->last_ack_signal = info->status.ack_signal;
}
rcu_read_unlock();
@@ -515,62 +592,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
msecs_to_jiffies(10));
}
- if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
- u64 cookie = (unsigned long)skb;
- bool found = false;
-
- acked = info->flags & IEEE80211_TX_STAT_ACK;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (!sdata->dev)
- continue;
-
- if (skb->dev != sdata->dev)
- continue;
-
- found = true;
- break;
- }
-
- if (!skb->dev) {
- sdata = rcu_dereference(local->p2p_sdata);
- if (sdata)
- found = true;
- }
-
- if (!found)
- skb->dev = NULL;
- else if (ieee80211_is_nullfunc(hdr->frame_control) ||
- ieee80211_is_qos_nullfunc(hdr->frame_control)) {
- cfg80211_probe_status(sdata->dev, hdr->addr1,
- cookie, acked, GFP_ATOMIC);
- } else {
- cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
- skb->len, acked, GFP_ATOMIC);
- }
-
- rcu_read_unlock();
- }
-
- if (unlikely(info->ack_frame_id)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- /* consumes ack_skb */
- if (ack_skb)
- skb_complete_wifi_ack(ack_skb,
- info->flags & IEEE80211_TX_STAT_ACK);
- }
+ ieee80211_report_used_skb(local, skb, false);
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
@@ -646,25 +668,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (unlikely(info->ack_frame_id)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- /* consumes ack_skb */
- if (ack_skb)
- dev_kfree_skb_any(ack_skb);
- }
+ ieee80211_report_used_skb(local, skb, true);
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL(ieee80211_free_txskb);
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 18d9c8a52e9e..a8270b441a6f 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -28,6 +28,25 @@
#define VIF_PR_FMT " vif:%s(%d%s)"
#define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
+#define CHANCTX_ENTRY __field(u32, control_freq) \
+ __field(u32, chan_width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2) \
+ __field(u8, rx_chains_static) \
+ __field(u8, rx_chains_dynamic)
+#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\
+ __entry->chan_width = ctx->conf.def.width; \
+ __entry->center_freq1 = ctx->conf.def.center_freq1; \
+ __entry->center_freq2 = ctx->conf.def.center_freq2; \
+ __entry->rx_chains_static = ctx->conf.rx_chains_static; \
+ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic
+#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d"
+#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width, \
+ __entry->center_freq1, __entry->center_freq2, \
+ __entry->rx_chains_static, __entry->rx_chains_dynamic
+
+
+
/*
* Tracing for driver callbacks.
*/
@@ -301,20 +320,37 @@ TRACE_EVENT(drv_bss_info_changed,
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
+ __field(u32, changed)
__field(bool, assoc)
+ __field(bool, ibss_joined)
+ __field(bool, ibss_creator)
__field(u16, aid)
__field(bool, cts)
__field(bool, shortpre)
__field(bool, shortslot)
+ __field(bool, enable_beacon)
__field(u8, dtimper)
__field(u16, bcnint)
__field(u16, assoc_cap)
__field(u64, sync_tsf)
__field(u32, sync_device_ts)
__field(u32, basic_rates)
- __field(u32, changed)
- __field(bool, enable_beacon)
+ __array(int, mcast_rate, IEEE80211_NUM_BANDS)
__field(u16, ht_operation_mode)
+ __field(s32, cqm_rssi_thold);
+ __field(s32, cqm_rssi_hyst);
+ __field(u32, channel_width);
+ __field(u32, channel_cfreq1);
+ __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt);
+ __field(bool, arp_filter_enabled);
+ __field(bool, qos);
+ __field(bool, idle);
+ __field(bool, ps);
+ __dynamic_array(u8, ssid, info->ssid_len);
+ __field(bool, hidden_ssid);
+ __field(int, txpower)
+ __field(u8, p2p_ctwindow)
+ __field(bool, p2p_oppps)
),
TP_fast_assign(
@@ -323,17 +359,36 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->changed = changed;
__entry->aid = info->aid;
__entry->assoc = info->assoc;
+ __entry->ibss_joined = info->ibss_joined;
+ __entry->ibss_creator = info->ibss_creator;
__entry->shortpre = info->use_short_preamble;
__entry->cts = info->use_cts_prot;
__entry->shortslot = info->use_short_slot;
+ __entry->enable_beacon = info->enable_beacon;
__entry->dtimper = info->dtim_period;
__entry->bcnint = info->beacon_int;
__entry->assoc_cap = info->assoc_capability;
__entry->sync_tsf = info->sync_tsf;
__entry->sync_device_ts = info->sync_device_ts;
__entry->basic_rates = info->basic_rates;
- __entry->enable_beacon = info->enable_beacon;
+ memcpy(__entry->mcast_rate, info->mcast_rate,
+ sizeof(__entry->mcast_rate));
__entry->ht_operation_mode = info->ht_operation_mode;
+ __entry->cqm_rssi_thold = info->cqm_rssi_thold;
+ __entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
+ __entry->channel_width = info->chandef.width;
+ __entry->channel_cfreq1 = info->chandef.center_freq1;
+ memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
+ sizeof(u32) * info->arp_addr_cnt);
+ __entry->arp_filter_enabled = info->arp_filter_enabled;
+ __entry->qos = info->qos;
+ __entry->idle = info->idle;
+ __entry->ps = info->ps;
+ memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+ __entry->hidden_ssid = info->hidden_ssid;
+ __entry->txpower = info->txpower;
+ __entry->p2p_ctwindow = info->p2p_ctwindow;
+ __entry->p2p_oppps = info->p2p_oppps;
),
TP_printk(
@@ -971,28 +1026,31 @@ TRACE_EVENT(drv_get_antenna,
);
TRACE_EVENT(drv_remain_on_channel,
- TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan,
- enum nl80211_channel_type chantype, unsigned int duration),
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
- TP_ARGS(local, chan, chantype, duration),
+ TP_ARGS(local, sdata, chan, duration),
TP_STRUCT__entry(
LOCAL_ENTRY
+ VIF_ENTRY
__field(int, center_freq)
- __field(int, channel_type)
__field(unsigned int, duration)
),
TP_fast_assign(
LOCAL_ASSIGN;
+ VIF_ASSIGN;
__entry->center_freq = chan->center_freq;
- __entry->channel_type = chantype;
__entry->duration = duration;
),
TP_printk(
- LOCAL_PR_FMT " freq:%dMHz duration:%dms",
- LOCAL_PR_ARG, __entry->center_freq, __entry->duration
+ LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms",
+ LOCAL_PR_ARG, VIF_PR_ARG,
+ __entry->center_freq, __entry->duration
)
);
@@ -1001,34 +1059,6 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
TP_ARGS(local)
);
-TRACE_EVENT(drv_offchannel_tx,
- TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
- unsigned int wait),
-
- TP_ARGS(local, skb, chan, channel_type, wait),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(int, center_freq)
- __field(int, channel_type)
- __field(unsigned int, wait)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->center_freq = chan->center_freq;
- __entry->channel_type = channel_type;
- __entry->wait = wait;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
- LOCAL_PR_ARG, __entry->center_freq, __entry->wait
- )
-);
-
TRACE_EVENT(drv_set_ringparam,
TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx),
@@ -1256,6 +1286,146 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
TP_ARGS(local, sdata)
);
+DECLARE_EVENT_CLASS(local_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx),
+
+ TP_ARGS(local, ctx),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ CHANCTX_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ CHANCTX_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT CHANCTX_PR_FMT,
+ LOCAL_PR_ARG, CHANCTX_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_chanctx, drv_add_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, ctx)
+);
+
+DEFINE_EVENT(local_chanctx, drv_remove_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, ctx)
+);
+
+TRACE_EVENT(drv_change_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ u32 changed),
+
+ TP_ARGS(local, ctx, changed),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ CHANCTX_ENTRY
+ __field(u32, changed)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ CHANCTX_ASSIGN;
+ __entry->changed = changed;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT CHANCTX_PR_FMT " changed:%#x",
+ LOCAL_PR_ARG, CHANCTX_PR_ARG, __entry->changed
+ )
+);
+
+DECLARE_EVENT_CLASS(local_sdata_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx),
+
+ TP_ARGS(local, sdata, ctx),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ CHANCTX_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ CHANCTX_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT CHANCTX_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANCTX_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_sdata_chanctx, drv_assign_vif_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, sdata, ctx)
+);
+
+DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, sdata, ctx)
+);
+
+TRACE_EVENT(drv_start_ap,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *info),
+
+ TP_ARGS(local, sdata, info),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u8, dtimper)
+ __field(u16, bcnint)
+ __dynamic_array(u8, ssid, info->ssid_len);
+ __field(bool, hidden_ssid);
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->dtimper = info->dtim_period;
+ __entry->bcnint = info->beacon_int;
+ memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+ __entry->hidden_ssid = info->hidden_ssid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
+DEFINE_EVENT(local_only_evt, drv_restart_complete,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
/*
* Tracing for API calls that drivers call.
*/
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index b858ebe41fda..e9eadc40c09c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -324,22 +324,20 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- /*
- * virtual interfaces are protected by RCU
- */
- rcu_read_lock();
-
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- struct ieee80211_if_ap *ap;
- if (sdata->vif.type != NL80211_IFTYPE_AP)
+ struct ps_data *ps;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ ps = &sdata->u.ap.ps;
+ else
continue;
- ap = &sdata->u.ap;
- skb = skb_dequeue(&ap->ps_bc_buf);
+
+ skb = skb_dequeue(&ps->bc_buf);
if (skb) {
purged++;
dev_kfree_skb(skb);
}
- total += skb_queue_len(&ap->ps_bc_buf);
+ total += skb_queue_len(&ps->bc_buf);
}
/*
@@ -360,8 +358,6 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
}
}
- rcu_read_unlock();
-
local->total_ps_buffered = total;
ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged);
}
@@ -371,6 +367,7 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ struct ps_data *ps;
/*
* broadcast/multicast frame
@@ -380,16 +377,24 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
* This is done either by the hardware or us.
*/
- /* powersaving STAs only in AP/VLAN mode */
- if (!tx->sdata->bss)
+ /* powersaving STAs currently only in AP/VLAN mode */
+ if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
+ tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ if (!tx->sdata->bss)
+ return TX_CONTINUE;
+
+ ps = &tx->sdata->bss->ps;
+ } else {
return TX_CONTINUE;
+ }
+
/* no buffering for ordered frames */
if (ieee80211_has_order(hdr->frame_control))
return TX_CONTINUE;
/* no stations in PS mode */
- if (!atomic_read(&tx->sdata->bss->num_sta_ps))
+ if (!atomic_read(&ps->num_sta_ps))
return TX_CONTINUE;
info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
@@ -404,14 +409,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
purge_old_ps_buffers(tx->local);
- if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) {
+ if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) {
ps_dbg(tx->sdata,
"BC TX buffer full - dropping the oldest frame\n");
- dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
+ dev_kfree_skb(skb_dequeue(&ps->bc_buf));
} else
tx->local->total_ps_buffered++;
- skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
+ skb_queue_tail(&ps->bc_buf, tx->skb);
return TX_QUEUED;
}
@@ -951,7 +956,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
fragnum = 0;
skb_queue_walk(&tx->skbs, skb) {
- int next_len;
const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
hdr = (void *)skb->data;
@@ -970,7 +974,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
} else {
hdr->frame_control &= ~morefrags;
- next_len = 0;
}
hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
fragnum++;
@@ -1372,7 +1375,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending)
+ struct sk_buff *skb, bool txpending,
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1386,20 +1390,18 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
return true;
}
- rcu_read_lock();
-
/* initialises tx */
led_len = skb->len;
res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
if (unlikely(res_prepare == TX_DROP)) {
ieee80211_free_txskb(&local->hw, skb);
- goto out;
+ return true;
} else if (unlikely(res_prepare == TX_QUEUED)) {
- goto out;
+ return true;
}
- info->band = local->hw.conf.channel->band;
+ info->band = band;
/* set up hw_queue value early */
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
@@ -1410,8 +1412,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
if (!invoke_tx_handlers(&tx))
result = __ieee80211_tx(local, &tx.skbs, led_len,
tx.sta, txpending);
- out:
- rcu_read_unlock();
+
return result;
}
@@ -1446,7 +1447,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1454,8 +1456,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
int headroom;
bool may_encrypt;
- rcu_read_lock();
-
may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
headroom = local->tx_headroom;
@@ -1466,7 +1466,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
ieee80211_free_txskb(&local->hw, skb);
- rcu_read_unlock();
return;
}
@@ -1478,13 +1477,11 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
!is_multicast_ether_addr(hdr->addr1) &&
mesh_nexthop_resolve(skb, sdata)) {
/* skb queued: don't free */
- rcu_read_unlock();
return;
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false);
- rcu_read_unlock();
+ ieee80211_tx(sdata, skb, false, band);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1574,7 +1571,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_channel *chan = local->hw.conf.channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
struct ieee80211_radiotap_header *prthdr =
(struct ieee80211_radiotap_header *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1583,26 +1581,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
u16 len_rthdr;
int hdrlen;
- /*
- * Frame injection is not allowed if beaconing is not allowed
- * or if we need radar detection. Beaconing is usually not allowed when
- * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
- * Passive scan is also used in world regulatory domains where
- * your country is not known and as such it should be treated as
- * NO TX unless the channel is explicitly allowed in which case
- * your current regulatory domain would not have the passive scan
- * flag.
- *
- * Since AP mode uses monitor interfaces to inject/TX management
- * frames we can make AP mode the exception to this rule once it
- * supports radar detection as its implementation can deal with
- * radar detection by itself. We can do that later by adding a
- * monitor flag interfaces used for AP support.
- */
- if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_PASSIVE_SCAN)))
- goto fail;
-
/* check for not even having the fixed radiotap header part */
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
goto fail; /* too short to be possibly valid */
@@ -1688,11 +1666,45 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
}
}
- ieee80211_xmit(sdata, skb);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ tmp_sdata = rcu_dereference(local->monitor_sdata);
+ if (tmp_sdata)
+ chanctx_conf =
+ rcu_dereference(tmp_sdata->vif.chanctx_conf);
+ }
+ if (!chanctx_conf)
+ goto fail_rcu;
+
+ chan = chanctx_conf->def.chan;
+
+ /*
+ * Frame injection is not allowed if beaconing is not allowed
+ * or if we need radar detection. Beaconing is usually not allowed when
+ * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
+ * Passive scan is also used in world regulatory domains where
+ * your country is not known and as such it should be treated as
+ * NO TX unless the channel is explicitly allowed in which case
+ * your current regulatory domain would not have the passive scan
+ * flag.
+ *
+ * Since AP mode uses monitor interfaces to inject/TX management
+ * frames we can make AP mode the exception to this rule once it
+ * supports radar detection as its implementation can deal with
+ * radar detection by itself. We can do that later by adding a
+ * monitor flag interfaces used for AP support.
+ */
+ if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_PASSIVE_SCAN)))
+ goto fail_rcu;
+
+ ieee80211_xmit(sdata, skb, chan->band);
rcu_read_unlock();
return NETDEV_TX_OK;
+fail_rcu:
+ rcu_read_unlock();
fail:
dev_kfree_skb(skb);
return NETDEV_TX_OK; /* meaning, we dealt with the skb */
@@ -1734,6 +1746,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
bool multicast;
u32 info_flags = 0;
u16 info_id = 0;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_sub_if_data *ap_sdata;
+ enum ieee80211_band band;
if (unlikely(skb->len < ETH_HLEN))
goto fail;
@@ -1743,9 +1758,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+ rcu_read_lock();
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- rcu_read_lock();
sta = rcu_dereference(sdata->u.vlan.sta);
if (sta) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1758,7 +1774,12 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
}
- rcu_read_unlock();
+ ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+ u.ap);
+ chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
if (sta)
break;
/* fall through */
@@ -1769,6 +1790,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 24;
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_WDS:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1778,15 +1804,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 30;
+ /*
+ * This is the exception! WDS style interfaces are prohibited
+ * when channel contexts are in used so this must be valid
+ */
+ band = local->hw.conf.channel->band;
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++;
- goto fail;
+ goto fail_rcu;
}
- rcu_read_lock();
+
if (!is_multicast_ether_addr(skb->data)) {
mpath = mesh_path_lookup(skb->data, sdata);
if (!mpath)
@@ -1803,7 +1834,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
!(mppath && !ether_addr_equal(mppath->mpp, skb->data))) {
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
skb->data, skb->data + ETH_ALEN);
- rcu_read_unlock();
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata, NULL, NULL);
} else {
@@ -1819,7 +1849,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
mesh_da = mppath->mpp;
else if (mpath)
mesh_da = mpath->dst;
- rcu_read_unlock();
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
mesh_da, sdata->vif.addr);
@@ -1839,13 +1868,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
skb->data + ETH_ALEN);
}
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
#endif
case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
bool tdls_peer = false;
- rcu_read_lock();
sta = sta_info_get(sdata, skb->data);
if (sta) {
authorized = test_sta_flag(sta,
@@ -1856,7 +1888,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
}
- rcu_read_unlock();
/*
* If the TDLS link is enabled, send everything
@@ -1871,7 +1902,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
if (tdls_direct) {
/* link during setup - throw out frames to peer */
if (!tdls_auth)
- goto fail;
+ goto fail_rcu;
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -1896,6 +1927,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
}
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
@@ -1903,9 +1938,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
default:
- goto fail;
+ goto fail_rcu;
}
/*
@@ -1915,13 +1954,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
multicast = is_multicast_ether_addr(hdr.addr1);
if (!multicast) {
- rcu_read_lock();
sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
}
- rcu_read_unlock();
}
/* For mesh, the use of the QoS header is mandatory */
@@ -1949,7 +1986,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- goto fail;
+ goto fail_rcu;
}
if (unlikely(!multicast && skb->sk &&
@@ -2004,7 +2041,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
kfree_skb(tmp_skb);
if (!skb)
- goto fail;
+ goto fail_rcu;
}
hdr.frame_control = fc;
@@ -2052,7 +2089,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
- return NETDEV_TX_OK;
+ skb = NULL;
+ goto fail_rcu;
}
}
@@ -2104,10 +2142,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
info->flags = info_flags;
info->ack_frame_id = info_id;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
+ rcu_read_unlock();
return NETDEV_TX_OK;
+ fail_rcu:
+ rcu_read_unlock();
fail:
dev_kfree_skb(skb);
return NETDEV_TX_OK;
@@ -2142,11 +2183,18 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
struct sta_info *sta;
struct ieee80211_hdr *hdr;
bool result;
+ struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(info->control.vif);
if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
- result = ieee80211_tx(sdata, skb, true);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (unlikely(!chanctx_conf)) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+ result = ieee80211_tx(sdata, skb, true,
+ chanctx_conf->def.chan->band);
} else {
struct sk_buff_head skbs;
@@ -2214,9 +2262,8 @@ void ieee80211_tx_pending(unsigned long data)
/* functions for drivers to get certain frames */
static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_ap *bss,
- struct sk_buff *skb,
- struct beacon_data *beacon)
+ struct ps_data *ps,
+ struct sk_buff *skb)
{
u8 *pos, *tim;
int aid0 = 0;
@@ -2224,27 +2271,27 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
/* Generate bitmap for TIM only if there are any STAs in power save
* mode. */
- if (atomic_read(&bss->num_sta_ps) > 0)
+ if (atomic_read(&ps->num_sta_ps) > 0)
/* in the hope that this is faster than
* checking byte-for-byte */
- have_bits = !bitmap_empty((unsigned long*)bss->tim,
+ have_bits = !bitmap_empty((unsigned long*)ps->tim,
IEEE80211_MAX_AID+1);
- if (bss->dtim_count == 0)
- bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
+ if (ps->dtim_count == 0)
+ ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
else
- bss->dtim_count--;
+ ps->dtim_count--;
tim = pos = (u8 *) skb_put(skb, 6);
*pos++ = WLAN_EID_TIM;
*pos++ = 4;
- *pos++ = bss->dtim_count;
+ *pos++ = ps->dtim_count;
*pos++ = sdata->vif.bss_conf.dtim_period;
- if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
+ if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf))
aid0 = 1;
- bss->dtim_bc_mc = aid0 == 1;
+ ps->dtim_bc_mc = aid0 == 1;
if (have_bits) {
/* Find largest even number N1 so that bits numbered 1 through
@@ -2252,14 +2299,14 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
* (N2 + 1) x 8 through 2007 are 0. */
n1 = 0;
for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
- if (bss->tim[i]) {
+ if (ps->tim[i]) {
n1 = i & 0xfe;
break;
}
}
n2 = n1;
for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
- if (bss->tim[i]) {
+ if (ps->tim[i]) {
n2 = i;
break;
}
@@ -2269,7 +2316,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
*pos++ = n1 | aid0;
/* Part Virt Bitmap */
skb_put(skb, n2 - n1);
- memcpy(pos, bss->tim + n1, n2 - n1 + 1);
+ memcpy(pos, ps->tim + n1, n2 - n1 + 1);
tim[1] = n2 - n1 + 4;
} else {
@@ -2286,16 +2333,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info;
struct ieee80211_sub_if_data *sdata = NULL;
- struct ieee80211_if_ap *ap = NULL;
- struct beacon_data *beacon;
- enum ieee80211_band band = local->oper_channel->band;
+ enum ieee80211_band band;
struct ieee80211_tx_rate_control txrc;
+ struct ieee80211_chanctx_conf *chanctx_conf;
rcu_read_lock();
sdata = vif_to_sdata(vif);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!ieee80211_sdata_running(sdata))
+ if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
goto out;
if (tim_offset)
@@ -2304,8 +2351,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*tim_length = 0;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
- ap = &sdata->u.ap;
- beacon = rcu_dereference(ap->beacon);
+ struct ieee80211_if_ap *ap = &sdata->u.ap;
+ struct beacon_data *beacon = rcu_dereference(ap->beacon);
+
if (beacon) {
/*
* headroom, head length,
@@ -2329,14 +2377,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
- ieee80211_beacon_add_tim(sdata, ap, skb,
- beacon);
+ ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
} else {
unsigned long flags;
spin_lock_irqsave(&local->tim_lock, flags);
- ieee80211_beacon_add_tim(sdata, ap, skb,
- beacon);
+ ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
spin_unlock_irqrestore(&local->tim_lock, flags);
}
@@ -2412,6 +2458,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
+ band = chanctx_conf->def.chan->band;
+
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
mesh_add_ds_params_ie(skb, sdata) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@@ -2429,6 +2477,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
goto out;
}
+ band = chanctx_conf->def.chan->band;
+
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -2573,7 +2623,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get);
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len)
+ size_t tailroom)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local;
@@ -2587,7 +2637,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
ie_ssid_len = 2 + ssid_len;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
- ie_ssid_len + ie_len);
+ ie_ssid_len + tailroom);
if (!skb)
return NULL;
@@ -2608,11 +2658,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
- if (ie) {
- pos = skb_put(skb, ie_len);
- memcpy(pos, ie, ie_len);
- }
-
return skb;
}
EXPORT_SYMBOL(ieee80211_probereq_get);
@@ -2656,29 +2701,40 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct ieee80211_tx_data tx;
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_if_ap *bss = NULL;
- struct beacon_data *beacon;
+ struct ps_data *ps;
struct ieee80211_tx_info *info;
+ struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(vif);
- bss = &sdata->u.ap;
rcu_read_lock();
- beacon = rcu_dereference(bss->beacon);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (!chanctx_conf)
+ goto out;
- if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct beacon_data *beacon =
+ rcu_dereference(sdata->u.ap.beacon);
+
+ if (!beacon || !beacon->head)
+ goto out;
+
+ ps = &sdata->u.ap.ps;
+ } else {
goto out;
+ }
- if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
+ if (ps->dtim_count != 0 || !ps->dtim_bc_mc)
goto out; /* send buffered bc/mc only after DTIM beacon */
while (1) {
- skb = skb_dequeue(&bss->ps_bc_buf);
+ skb = skb_dequeue(&ps->bc_buf);
if (!skb)
goto out;
local->total_ps_buffered--;
- if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
+ if (!skb_queue_empty(&ps->bc_buf) && skb->len >= 2) {
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) skb->data;
/* more buffered multicast/broadcast frames ==> set
@@ -2696,7 +2752,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
info = IEEE80211_SKB_CB(skb);
tx.flags |= IEEE80211_TX_PS_BUFFERED;
- info->band = local->oper_channel->band;
+ info->band = chanctx_conf->def.chan->band;
if (invoke_tx_handlers(&tx))
skb = NULL;
@@ -2707,8 +2763,9 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
-void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid)
+void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band)
{
int ac = ieee802_1d_to_ac[tid & 7];
@@ -2725,6 +2782,6 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
* requirements are that we do not come into tx with bhs on.
*/
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
local_bh_enable();
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0151ae33c4cd..f11e8c540db4 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
EXPORT_SYMBOL(ieee80211_wake_queues);
void ieee80211_iterate_active_interfaces(
- struct ieee80211_hw *hw,
+ struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
@@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces(
default:
break;
}
+ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
@@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces(
sdata = rcu_dereference_protected(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx));
- if (sdata)
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
mutex_unlock(&local->iflist_mtx);
@@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces(
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
void ieee80211_iterate_active_interfaces_atomic(
- struct ieee80211_hw *hw,
+ struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
@@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic(
default:
break;
}
+ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
}
sdata = rcu_dereference(local->monitor_sdata);
- if (sdata)
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
rcu_read_unlock();
@@ -769,6 +779,18 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
else
elem_parse_failed = true;
break;
+ case WLAN_EID_VHT_CAPABILITY:
+ if (elen >= sizeof(struct ieee80211_vht_cap))
+ elems->vht_cap_elem = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
+ case WLAN_EID_VHT_OPERATION:
+ if (elen >= sizeof(struct ieee80211_vht_operation))
+ elems->vht_operation = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
case WLAN_EID_MESH_ID:
elems->mesh_id = pos;
elems->mesh_id_len = elen;
@@ -837,7 +859,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
if (elem_parse_failed)
elems->parse_error = true;
else
- set_bit(id, seen_elems);
+ __set_bit(id, seen_elems);
left -= elen;
pos += elen;
@@ -860,6 +882,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_queue_params qparam;
+ struct ieee80211_chanctx_conf *chanctx_conf;
int ac;
bool use_11b, enable_qos;
int aCWmin, aCWmax;
@@ -872,8 +895,12 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
memset(&qparam, 0, sizeof(qparam));
- use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) &&
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ use_11b = (chanctx_conf &&
+ chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&
!(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
+ rcu_read_unlock();
/*
* By default disable QoS in STA mode for old access points, which do
@@ -952,7 +979,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
const u8 *supp_rates)
{
- struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
int i, have_higher_than_11mbit = 0;
/* cf. IEEE 802.11 9.2.12 */
@@ -960,11 +987,16 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
if ((supp_rates[i] & 0x7f) * 5 > 110)
have_higher_than_11mbit = 1;
- if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf &&
+ chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+ rcu_read_unlock();
ieee80211_set_wmm_default(sdata, true);
}
@@ -996,7 +1028,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
}
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
- u16 transaction, u16 auth_alg,
+ u16 transaction, u16 auth_alg, u16 status,
u8 *extra, size_t extra_len, const u8 *da,
const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
{
@@ -1021,7 +1053,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->bssid, bssid, ETH_ALEN);
mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
- mgmt->u.auth.status_code = cpu_to_le16(0);
+ mgmt->u.auth.status_code = cpu_to_le16(status);
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
@@ -1075,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
- const u8 *ie, size_t ie_len,
+ size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel)
{
struct ieee80211_supported_band *sband;
- u8 *pos;
+ u8 *pos = buffer, *end = buffer + buffer_len;
size_t offset = 0, noffset;
int supp_rates_len, i;
u8 rates[32];
@@ -1091,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
if (WARN_ON_ONCE(!sband))
return 0;
- pos = buffer;
-
num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0)
@@ -1102,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
supp_rates_len = min_t(int, num_rates, 8);
+ if (end - pos < 2 + supp_rates_len)
+ goto out_err;
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = supp_rates_len;
memcpy(pos, rates, supp_rates_len);
@@ -1118,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
before_extrates,
ARRAY_SIZE(before_extrates),
offset);
+ if (end - pos < noffset - offset)
+ goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
offset = noffset;
@@ -1125,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
ext_rates_len = num_rates - supp_rates_len;
if (ext_rates_len > 0) {
+ if (end - pos < 2 + ext_rates_len)
+ goto out_err;
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = ext_rates_len;
memcpy(pos, rates + supp_rates_len, ext_rates_len);
@@ -1132,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
}
if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+ if (end - pos < 3)
+ goto out_err;
*pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1;
*pos++ = channel;
@@ -1150,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
noffset = ieee80211_ie_split(ie, ie_len,
before_ht, ARRAY_SIZE(before_ht),
offset);
+ if (end - pos < noffset - offset)
+ goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
offset = noffset;
}
- if (sband->ht_cap.ht_supported)
+ if (sband->ht_cap.ht_supported) {
+ if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
+ goto out_err;
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap);
+ }
/*
* If adding more here, adjust code in main.c
@@ -1167,15 +1210,23 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
/* add any remaining custom IEs */
if (ie && ie_len) {
noffset = ie_len;
+ if (end - pos < noffset - offset)
+ goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
}
- if (sband->vht_cap.vht_supported)
+ if (sband->vht_cap.vht_supported) {
+ if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
+ goto out_err;
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
sband->vht_cap.cap);
+ }
return pos - buffer;
+ out_err:
+ WARN_ONCE(1, "not enough space for preq IEs\n");
+ return pos - buffer;
}
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
@@ -1188,14 +1239,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- size_t buf_len;
- u8 *buf;
u8 chan_no;
-
- /* FIXME: come up with a proper value */
- buf = kmalloc(200 + ie_len, GFP_KERNEL);
- if (!buf)
- return NULL;
+ int ies_len;
/*
* Do not send DS Channel parameter for directed probe requests
@@ -1207,14 +1252,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
else
chan_no = ieee80211_frequency_to_channel(chan->center_freq);
- buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band,
- ratemask, chan_no);
-
skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
- ssid, ssid_len,
- buf, buf_len);
+ ssid, ssid_len, 100 + ie_len);
if (!skb)
- goto out;
+ return NULL;
+
+ ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
+ skb_tailroom(skb),
+ ie, ie_len, chan->band,
+ ratemask, chan_no);
+ skb_put(skb, ies_len);
if (dst) {
mgmt = (struct ieee80211_mgmt *) skb->data;
@@ -1224,9 +1271,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
- out:
- kfree(buf);
-
return skb;
}
@@ -1234,7 +1278,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, bool no_cck,
- struct ieee80211_channel *channel)
+ struct ieee80211_channel *channel, bool scan)
{
struct sk_buff *skb;
@@ -1245,7 +1289,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
if (no_cck)
IEEE80211_SKB_CB(skb)->flags |=
IEEE80211_TX_CTL_NO_CCK_RATE;
- ieee80211_tx_skb(sdata, skb);
+ if (scan)
+ ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
+ else
+ ieee80211_tx_skb(sdata, skb);
}
}
@@ -1308,6 +1355,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_chanctx *ctx;
struct sta_info *sta;
int res, i;
@@ -1380,6 +1428,46 @@ int ieee80211_reconfig(struct ieee80211_local *local)
res = drv_add_interface(local, sdata);
}
+ /* add channel contexts */
+ if (local->use_chanctx) {
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ WARN_ON(drv_add_chanctx(local, ctx));
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ struct ieee80211_chanctx_conf *ctx_conf;
+
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+
+ mutex_lock(&local->chanctx_mtx);
+ ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (ctx_conf) {
+ ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+ conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) {
+ struct ieee80211_chanctx_conf *ctx_conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (ctx_conf) {
+ ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+ conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
/* add STAs back */
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
@@ -1435,7 +1523,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM |
BSS_CHANGED_QOS |
- BSS_CHANGED_IDLE;
+ BSS_CHANGED_IDLE |
+ BSS_CHANGED_TXPOWER;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
@@ -1450,11 +1539,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
changed |= BSS_CHANGED_IBSS;
/* fall through */
case NL80211_IFTYPE_AP:
- changed |= BSS_CHANGED_SSID;
+ changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
- if (sdata->vif.type == NL80211_IFTYPE_AP)
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
changed |= BSS_CHANGED_AP_PROBE_RESP;
+ if (rcu_access_pointer(sdata->u.ap.beacon))
+ drv_start_ap(local, sdata);
+ }
+
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
changed |= BSS_CHANGED_BEACON |
@@ -1553,8 +1646,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* If this is for hw restart things are still running.
* We may want to change that later, however.
*/
- if (!local->suspended)
+ if (!local->suspended) {
+ drv_restart_complete(local);
return 0;
+ }
#ifdef CONFIG_PM
/* first set suspended false, then resuming */
@@ -1617,68 +1712,24 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
}
EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
-static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
- enum ieee80211_smps_mode *smps_mode)
-{
- if (ifmgd->associated) {
- *smps_mode = ifmgd->ap_smps;
-
- if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
- if (ifmgd->powersave)
- *smps_mode = IEEE80211_SMPS_DYNAMIC;
- else
- *smps_mode = IEEE80211_SMPS_OFF;
- }
-
- return 1;
- }
-
- return 0;
-}
-
-void ieee80211_recalc_smps(struct ieee80211_local *local)
+void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata;
- enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
- int count = 0;
-
- mutex_lock(&local->iflist_mtx);
-
- /*
- * This function could be improved to handle multiple
- * interfaces better, but right now it makes any
- * non-station interfaces force SM PS to be turned
- * off. If there are multiple station interfaces it
- * could also use the best possible mode, e.g. if
- * one is in static and the other in dynamic then
- * dynamic is ok.
- */
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (!ieee80211_sdata_running(sdata))
- continue;
- if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
- continue;
- if (sdata->vif.type != NL80211_IFTYPE_STATION)
- goto set;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_chanctx *chanctx;
- count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+ mutex_lock(&local->chanctx_mtx);
- if (count > 1) {
- smps_mode = IEEE80211_SMPS_OFF;
- break;
- }
- }
+ chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
- if (smps_mode == local->smps_mode)
+ if (WARN_ON_ONCE(!chanctx_conf))
goto unlock;
- set:
- local->smps_mode = smps_mode;
- /* changed flag is auto-detected for this */
- ieee80211_hw_config(local, 0);
+ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+ ieee80211_recalc_smps_chanctx(local, chanctx);
unlock:
- mutex_unlock(&local->iflist_mtx);
+ mutex_unlock(&local->chanctx_mtx);
}
static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
@@ -1818,8 +1869,8 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
__le32 tmp;
*pos++ = WLAN_EID_VHT_CAPABILITY;
- *pos++ = sizeof(struct ieee80211_vht_capabilities);
- memset(pos, 0, sizeof(struct ieee80211_vht_capabilities));
+ *pos++ = sizeof(struct ieee80211_vht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_vht_cap));
/* capability flags */
tmp = cpu_to_le32(cap);
@@ -1834,8 +1885,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
}
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
u16 prot_mode)
{
struct ieee80211_ht_operation *ht_oper;
@@ -1843,23 +1893,25 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
*pos++ = WLAN_EID_HT_OPERATION;
*pos++ = sizeof(struct ieee80211_ht_operation);
ht_oper = (struct ieee80211_ht_operation *)pos;
- ht_oper->primary_chan =
- ieee80211_frequency_to_channel(channel->center_freq);
- switch (channel_type) {
- case NL80211_CHAN_HT40MINUS:
- ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- break;
- case NL80211_CHAN_HT40PLUS:
- ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ht_oper->primary_chan = ieee80211_frequency_to_channel(
+ chandef->chan->center_freq);
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
break;
- case NL80211_CHAN_HT20:
default:
ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
break;
}
if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
- channel_type != NL80211_CHAN_NO_HT &&
- channel_type != NL80211_CHAN_HT20)
+ chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef->width != NL80211_CHAN_WIDTH_20)
ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
ht_oper->operation_mode = cpu_to_le16(prot_mode);
@@ -1873,13 +1925,17 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
return pos + sizeof(struct ieee80211_ht_operation);
}
-enum nl80211_channel_type
-ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
+void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef)
{
enum nl80211_channel_type channel_type;
- if (!ht_oper)
- return NL80211_CHAN_NO_HT;
+ if (!ht_oper) {
+ cfg80211_chandef_create(chandef, control_chan,
+ NL80211_CHAN_NO_HT);
+ return;
+ }
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
@@ -1895,7 +1951,7 @@ ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
channel_type = NL80211_CHAN_NO_HT;
}
- return channel_type;
+ cfg80211_chandef_create(chandef, control_chan, channel_type);
}
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
@@ -1977,3 +2033,84 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif)
return ifmgd->ave_beacon_signal;
}
EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
+
+u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
+{
+ if (!mcs)
+ return 1;
+
+ /* TODO: consider rx_highest */
+
+ if (mcs->rx_mask[3])
+ return 4;
+ if (mcs->rx_mask[2])
+ return 3;
+ if (mcs->rx_mask[1])
+ return 2;
+ return 1;
+}
+
+/**
+ * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
+ * @local: mac80211 hw info struct
+ * @status: RX status
+ * @mpdu_len: total MPDU length (including FCS)
+ * @mpdu_offset: offset into MPDU to calculate timestamp at
+ *
+ * This function calculates the RX timestamp at the given MPDU offset, taking
+ * into account what the RX timestamp was. An offset of 0 will just normalize
+ * the timestamp to TSF at beginning of MPDU reception.
+ */
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ unsigned int mpdu_len,
+ unsigned int mpdu_offset)
+{
+ u64 ts = status->mactime;
+ struct rate_info ri;
+ u16 rate;
+
+ if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
+ return 0;
+
+ memset(&ri, 0, sizeof(ri));
+
+ /* Fill cfg80211 rate info */
+ if (status->flag & RX_FLAG_HT) {
+ ri.mcs = status->rate_idx;
+ ri.flags |= RATE_INFO_FLAGS_MCS;
+ if (status->flag & RX_FLAG_40MHZ)
+ ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_SHORT_GI)
+ ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ } else if (status->flag & RX_FLAG_VHT) {
+ ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
+ ri.mcs = status->rate_idx;
+ ri.nss = status->vht_nss;
+ if (status->flag & RX_FLAG_40MHZ)
+ ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_80MHZ)
+ ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_80P80MHZ)
+ ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_160MHZ)
+ ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_SHORT_GI)
+ ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[status->band];
+ ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+ }
+
+ rate = cfg80211_calculate_bitrate(&ri);
+
+ /* rewind from end of MPDU */
+ if (status->flag & RX_FLAG_MACTIME_END)
+ ts -= mpdu_len * 8 * 10 / rate;
+
+ ts += mpdu_offset * 8 * 10 / rate;
+
+ return ts;
+}
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
new file mode 100644
index 000000000000..f311388aeedf
--- /dev/null
+++ b/net/mac80211/vht.c
@@ -0,0 +1,35 @@
+/*
+ * VHT handling
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/ieee80211.h>
+#include <linux/export.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+
+
+void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_vht_cap *vht_cap_ie,
+ struct ieee80211_sta_vht_cap *vht_cap)
+{
+ if (WARN_ON_ONCE(!vht_cap))
+ return;
+
+ memset(vht_cap, 0, sizeof(*vht_cap));
+
+ if (!vht_cap_ie || !sband->vht_cap.vht_supported)
+ return;
+
+ vht_cap->vht_supported = true;
+
+ vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
+
+ /* Copy peer MCS info, the driver might need them. */
+ memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
+ sizeof(struct ieee80211_vht_mcs_info));
+}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index cea06e9f26f4..906f00cd6d2f 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -160,31 +160,37 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
return ieee80211_downgrade_queue(sdata, skb);
}
+/**
+ * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
+ *
+ * @sdata: local subif
+ * @skb: packet to be updated
+ */
void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u8 *p;
+ u8 ack_policy, tid;
- /* Fill in the QoS header if there is one. */
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- u8 *p = ieee80211_get_qos_ctl(hdr);
- u8 ack_policy, tid;
-
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return;
- /* preserve EOSP bit */
- ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
+ p = ieee80211_get_qos_ctl(hdr);
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- if (is_multicast_ether_addr(hdr->addr1) ||
- sdata->noack_map & BIT(tid)) {
- ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
- info->flags |= IEEE80211_TX_CTL_NO_ACK;
- }
+ /* preserve EOSP bit */
+ ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
- /* qos header is 2 bytes */
- *p++ = ack_policy | tid;
- *p = ieee80211_vif_is_mesh(&sdata->vif) ?
- (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
+ if (is_multicast_ether_addr(hdr->addr1) ||
+ sdata->noack_map & BIT(tid)) {
+ ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
}
+
+ /* qos header is 2 bytes */
+ *p++ = ack_policy | tid;
+ *p = ieee80211_vif_is_mesh(&sdata->vif) ?
+ (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 8bd2f5c6a56e..c175ee866ff4 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -104,7 +104,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
*/
if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
if (status->flag & RX_FLAG_MMIC_ERROR)
- goto mic_fail;
+ goto mic_fail_no_key;
if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key &&
rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
@@ -161,6 +161,9 @@ update_iv:
return RX_CONTINUE;
mic_fail:
+ rx->key->u.tkip.mic_failures++;
+
+mic_fail_no_key:
/*
* In some cases the key can be unset - e.g. a multicast packet, in
* a driver that supports HW encryption. Send up the key idx only if