summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Kconfig11
-rw-r--r--net/mac80211/Makefile3
-rw-r--r--net/mac80211/cfg.c95
-rw-r--r--net/mac80211/chan.c155
-rw-r--r--net/mac80211/debug.h10
-rw-r--r--net/mac80211/debugfs.c6
-rw-r--r--net/mac80211/debugfs_netdev.c5
-rw-r--r--net/mac80211/debugfs_sta.c5
-rw-r--r--net/mac80211/driver-ops.h54
-rw-r--r--net/mac80211/ht.c110
-rw-r--r--net/mac80211/ibss.c53
-rw-r--r--net/mac80211/ieee80211_i.h196
-rw-r--r--net/mac80211/iface.c132
-rw-r--r--net/mac80211/main.c42
-rw-r--r--net/mac80211/mesh.c284
-rw-r--r--net/mac80211/mesh.h40
-rw-r--r--net/mac80211/mesh_hwmp.c49
-rw-r--r--net/mac80211/mesh_pathtbl.c12
-rw-r--r--net/mac80211/mesh_plink.c120
-rw-r--r--net/mac80211/mesh_ps.c598
-rw-r--r--net/mac80211/mlme.c1081
-rw-r--r--net/mac80211/offchannel.c35
-rw-r--r--net/mac80211/pm.c12
-rw-r--r--net/mac80211/rate.h2
-rw-r--r--net/mac80211/rc80211_minstrel.c29
-rw-r--r--net/mac80211/rc80211_minstrel.h2
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c181
-rw-r--r--net/mac80211/rc80211_minstrel_ht.h5
-rw-r--r--net/mac80211/rc80211_minstrel_ht_debugfs.c112
-rw-r--r--net/mac80211/rx.c161
-rw-r--r--net/mac80211/scan.c66
-rw-r--r--net/mac80211/sta_info.c38
-rw-r--r--net/mac80211/sta_info.h20
-rw-r--r--net/mac80211/status.c25
-rw-r--r--net/mac80211/tkip.c10
-rw-r--r--net/mac80211/trace.h23
-rw-r--r--net/mac80211/tx.c109
-rw-r--r--net/mac80211/util.c82
-rw-r--r--net/mac80211/vht.c172
-rw-r--r--net/mac80211/wme.c13
-rw-r--r--net/mac80211/wpa.c5
41 files changed, 3026 insertions, 1137 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index b4ecf267a34b..0ecf947ad378 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -258,6 +258,17 @@ config MAC80211_MESH_SYNC_DEBUG
Do not select this option.
+config MAC80211_MESH_PS_DEBUG
+ bool "Verbose mesh powersave debugging"
+ depends on MAC80211_DEBUG_MENU
+ depends on MAC80211_MESH
+ ---help---
+ Selecting this option causes mac80211 to print out very verbose mesh
+ powersave debugging messages (when mac80211 is taking part in a
+ mesh network).
+
+ Do not select this option.
+
config MAC80211_TDLS_DEBUG
bool "Verbose TDLS debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 4911202334d9..9d7d840aac6d 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -39,7 +39,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
mesh_pathtbl.o \
mesh_plink.o \
mesh_hwmp.o \
- mesh_sync.o
+ mesh_sync.o \
+ mesh_ps.o
mac80211-$(CONFIG_PM) += pm.o
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 15d886c639e9..179dcbd8be1c 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -492,7 +492,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
STATION_INFO_PLID |
- STATION_INFO_PLINK_STATE;
+ STATION_INFO_PLINK_STATE |
+ STATION_INFO_LOCAL_PM |
+ STATION_INFO_PEER_PM |
+ STATION_INFO_NONPEER_PM;
sinfo->llid = le16_to_cpu(sta->llid);
sinfo->plid = le16_to_cpu(sta->plid);
@@ -501,6 +504,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled |= STATION_INFO_T_OFFSET;
sinfo->t_offset = sta->t_offset;
}
+ sinfo->local_pm = sta->local_pm;
+ sinfo->peer_pm = sta->peer_pm;
+ sinfo->nonpeer_pm = sta->nonpeer_pm;
#endif
}
@@ -922,11 +928,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
/* TODO: make hostapd tell us what it wants */
sdata->smps_mode = IEEE80211_SMPS_OFF;
sdata->needed_rx_chains = sdata->local->rx_chains;
+ sdata->radar_required = params->radar_required;
err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED);
if (err)
return err;
+ ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
/*
* Apply control port protocol, this allows us to
@@ -1041,6 +1049,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
skb_queue_purge(&sdata->u.ap.ps.bc_buf);
+ ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
ieee80211_vif_release_channel(sdata);
return 0;
@@ -1243,25 +1252,26 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (params->ht_capa)
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
- params->ht_capa,
- &sta->sta.ht_cap);
+ params->ht_capa, sta);
if (params->vht_capa)
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
- params->vht_capa,
- &sta->sta.vht_cap);
+ params->vht_capa, sta);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
+ u32 changed = 0;
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
- u32 changed = 0;
-
switch (params->plink_state) {
case NL80211_PLINK_ESTAB:
if (sta->plink_state != NL80211_PLINK_ESTAB)
changed = mesh_plink_inc_estab_count(
sdata);
sta->plink_state = params->plink_state;
+
+ ieee80211_mps_sta_status_update(sta);
+ changed |= ieee80211_mps_set_sta_local_pm(sta,
+ sdata->u.mesh.mshcfg.power_mode);
break;
case NL80211_PLINK_LISTEN:
case NL80211_PLINK_BLOCKED:
@@ -1273,22 +1283,31 @@ static int sta_apply_parameters(struct ieee80211_local *local,
changed = mesh_plink_dec_estab_count(
sdata);
sta->plink_state = params->plink_state;
+
+ ieee80211_mps_sta_status_update(sta);
+ changed |=
+ ieee80211_mps_local_status_update(sdata);
break;
default:
/* nothing */
break;
}
- ieee80211_bss_info_change_notify(sdata, changed);
} else {
switch (params->plink_action) {
case PLINK_ACTION_OPEN:
- mesh_plink_open(sta);
+ changed |= mesh_plink_open(sta);
break;
case PLINK_ACTION_BLOCK:
- mesh_plink_block(sta);
+ changed |= mesh_plink_block(sta);
break;
}
}
+
+ if (params->local_pm)
+ changed |=
+ ieee80211_mps_set_sta_local_pm(sta,
+ params->local_pm);
+ ieee80211_bss_info_change_notify(sdata, changed);
#endif
}
@@ -1393,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
return -ENOENT;
}
- /* in station mode, supported rates are only valid with TDLS */
+ /* in station mode, some updates are only valid with TDLS */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- params->supported_rates &&
+ (params->supported_rates || params->ht_capa || params->vht_capa ||
+ params->sta_modify_mask ||
+ (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
mutex_unlock(&local->sta_mtx);
return -EINVAL;
@@ -1777,6 +1798,14 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
conf->dot11MeshHWMPconfirmationInterval =
nconf->dot11MeshHWMPconfirmationInterval;
+ if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
+ conf->power_mode = nconf->power_mode;
+ ieee80211_mps_local_status_update(sdata);
+ }
+ if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
+ conf->dot11MeshAwakeWindowDuration =
+ nconf->dot11MeshAwakeWindowDuration;
+ ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
return 0;
}
@@ -1802,9 +1831,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
if (err)
return err;
- ieee80211_start_mesh(sdata);
-
- return 0;
+ return ieee80211_start_mesh(sdata);
}
static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
@@ -2369,7 +2396,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
INIT_LIST_HEAD(&roc->dependents);
/* if there's one pending or we're scanning, queue this one */
- if (!list_empty(&local->roc_list) || local->scanning)
+ if (!list_empty(&local->roc_list) ||
+ local->scanning || local->radar_detect_enabled)
goto out_check_combine;
/* if not HW assist, just queue & schedule work */
@@ -2619,6 +2647,37 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
return ieee80211_cancel_roc(local, cookie, false);
}
+static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ unsigned long timeout;
+ int err;
+
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+ /* whatever, but channel contexts should not complain about that one */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = local->rx_chains;
+ sdata->radar_required = true;
+
+ mutex_lock(&local->iflist_mtx);
+ err = ieee80211_vif_use_channel(sdata, chandef,
+ IEEE80211_CHANCTX_SHARED);
+ mutex_unlock(&local->iflist_mtx);
+ if (err)
+ return err;
+
+ timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+ ieee80211_queue_delayed_work(&sdata->local->hw,
+ &sdata->dfs_cac_timer_work, timeout);
+
+ return 0;
+}
+
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait, const u8 *buf, size_t len,
@@ -2723,7 +2782,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
goto out_unlock;
}
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
IEEE80211_SKB_CB(skb)->hw_queue =
local->hw.offchannel_tx_hw_queue;
@@ -3323,4 +3383,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_et_stats = ieee80211_get_et_stats,
.get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
+ .start_radar_detection = ieee80211_start_radar_detection,
};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 1bfe0a8b19d2..78c0d90dd641 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -9,7 +9,7 @@
#include "ieee80211_i.h"
#include "driver-ops.h"
-static void ieee80211_change_chandef(struct ieee80211_local *local,
+static void ieee80211_change_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
const struct cfg80211_chan_def *chandef)
{
@@ -49,7 +49,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
if (!compat)
continue;
- ieee80211_change_chandef(local, ctx, compat);
+ ieee80211_change_chanctx(local, ctx, compat);
return ctx;
}
@@ -91,6 +91,10 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
list_add_rcu(&ctx->list, &local->chanctx_list);
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
+
return ctx;
}
@@ -110,6 +114,10 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
list_del_rcu(&ctx->list);
kfree_rcu(ctx, rcu_head);
+
+ mutex_lock(&local->mtx);
+ ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
}
static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
@@ -128,6 +136,11 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
ctx->refcount++;
ieee80211_recalc_txpower(sdata);
+ sdata->vif.bss_conf.idle = false;
+
+ if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+ sdata->vif.type != NL80211_IFTYPE_MONITOR)
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
return 0;
}
@@ -162,7 +175,7 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
if (WARN_ON_ONCE(!compat))
return;
- ieee80211_change_chandef(local, ctx, compat);
+ ieee80211_change_chanctx(local, ctx, compat);
}
static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
@@ -175,11 +188,18 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
ctx->refcount--;
rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+ sdata->vif.bss_conf.idle = true;
+
+ if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+ sdata->vif.type != NL80211_IFTYPE_MONITOR)
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
+
drv_unassign_vif_chanctx(local, sdata, ctx);
if (ctx->refcount > 0) {
ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx);
+ ieee80211_recalc_radar_chanctx(local, ctx);
}
}
@@ -198,20 +218,42 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
ctx = container_of(conf, struct ieee80211_chanctx, conf);
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- struct ieee80211_sub_if_data *vlan;
-
- /* for the VLAN list */
- ASSERT_RTNL();
- list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
- rcu_assign_pointer(vlan->vif.chanctx_conf, NULL);
- }
-
ieee80211_unassign_vif_chanctx(sdata, ctx);
if (ctx->refcount == 0)
ieee80211_free_chanctx(local, ctx);
}
+void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx)
+{
+ struct ieee80211_sub_if_data *sdata;
+ bool radar_enabled = false;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->radar_required) {
+ radar_enabled = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (radar_enabled == chanctx->conf.radar_enabled)
+ return;
+
+ chanctx->conf.radar_enabled = radar_enabled;
+ local->radar_detect_enabled = chanctx->conf.radar_enabled;
+
+ if (!local->use_chanctx) {
+ local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ }
+
+ drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
+}
+
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx)
{
@@ -326,16 +368,57 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
goto out;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- struct ieee80211_sub_if_data *vlan;
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ ieee80211_recalc_radar_chanctx(local, ctx);
+ out:
+ mutex_unlock(&local->chanctx_mtx);
+ return ret;
+}
+
+int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+ int ret;
+
+ if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ mutex_lock(&local->chanctx_mtx);
+ if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
+ ret = -EINVAL;
+ goto out;
+ }
- /* for the VLAN list */
- ASSERT_RTNL();
- list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
- rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf);
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (!conf) {
+ ret = -EINVAL;
+ goto out;
}
- ieee80211_recalc_smps_chanctx(local, ctx);
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+ if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sdata->vif.bss_conf.chandef = *chandef;
+
+ ieee80211_recalc_chanctx_chantype(local, ctx);
+
+ *changed |= BSS_CHANGED_BANDWIDTH;
+ ret = 0;
out:
mutex_unlock(&local->chanctx_mtx);
return ret;
@@ -369,6 +452,40 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->chanctx_mtx);
}
+void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+ bool clear)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *vlan;
+ struct ieee80211_chanctx_conf *conf;
+
+ ASSERT_RTNL();
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+ return;
+
+ mutex_lock(&local->chanctx_mtx);
+
+ /*
+ * Check that conf exists, even when clearing this function
+ * must be called with the AP's channel context still there
+ * as it would otherwise cause VLANs to have an invalid
+ * channel context pointer for a while, possibly pointing
+ * to a channel context that has already been freed.
+ */
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ WARN_ON(!conf);
+
+ if (clear)
+ conf = NULL;
+
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
+
+ mutex_unlock(&local->chanctx_mtx);
+}
+
void ieee80211_iter_chan_contexts_atomic(
struct ieee80211_hw *hw,
void (*iter)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h
index 8f383a576016..4ccc5ed6237d 100644
--- a/net/mac80211/debug.h
+++ b/net/mac80211/debug.h
@@ -44,6 +44,12 @@
#define MAC80211_MESH_SYNC_DEBUG 0
#endif
+#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
+#define MAC80211_MESH_PS_DEBUG 1
+#else
+#define MAC80211_MESH_PS_DEBUG 0
+#endif
+
#ifdef CONFIG_MAC80211_TDLS_DEBUG
#define MAC80211_TDLS_DEBUG 1
#else
@@ -151,6 +157,10 @@ do { \
_sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
+#define mps_dbg(sdata, fmt, ...) \
+ _sdata_dbg(MAC80211_MESH_PS_DEBUG, \
+ sdata, fmt, ##__VA_ARGS__)
+
#define tdls_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_TDLS_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 466f4b45dd94..b0e32d628114 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -121,8 +121,8 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
- if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
- sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n");
+ if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
+ sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n");
if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
@@ -151,8 +151,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)
sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
- if (local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)
- sf += snprintf(buf + sf, mxln - sf, "SCAN_WHILE_IDLE\n");
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
kfree(buf);
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index cbde5cc49a40..059bbb82e84f 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -515,6 +515,9 @@ IEEE80211_IF_FILE(dot11MeshHWMProotInterval,
u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC);
IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC);
+IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC);
+IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
+ u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
#endif
#define DEBUGFS_ADD_MODE(name, mode) \
@@ -620,6 +623,8 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout);
MESHPARAMS_ADD(dot11MeshHWMProotInterval);
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
+ MESHPARAMS_ADD(power_mode);
+ MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
#undef MESHPARAMS_ADD
}
#endif
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 6fb1168b9f16..c7591f73dbc3 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -65,7 +65,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
@@ -74,7 +74,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
TEST(INSERTED), TEST(RATE_CONTROL),
- TEST(TOFFSET_KNOWN));
+ TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER),
+ TEST(MPSP_RECIPIENT));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 434b3c4f31b5..ee56d0779d8b 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -207,13 +207,16 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
{
might_sleep();
- WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
- BSS_CHANGED_BEACON_ENABLED) &&
- sdata->vif.type != NL80211_IFTYPE_AP &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT);
- WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE &&
- changed & ~BSS_CHANGED_IDLE);
+ if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED) &&
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
+ return;
+
+ if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
+ sdata->vif.type == NL80211_IFTYPE_MONITOR))
+ return;
check_sdata_in_driver(sdata);
@@ -528,6 +531,43 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
sta, dir);
}
+
+static inline
+void drv_add_interface_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ might_sleep();
+
+ check_sdata_in_driver(sdata);
+
+ if (!local->ops->add_interface_debugfs)
+ return;
+
+ local->ops->add_interface_debugfs(&local->hw, &sdata->vif,
+ sdata->debugfs.dir);
+}
+
+static inline
+void drv_remove_interface_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ might_sleep();
+
+ check_sdata_in_driver(sdata);
+
+ if (!local->ops->remove_interface_debugfs)
+ return;
+
+ local->ops->remove_interface_debugfs(&local->hw, &sdata->vif,
+ sdata->debugfs.dir);
+}
+#else
+static inline
+void drv_add_interface_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata) {}
+static inline
+void drv_remove_interface_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata) {}
#endif
static inline __must_check
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 61ac7c48ac0c..0db25d4bb223 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -37,6 +37,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
int i;
+ if (!ht_cap->ht_supported)
+ return;
+
if (sdata->vif.type != NL80211_IFTYPE_STATION) {
/* AP interfaces call this code when adding new stations,
* so just silently ignore non station interfaces.
@@ -89,22 +92,24 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
}
-void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
+bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
- struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_sta_ht_cap *ht_cap)
+ const struct ieee80211_ht_cap *ht_cap_ie,
+ struct sta_info *sta)
{
+ struct ieee80211_sta_ht_cap ht_cap;
u8 ampdu_info, tx_mcs_set_cap;
int i, max_tx_streams;
+ bool changed;
+ enum ieee80211_sta_rx_bandwidth bw;
+ enum ieee80211_smps_mode smps_mode;
- BUG_ON(!ht_cap);
-
- memset(ht_cap, 0, sizeof(*ht_cap));
+ memset(&ht_cap, 0, sizeof(ht_cap));
if (!ht_cap_ie || !sband->ht_cap.ht_supported)
- return;
+ goto apply;
- ht_cap->ht_supported = true;
+ ht_cap.ht_supported = true;
/*
* The bits listed in this expression should be
@@ -112,7 +117,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
* advertises more then we can't use those thus
* we mask them out.
*/
- ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) &
+ ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
(sband->ht_cap.cap |
~(IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
@@ -121,44 +126,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40));
- /* Unset 40 MHz if we're not using a 40 MHz channel */
- switch (sdata->vif.bss_conf.chandef.width) {
- case NL80211_CHAN_WIDTH_20_NOHT:
- case NL80211_CHAN_WIDTH_20:
- ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40;
- ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- break;
- case NL80211_CHAN_WIDTH_40:
- case NL80211_CHAN_WIDTH_80:
- case NL80211_CHAN_WIDTH_80P80:
- case NL80211_CHAN_WIDTH_160:
- break;
- }
-
/*
* The STBC bits are asymmetric -- if we don't have
* TX then mask out the peer's RX and vice versa.
*/
if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
- ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC;
+ ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
- ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
+ ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
ampdu_info = ht_cap_ie->ampdu_params_info;
- ht_cap->ampdu_factor =
+ ht_cap.ampdu_factor =
ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
- ht_cap->ampdu_density =
+ ht_cap.ampdu_density =
(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
/* own MCS TX capabilities */
tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
/* Copy peer MCS TX capabilities, the driver might need them. */
- ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params;
+ ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
/* can we TX with MCS rates? */
if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
- return;
+ goto apply;
/* Counting from 0, therefore +1 */
if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
@@ -176,25 +167,75 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
* - remainder are multiple spatial streams using unequal modulation
*/
for (i = 0; i < max_tx_streams; i++)
- ht_cap->mcs.rx_mask[i] =
+ ht_cap.mcs.rx_mask[i] =
sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
i < IEEE80211_HT_MCS_MASK_LEN; i++)
- ht_cap->mcs.rx_mask[i] =
+ ht_cap.mcs.rx_mask[i] =
sband->ht_cap.mcs.rx_mask[i] &
ht_cap_ie->mcs.rx_mask[i];
/* handle MCS rate 32 too */
if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
- ht_cap->mcs.rx_mask[32/8] |= 1;
+ ht_cap.mcs.rx_mask[32/8] |= 1;
+ apply:
/*
* If user has specified capability over-rides, take care
* of that here.
*/
- ieee80211_apply_htcap_overrides(sdata, ht_cap);
+ ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+ changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
+
+ memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
+
+ switch (sdata->vif.bss_conf.chandef.width) {
+ default:
+ WARN_ON_ONCE(1);
+ /* fall through */
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ bw = IEEE80211_STA_RX_BW_20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+ IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
+ break;
+ }
+
+ if (bw != sta->sta.bandwidth)
+ changed = true;
+ sta->sta.bandwidth = bw;
+
+ sta->cur_max_bandwidth =
+ ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+ IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
+
+ switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
+ >> IEEE80211_HT_CAP_SM_PS_SHIFT) {
+ case WLAN_HT_CAP_SM_PS_INVALID:
+ case WLAN_HT_CAP_SM_PS_STATIC:
+ smps_mode = IEEE80211_SMPS_STATIC;
+ break;
+ case WLAN_HT_CAP_SM_PS_DYNAMIC:
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
+ break;
+ case WLAN_HT_CAP_SM_PS_DISABLED:
+ smps_mode = IEEE80211_SMPS_OFF;
+ break;
+ }
+
+ if (smps_mode != sta->sta.smps_mode)
+ changed = true;
+ sta->sta.smps_mode = smps_mode;
+
+ return changed;
}
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
@@ -406,6 +447,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ if (sdata->u.mgd.driver_smps_mode == smps_mode)
+ return;
+
sdata->u.mgd.driver_smps_mode = smps_mode;
ieee80211_queue_work(&sdata->local->hw,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index b4b866f41919..40b71dfcc79d 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -228,7 +228,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
mgmt, skb->len, 0, GFP_KERNEL);
- cfg80211_put_bss(bss);
+ cfg80211_put_bss(local->hw.wiphy, bss);
netif_carrier_on(sdata->dev);
cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
}
@@ -242,6 +242,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u32 basic_rates;
int i, j;
u16 beacon_int = cbss->beacon_interval;
+ const struct cfg80211_bss_ies *ies;
+ u64 tsf;
lockdep_assert_held(&sdata->u.ibss.mtx);
@@ -265,13 +267,17 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
}
}
+ rcu_read_lock();
+ ies = rcu_dereference(cbss->ies);
+ tsf = ies->tsf;
+ rcu_read_unlock();
+
__ieee80211_sta_join_ibss(sdata, cbss->bssid,
beacon_int,
cbss->channel,
basic_rates,
cbss->capability,
- cbss->tsf,
- false);
+ tsf, false);
}
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
@@ -302,7 +308,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
"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, 0, NULL, 0,
- addr, sdata->u.ibss.bssid, NULL, 0, 0);
+ addr, sdata->u.ibss.bssid, NULL, 0, 0, 0);
}
return sta;
}
@@ -422,7 +428,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
* has actually implemented this.
*/
ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
- mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
+ mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -490,33 +496,26 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (sta && elems->ht_operation && elems->ht_cap_elem &&
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
/* we both use HT */
- struct ieee80211_sta_ht_cap sta_ht_cap_new;
+ struct ieee80211_ht_cap htcap_ie;
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,
- &sta_ht_cap_new);
+ memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie));
/*
* fall back to HT20 if we don't use or use
* the other extension channel
*/
- if (chandef.width != NL80211_CHAN_WIDTH_40 ||
- cfg80211_get_chandef_type(&chandef) !=
+ if (cfg80211_get_chandef_type(&chandef) !=
sdata->u.ibss.channel_type)
- sta_ht_cap_new.cap &=
- ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-
- if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new,
- sizeof(sta_ht_cap_new))) {
- memcpy(&sta->sta.ht_cap, &sta_ht_cap_new,
- sizeof(sta_ht_cap_new));
- rates_updated = true;
- }
+ htcap_ie.cap_info &=
+ cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+ rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(
+ sdata, sband, &htcap_ie, sta);
}
if (sta && rates_updated) {
@@ -535,8 +534,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
cbss = container_of((void *)bss, struct cfg80211_bss, priv);
- /* was just updated in ieee80211_bss_info_update */
- beacon_timestamp = cbss->tsf;
+ /* same for beacon and probe response */
+ beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp);
/* check if we need to merge IBSS */
@@ -1102,10 +1101,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&sdata->u.ibss.mtx);
- mutex_lock(&sdata->local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&sdata->local->mtx);
-
/*
* 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
* reserved, but an HT STA shall protect HT transmissions as though
@@ -1159,7 +1154,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
if (cbss) {
cfg80211_unlink_bss(local->hw.wiphy, cbss);
- cfg80211_put_bss(cbss);
+ cfg80211_put_bss(local->hw.wiphy, cbss);
}
}
@@ -1203,9 +1198,5 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&sdata->u.ibss.mtx);
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&local->mtx);
-
return 0;
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5fba867d9e2e..388580a1bada 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -86,23 +86,11 @@ struct ieee80211_fragment_entry {
struct ieee80211_bss {
- /* don't want to look up all the time */
- size_t ssid_len;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
-
- u32 device_ts;
+ u32 device_ts_beacon, device_ts_presp;
bool wmm_used;
bool uapsd_supported;
- unsigned long last_probe_resp;
-
-#ifdef CONFIG_MAC80211_MESH
- u8 *mesh_id;
- size_t mesh_id_len;
- u8 *mesh_cfg;
-#endif
-
#define IEEE80211_MAX_SUPP_RATES 32
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
size_t supp_rates_len;
@@ -153,31 +141,6 @@ enum ieee80211_bss_valid_data_flags {
IEEE80211_BSS_VALID_ERP = BIT(3)
};
-static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss)
-{
-#ifdef CONFIG_MAC80211_MESH
- return bss->mesh_cfg;
-#endif
- return NULL;
-}
-
-static inline u8 *bss_mesh_id(struct ieee80211_bss *bss)
-{
-#ifdef CONFIG_MAC80211_MESH
- return bss->mesh_id;
-#endif
- return NULL;
-}
-
-static inline u8 bss_mesh_id_len(struct ieee80211_bss *bss)
-{
-#ifdef CONFIG_MAC80211_MESH
- return bss->mesh_id_len;
-#endif
- return 0;
-}
-
-
typedef unsigned __bitwise__ ieee80211_tx_result;
#define TX_CONTINUE ((__force ieee80211_tx_result) 0u)
#define TX_DROP ((__force ieee80211_tx_result) 1u)
@@ -380,6 +343,7 @@ struct ieee80211_mgd_auth_data {
u8 key[WLAN_KEY_LEN_WEP104];
u8 key_len, key_idx;
bool done;
+ bool timeout_started;
u16 sae_trans, sae_status;
size_t data_len;
@@ -399,9 +363,9 @@ struct ieee80211_mgd_assoc_data {
u8 ssid_len;
u8 supp_rates_len;
bool wmm, uapsd;
- bool have_beacon;
- bool sent_assoc;
+ bool have_beacon, need_beacon;
bool synced;
+ bool timeout_started;
u8 ap_ht_param;
@@ -425,6 +389,7 @@ struct ieee80211_if_managed {
unsigned long probe_timeout;
int probe_send_count;
bool nullfunc_failed;
+ bool connection_loss;
struct mutex mtx;
struct cfg80211_bss *associated;
@@ -449,6 +414,10 @@ struct ieee80211_if_managed {
bool beacon_crc_valid;
u32 beacon_crc;
+ bool status_acked;
+ bool status_received;
+ __le16 status_fc;
+
enum {
IEEE80211_MFP_DISABLED,
IEEE80211_MFP_OPTIONAL,
@@ -611,6 +580,9 @@ struct ieee80211_if_mesh {
u32 mesh_seqnum;
bool accepting_plinks;
int num_gates;
+ struct beacon_data __rcu *beacon;
+ /* just protects beacon updates for now */
+ struct mutex mtx;
const u8 *ie;
u8 ie_len;
enum {
@@ -623,6 +595,11 @@ struct ieee80211_if_mesh {
s64 sync_offset_clockdrift_max;
spinlock_t sync_offset_lock;
bool adjusting_tbtt;
+ /* mesh power save */
+ enum nl80211_mesh_power_mode nonpeer_pm;
+ int ps_peers_light_sleep;
+ int ps_peers_deep_sleep;
+ struct ps_data ps;
};
#ifdef CONFIG_MAC80211_MESH
@@ -717,9 +694,6 @@ struct ieee80211_sub_if_data {
char name[IFNAMSIZ];
- /* to detect idle changes */
- bool old_idle;
-
/* Fragment table for host-based reassembly */
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next;
@@ -753,6 +727,9 @@ struct ieee80211_sub_if_data {
int user_power_level; /* in dBm */
int ap_power_level; /* in dBm */
+ bool radar_required;
+ struct delayed_work dfs_cac_timer_work;
+
/*
* AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for
@@ -840,6 +817,7 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+ IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
};
#ifdef CONFIG_MAC80211_LEDS
@@ -972,6 +950,10 @@ struct ieee80211_local {
/* wowlan is enabled -- don't reconfig on resume */
bool wowlan;
+ /* DFS/radar detection is enabled */
+ bool radar_detect_enabled;
+ struct work_struct radar_detected_work;
+
/* number of RX chains the hardware has */
u8 rx_chains;
@@ -986,14 +968,7 @@ struct ieee80211_local {
struct sk_buff_head skb_queue;
struct sk_buff_head skb_queue_unreliable;
- /*
- * Internal FIFO queue which is shared between multiple rx path
- * stages. Its main task is to provide a serialization mechanism,
- * so all rx handlers can enjoy having exclusive access to their
- * private data structures.
- */
- struct sk_buff_head rx_skb_queue;
- bool running_rx_handler; /* protected by rx_skb_queue.lock */
+ spinlock_t rx_path_lock;
/* Station data */
/*
@@ -1134,8 +1109,6 @@ struct ieee80211_local {
* this will override whatever chosen by mac80211 internally.
*/
int dynamic_ps_forced_timeout;
- int dynamic_ps_user_timeout;
- bool disable_dynamic_ps;
int user_power_level; /* in dBm, for all interfaces */
@@ -1193,40 +1166,41 @@ struct ieee80211_ra_tid {
/* Parsed Information Elements */
struct ieee802_11_elems {
- u8 *ie_start;
+ const u8 *ie_start;
size_t total_len;
/* pointers to IEs */
- u8 *ssid;
- u8 *supp_rates;
- u8 *fh_params;
- u8 *ds_params;
- u8 *cf_params;
- struct ieee80211_tim_ie *tim;
- u8 *ibss_params;
- u8 *challenge;
- u8 *wpa;
- u8 *rsn;
- u8 *erp_info;
- u8 *ext_supp_rates;
- u8 *wmm_info;
- 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;
- u8 *preq;
- u8 *prep;
- u8 *perr;
- struct ieee80211_rann_ie *rann;
- struct ieee80211_channel_sw_ie *ch_switch_ie;
- u8 *country_elem;
- u8 *pwr_constr_elem;
- u8 *quiet_elem; /* first quite element */
- u8 *timeout_int;
+ const u8 *ssid;
+ const u8 *supp_rates;
+ const u8 *fh_params;
+ const u8 *ds_params;
+ const u8 *cf_params;
+ const struct ieee80211_tim_ie *tim;
+ const u8 *ibss_params;
+ const u8 *challenge;
+ const u8 *rsn;
+ const u8 *erp_info;
+ const u8 *ext_supp_rates;
+ const u8 *wmm_info;
+ const u8 *wmm_param;
+ const struct ieee80211_ht_cap *ht_cap_elem;
+ const struct ieee80211_ht_operation *ht_operation;
+ const struct ieee80211_vht_cap *vht_cap_elem;
+ const struct ieee80211_vht_operation *vht_operation;
+ const struct ieee80211_meshconf_ie *mesh_config;
+ const u8 *mesh_id;
+ const u8 *peering;
+ const __le16 *awake_window;
+ const u8 *preq;
+ const u8 *prep;
+ const u8 *perr;
+ const struct ieee80211_rann_ie *rann;
+ const struct ieee80211_channel_sw_ie *ch_switch_ie;
+ const u8 *country_elem;
+ const u8 *pwr_constr_elem;
+ const u8 *quiet_elem; /* first quite element */
+ const u8 *timeout_int;
+ const u8 *opmode_notif;
/* length of them, respectively */
u8 ssid_len;
@@ -1237,7 +1211,6 @@ struct ieee802_11_elems {
u8 tim_len;
u8 ibss_params_len;
u8 challenge_len;
- u8 wpa_len;
u8 rsn_len;
u8 erp_info_len;
u8 ext_supp_rates_len;
@@ -1306,10 +1279,10 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata);
int ieee80211_max_network_latency(struct notifier_block *nb,
unsigned long data, void *dummy);
int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata);
-void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel_sw_ie *sw_elem,
- struct ieee80211_bss *bss,
- u64 timestamp);
+void
+ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_channel_sw_ie *sw_elem,
+ struct ieee80211_bss *bss, u64 timestamp);
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
@@ -1318,6 +1291,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
+ __le16 fc, bool acked);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -1413,10 +1388,10 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap);
-void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
+bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
- struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_sta_ht_cap *ht_cap);
+ const struct ieee80211_ht_cap *ht_cap_ie,
+ struct sta_info *sta);
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
const u8 *da, u16 tid,
u16 initiator, u16 reason_code);
@@ -1456,10 +1431,17 @@ 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);
+void
+ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_vht_cap *vht_cap_ie,
+ struct sta_info *sta);
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
+void ieee80211_sta_set_rx_nss(struct sta_info *sta);
+void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, u8 opmode,
+ enum ieee80211_band band, bool nss_only);
+
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -1577,8 +1559,9 @@ 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 status,
- u8 *extra, size_t extra_len, const u8 *bssid,
- const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
+ const u8 *extra, size_t extra_len, const u8 *bssid,
+ const u8 *da, const u8 *key, u8 key_len, u8 key_idx,
+ u32 tx_flags);
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, u16 stype, u16 reason,
bool send_frame, u8 *frame_buf);
@@ -1595,7 +1578,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
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,
+ u32 ratemask, bool directed, u32 tx_flags,
struct ieee80211_channel *channel, bool scan);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
@@ -1627,18 +1610,31 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
/* channel management */
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
- struct ieee80211_ht_operation *ht_oper,
+ const 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);
+int __must_check
+ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed);
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
+void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+ bool clear);
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx);
+void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx);
+
+void ieee80211_dfs_cac_timer(unsigned long data);
+void ieee80211_dfs_cac_timer_work(struct work_struct *work);
+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
+void ieee80211_dfs_radar_detected_work(struct work_struct *work);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 0a36dc6346bb..86c83084542a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -78,8 +78,7 @@ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
}
-static u32 ieee80211_idle_off(struct ieee80211_local *local,
- const char *reason)
+static u32 ieee80211_idle_off(struct ieee80211_local *local)
{
if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))
return 0;
@@ -99,110 +98,45 @@ static u32 ieee80211_idle_on(struct ieee80211_local *local)
return IEEE80211_CONF_CHANGE_IDLE;
}
-static u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
+void ieee80211_recalc_idle(struct ieee80211_local *local)
{
- struct ieee80211_sub_if_data *sdata;
- int count = 0;
- bool working = false, scanning = false;
+ bool working = false, scanning, active;
unsigned int led_trig_start = 0, led_trig_stop = 0;
struct ieee80211_roc_work *roc;
+ u32 change;
-#ifdef CONFIG_PROVE_LOCKING
- WARN_ON(debug_locks && !lockdep_rtnl_is_held() &&
- !lockdep_is_held(&local->iflist_mtx));
-#endif
lockdep_assert_held(&local->mtx);
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (!ieee80211_sdata_running(sdata)) {
- sdata->vif.bss_conf.idle = true;
- continue;
- }
-
- sdata->old_idle = sdata->vif.bss_conf.idle;
-
- /* do not count disabled managed interfaces */
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- !sdata->u.mgd.associated &&
- !sdata->u.mgd.auth_data &&
- !sdata->u.mgd.assoc_data) {
- sdata->vif.bss_conf.idle = true;
- continue;
- }
- /* do not count unused IBSS interfaces */
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- !sdata->u.ibss.ssid_len) {
- sdata->vif.bss_conf.idle = true;
- continue;
- }
-
- if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
- continue;
-
- /* count everything else */
- sdata->vif.bss_conf.idle = false;
- count++;
- }
+ active = !list_empty(&local->chanctx_list);
if (!local->ops->remain_on_channel) {
list_for_each_entry(roc, &local->roc_list, list) {
working = true;
- roc->sdata->vif.bss_conf.idle = false;
+ break;
}
}
- sdata = rcu_dereference_protected(local->scan_sdata,
- lockdep_is_held(&local->mtx));
- if (sdata && !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) {
- scanning = true;
- sdata->vif.bss_conf.idle = false;
- }
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
- continue;
- if (sdata->old_idle == sdata->vif.bss_conf.idle)
- continue;
- if (!ieee80211_sdata_running(sdata))
- continue;
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
- }
+ scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);
if (working || scanning)
led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK;
else
led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK;
- if (count)
+ if (active)
led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED;
else
led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED;
ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop);
- if (working)
- return ieee80211_idle_off(local, "working");
- if (scanning)
- return ieee80211_idle_off(local, "scanning");
- if (!count)
- return ieee80211_idle_on(local);
+ if (working || scanning || active)
+ change = ieee80211_idle_off(local);
else
- return ieee80211_idle_off(local, "in use");
-
- return 0;
-}
-
-void ieee80211_recalc_idle(struct ieee80211_local *local)
-{
- u32 chg;
-
- mutex_lock(&local->iflist_mtx);
- chg = __ieee80211_recalc_idle(local);
- mutex_unlock(&local->iflist_mtx);
- if (chg)
- ieee80211_hw_config(local, chg);
+ change = ieee80211_idle_on(local);
+ if (change)
+ ieee80211_hw_config(local, change);
}
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
@@ -621,6 +555,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
goto err_del_interface;
}
+ drv_add_interface_debugfs(local, sdata);
+
if (sdata->vif.type == NL80211_IFTYPE_AP) {
local->fif_pspoll++;
local->fif_probe_req++;
@@ -694,10 +630,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
if (sdata->flags & IEEE80211_SDATA_PROMISC)
atomic_inc(&local->iff_promiscs);
- mutex_lock(&local->mtx);
- hw_reconf_flags |= __ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
-
if (coming_up)
local->open_count++;
@@ -748,6 +680,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
int i, flushed;
+ struct ps_data *ps;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -817,6 +750,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&sdata->recalc_smps);
+ cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+
+ if (sdata->wdev.cac_started) {
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_vif_release_channel(sdata);
+ mutex_unlock(&local->iflist_mtx);
+ cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+ GFP_KERNEL);
+ }
+
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata;
@@ -826,6 +769,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
u.vlan.list)
dev_close(vlan->dev);
WARN_ON(!list_empty(&sdata->u.ap.vlans));
+ } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ /* remove all packets in parent bc_buf pointing to this dev */
+ ps = &sdata->bss->ps;
+
+ spin_lock_irqsave(&ps->bc_buf.lock, flags);
+ skb_queue_walk_safe(&ps->bc_buf, skb, tmp) {
+ if (skb->dev == sdata->dev) {
+ __skb_unlink(skb, &ps->bc_buf);
+ local->total_ps_buffered--;
+ ieee80211_free_txskb(&local->hw, skb);
+ }
+ }
+ spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata);
}
@@ -882,16 +838,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
*/
ieee80211_free_keys(sdata);
+ drv_remove_interface_debugfs(local, sdata);
+
if (going_down)
drv_remove_interface(local, sdata);
}
sdata->bss = NULL;
- mutex_lock(&local->mtx);
- hw_reconf_flags |= __ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
-
ieee80211_recalc_ps(local, -1);
if (local->open_count == 0) {
@@ -1583,6 +1537,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
spin_lock_init(&sdata->cleanup_stations_lock);
INIT_LIST_HEAD(&sdata->cleanup_stations);
INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
+ INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
+ ieee80211_dfs_cac_timer_work);
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
struct ieee80211_supported_band *sband;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 2bdd454e8bcf..f9747689d604 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -34,8 +34,6 @@
#include "cfg.h"
#include "debugfs.h"
-static struct lock_class_key ieee80211_rx_skb_queue_class;
-
void ieee80211_configure_filter(struct ieee80211_local *local)
{
u64 mc;
@@ -503,6 +501,11 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
},
};
+static const u8 extended_capabilities[] = {
+ 0, 0, 0, 0, 0, 0, 0,
+ WLAN_EXT_CAPA8_OPMODE_NOTIF,
+};
+
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
@@ -559,14 +562,17 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
WIPHY_FLAG_REPORTS_OBSS |
WIPHY_FLAG_OFFCHAN_TX;
+ wiphy->extended_capabilities = extended_capabilities;
+ wiphy->extended_capabilities_mask = extended_capabilities;
+ wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);
+
if (ops->remain_on_channel)
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_SAE |
NL80211_FEATURE_HT_IBSS |
- NL80211_FEATURE_VIF_TXPOWER |
- NL80211_FEATURE_FULL_AP_CLIENT_STATE;
+ NL80211_FEATURE_VIF_TXPOWER;
if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@@ -613,25 +619,19 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
mutex_init(&local->key_mtx);
spin_lock_init(&local->filter_lock);
+ spin_lock_init(&local->rx_path_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
- * context. Therefore, this one needs a different
- * locking class so our direct, non-irq-safe use of
- * the queue's lock doesn't throw lockdep warnings.
- */
- skb_queue_head_init_class(&local->rx_skb_queue,
- &ieee80211_rx_skb_queue_class);
-
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
INIT_WORK(&local->restart_work, ieee80211_restart_work);
+ INIT_WORK(&local->radar_detected_work,
+ ieee80211_dfs_radar_detected_work);
+
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
local->smps_mode = IEEE80211_SMPS_OFF;
@@ -707,9 +707,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
return -EINVAL;
#endif
- 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;
@@ -727,6 +724,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
*/
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
return -EINVAL;
+
+ /* DFS currently not supported with channel context drivers */
+ 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->radar_detect_widths)
+ return -EINVAL;
+ }
}
/* Only HW csum features are currently compatible with mac80211 */
@@ -1089,7 +1096,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable);
- skb_queue_purge(&local->rx_skb_queue);
destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 694e27376afa..a77d40ed4e61 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -149,6 +149,31 @@ u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
return changed;
}
+/*
+ * mesh_sta_cleanup - clean up any mesh sta state
+ *
+ * @sta: mesh sta to clean up.
+ */
+void mesh_sta_cleanup(struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u32 changed;
+
+ /*
+ * maybe userspace handles peer allocation and peering, but in either
+ * case the beacon is still generated by the kernel and we might need
+ * an update.
+ */
+ changed = mesh_accept_plinks_update(sdata);
+ if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
+ changed |= mesh_plink_deactivate(sta);
+ del_timer_sync(&sta->plink_timer);
+ }
+
+ if (changed)
+ ieee80211_mbss_info_change_notify(sdata, changed);
+}
+
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
{
int i;
@@ -261,6 +286,9 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
*pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
*pos |= ifmsh->accepting_plinks ?
IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
+ /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
+ *pos |= ifmsh->ps_peers_deep_sleep ?
+ IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
@@ -286,6 +314,29 @@ mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
return 0;
}
+int mesh_add_awake_window_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ u8 *pos;
+
+ /* see IEEE802.11-2012 13.14.6 */
+ if (ifmsh->ps_peers_light_sleep == 0 &&
+ ifmsh->ps_peers_deep_sleep == 0 &&
+ ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE)
+ return 0;
+
+ if (skb_tailroom(skb) < 4)
+ return -ENOMEM;
+
+ pos = skb_put(skb, 2 + 2);
+ *pos++ = WLAN_EID_MESH_AWAKE_WINDOW;
+ *pos++ = 2;
+ put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos);
+
+ return 0;
+}
+
int
mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
{
@@ -342,8 +393,6 @@ mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
int mesh_add_ds_params_ie(struct sk_buff *skb,
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;
u8 *pos;
@@ -360,13 +409,10 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
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);
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = ieee80211_frequency_to_channel(chan->center_freq);
- }
+ pos = skb_put(skb, 2 + 1);
+ *pos++ = WLAN_EID_DS_PARAMS;
+ *pos++ = 1;
+ *pos++ = ieee80211_frequency_to_channel(chan->center_freq);
return 0;
}
@@ -547,7 +593,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
mesh_path_expire(sdata);
changed = mesh_accept_plinks_update(sdata);
- ieee80211_bss_info_change_notify(sdata, changed);
+ ieee80211_mbss_info_change_notify(sdata, changed);
mod_timer(&ifmsh->housekeeping_timer,
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
@@ -598,7 +644,140 @@ void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
}
#endif
-void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
+static int
+ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
+{
+ struct beacon_data *bcn;
+ int head_len, tail_len;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_band band;
+ u8 *pos;
+ struct ieee80211_sub_if_data *sdata;
+ int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
+ sizeof(mgmt->u.beacon);
+
+ sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
+ head_len = hdr_len +
+ 2 + /* NULL SSID */
+ 2 + 8 + /* supported rates */
+ 2 + 3; /* DS params */
+ tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
+ 2 + sizeof(struct ieee80211_ht_cap) +
+ 2 + sizeof(struct ieee80211_ht_operation) +
+ 2 + ifmsh->mesh_id_len +
+ 2 + sizeof(struct ieee80211_meshconf_ie) +
+ 2 + sizeof(__le16) + /* awake window */
+ ifmsh->ie_len;
+
+ bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
+ /* need an skb for IE builders to operate on */
+ skb = dev_alloc_skb(max(head_len, tail_len));
+
+ if (!bcn || !skb)
+ goto out_free;
+
+ /*
+ * pointers go into the block we allocated,
+ * memory is | beacon_data | head | tail |
+ */
+ bcn->head = ((u8 *) bcn) + sizeof(*bcn);
+
+ /* fill in the head */
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
+ memset(mgmt, 0, hdr_len);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON);
+ eth_broadcast_addr(mgmt->da);
+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+ ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
+ mgmt->u.beacon.beacon_int =
+ cpu_to_le16(sdata->vif.bss_conf.beacon_int);
+ mgmt->u.beacon.capab_info |= cpu_to_le16(
+ sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
+
+ pos = skb_put(skb, 2);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = 0x0;
+
+ if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
+ mesh_add_ds_params_ie(skb, sdata))
+ goto out_free;
+
+ bcn->head_len = skb->len;
+ memcpy(bcn->head, skb->data, bcn->head_len);
+
+ /* now the tail */
+ skb_trim(skb, 0);
+ bcn->tail = bcn->head + bcn->head_len;
+
+ if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
+ mesh_add_rsn_ie(skb, sdata) ||
+ mesh_add_ht_cap_ie(skb, sdata) ||
+ mesh_add_ht_oper_ie(skb, sdata) ||
+ mesh_add_meshid_ie(skb, sdata) ||
+ mesh_add_meshconf_ie(skb, sdata) ||
+ mesh_add_awake_window_ie(skb, sdata) ||
+ mesh_add_vendor_ies(skb, sdata))
+ goto out_free;
+
+ bcn->tail_len = skb->len;
+ memcpy(bcn->tail, skb->data, bcn->tail_len);
+
+ dev_kfree_skb(skb);
+ rcu_assign_pointer(ifmsh->beacon, bcn);
+ return 0;
+out_free:
+ kfree(bcn);
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+}
+
+static int
+ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct beacon_data *old_bcn;
+ int ret;
+ sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh);
+
+ mutex_lock(&ifmsh->mtx);
+
+ old_bcn = rcu_dereference_protected(ifmsh->beacon,
+ lockdep_is_held(&ifmsh->mtx));
+ ret = ieee80211_mesh_build_beacon(ifmsh);
+ if (ret)
+ /* just reuse old beacon */
+ goto out;
+
+ if (old_bcn)
+ kfree_rcu(old_bcn, rcu_head);
+out:
+ mutex_unlock(&ifmsh->mtx);
+ return ret;
+}
+
+void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
+ u32 changed)
+{
+ if (sdata->vif.bss_conf.enable_beacon &&
+ (changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_HT |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_BEACON_INT)))
+ if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh))
+ return;
+ ieee80211_bss_info_change_notify(sdata, changed);
+}
+
+int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
@@ -629,20 +808,24 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(local, band);
- if (band == IEEE80211_BAND_5GHZ) {
- sdata->vif.bss_conf.use_short_slot = true;
- changed |= BSS_CHANGED_ERP_SLOT;
+ changed |= ieee80211_mps_local_status_update(sdata);
+
+ if (ieee80211_mesh_build_beacon(ifmsh)) {
+ ieee80211_stop_mesh(sdata);
+ return -ENOMEM;
}
ieee80211_bss_info_change_notify(sdata, changed);
netif_carrier_on(sdata->dev);
+ return 0;
}
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct beacon_data *bcn;
netif_carrier_off(sdata->dev);
@@ -651,11 +834,21 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.enable_beacon = false;
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
+ mutex_lock(&ifmsh->mtx);
+ bcn = rcu_dereference_protected(ifmsh->beacon,
+ lockdep_is_held(&ifmsh->mtx));
+ rcu_assign_pointer(ifmsh->beacon, NULL);
+ kfree_rcu(bcn, rcu_head);
+ mutex_unlock(&ifmsh->mtx);
/* flush STAs and mpaths on this iface */
sta_info_flush(sdata);
mesh_path_flush_by_iface(sdata);
+ /* free all potentially still buffered group-addressed frames */
+ local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
+ skb_queue_purge(&ifmsh->ps.bc_buf);
+
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_timer);
@@ -675,6 +868,63 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
sdata->u.mesh.timers_running = 0;
}
+static void
+ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct sk_buff *presp;
+ struct beacon_data *bcn;
+ struct ieee80211_mgmt *hdr;
+ struct ieee802_11_elems elems;
+ size_t baselen;
+ u8 *pos, *end;
+
+ end = ((u8 *) mgmt) + len;
+ pos = mgmt->u.probe_req.variable;
+ baselen = (u8 *) pos - (u8 *) mgmt;
+ if (baselen > len)
+ return;
+
+ ieee802_11_parse_elems(pos, len - baselen, &elems);
+
+ /* 802.11-2012 10.1.4.3.2 */
+ if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
+ !is_broadcast_ether_addr(mgmt->da)) ||
+ elems.ssid_len != 0)
+ return;
+
+ if (elems.mesh_id_len != 0 &&
+ (elems.mesh_id_len != ifmsh->mesh_id_len ||
+ memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len)))
+ return;
+
+ rcu_read_lock();
+ bcn = rcu_dereference(ifmsh->beacon);
+
+ if (!bcn)
+ goto out;
+
+ presp = dev_alloc_skb(local->tx_headroom +
+ bcn->head_len + bcn->tail_len);
+ if (!presp)
+ goto out;
+
+ skb_reserve(presp, local->tx_headroom);
+ memcpy(skb_put(presp, bcn->head_len), bcn->head, bcn->head_len);
+ memcpy(skb_put(presp, bcn->tail_len), bcn->tail, bcn->tail_len);
+ hdr = (struct ieee80211_mgmt *) presp->data;
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memcpy(hdr->da, mgmt->sa, ETH_ALEN);
+ mpl_dbg(sdata, "sending probe resp. to %pM\n", hdr->da);
+ IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, presp);
+out:
+ rcu_read_unlock();
+}
+
static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
u16 stype,
struct ieee80211_mgmt *mgmt,
@@ -764,6 +1014,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len,
rx_status);
break;
+ case IEEE80211_STYPE_PROBE_REQ:
+ ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len);
+ break;
case IEEE80211_STYPE_ACTION:
ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
break;
@@ -833,8 +1086,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
ieee80211_mesh_path_root_timer,
(unsigned long) sdata);
INIT_LIST_HEAD(&ifmsh->preq_queue.list);
+ skb_queue_head_init(&ifmsh->ps.bc_buf);
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
spin_lock_init(&ifmsh->sync_offset_lock);
+ RCU_INIT_POINTER(ifmsh->beacon, NULL);
+ mutex_init(&ifmsh->mtx);
sdata->vif.bss_conf.bssid = zero_addr;
}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index aff301544c7f..1a1da877b1d2 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -222,6 +222,8 @@ int mesh_add_meshid_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_rsn_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
+int mesh_add_awake_window_ie(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata);
int mesh_add_vendor_ies(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_ds_params_ie(struct sk_buff *skb,
@@ -237,10 +239,28 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
struct sta_info *sta, struct sk_buff *skb);
void ieee80211s_stop(void);
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
-void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
+int 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);
const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
+/* wrapper for ieee80211_bss_info_change_notify() */
+void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
+ u32 changed);
+
+/* mesh power save */
+u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata);
+u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
+ enum nl80211_mesh_power_mode pm);
+void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_hdr *hdr);
+void ieee80211_mps_sta_status_update(struct sta_info *sta);
+void ieee80211_mps_rx_h_sta_process(struct sta_info *sta,
+ struct ieee80211_hdr *hdr);
+void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
+ bool tx, bool acked);
+void ieee80211_mps_frame_release(struct sta_info *sta,
+ struct ieee802_11_elems *elems);
/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
@@ -248,8 +268,8 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
int mesh_nexthop_resolve(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata);
-struct mesh_path *mesh_path_lookup(u8 *dst,
- struct ieee80211_sub_if_data *sdata);
+struct mesh_path *mesh_path_lookup(const u8 *dst,
+ struct ieee80211_sub_if_data *sdata);
struct mesh_path *mpp_path_lookup(u8 *dst,
struct ieee80211_sub_if_data *sdata);
int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata);
@@ -259,7 +279,7 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len);
-int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata);
+int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata);
int mesh_path_add_gate(struct mesh_path *mpath);
int mesh_path_send_to_gates(struct mesh_path *mpath);
@@ -271,20 +291,22 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
void mesh_plink_broken(struct sta_info *sta);
-void mesh_plink_deactivate(struct sta_info *sta);
-int mesh_plink_open(struct sta_info *sta);
-void mesh_plink_block(struct sta_info *sta);
+u32 mesh_plink_deactivate(struct sta_info *sta);
+u32 mesh_plink_open(struct sta_info *sta);
+u32 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);
+void mesh_sta_cleanup(struct sta_info *sta);
/* Private interfaces */
/* Mesh tables */
void mesh_mpath_table_grow(void);
void mesh_mpp_table_grow(void);
/* Mesh paths */
-int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode,
- const u8 *ra, struct ieee80211_sub_if_data *sdata);
+int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn,
+ __le16 target_rcode, const u8 *ra,
+ struct ieee80211_sub_if_data *sdata);
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
void mesh_path_flush_pending(struct mesh_path *mpath);
void mesh_path_tx_pending(struct mesh_path *mpath);
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 6b4603a90031..585c1e26cca8 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -30,14 +30,14 @@
static void mesh_queue_preq(struct mesh_path *, u8);
-static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
+static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
{
if (ae)
offset += 6;
return get_unaligned_le32(preq_elem + offset);
}
-static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)
+static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae)
{
if (ae)
offset += 6;
@@ -102,10 +102,13 @@ enum mpath_frame_type {
static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
- u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target,
- __le32 target_sn, const u8 *da, u8 hop_count, u8 ttl,
- __le32 lifetime, __le32 metric, __le32 preq_id,
- struct ieee80211_sub_if_data *sdata)
+ const u8 *orig_addr, __le32 orig_sn,
+ u8 target_flags, const u8 *target,
+ __le32 target_sn, const u8 *da,
+ u8 hop_count, u8 ttl,
+ __le32 lifetime, __le32 metric,
+ __le32 preq_id,
+ struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -205,6 +208,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
skb_set_mac_header(skb, 0);
skb_set_network_header(skb, 0);
@@ -217,6 +221,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
info->control.vif = &sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
ieee80211_set_qos_hdr(sdata, skb);
+ ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
}
/**
@@ -233,7 +238,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
* also acquires in the TX path. To avoid a deadlock we don't transmit the
* frame directly but add it to the pending queue instead.
*/
-int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
+int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn,
__le16 target_rcode, const u8 *ra,
struct ieee80211_sub_if_data *sdata)
{
@@ -367,14 +372,14 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
* path routing information is updated.
*/
static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- u8 *hwmp_ie, enum mpath_frame_type action)
+ struct ieee80211_mgmt *mgmt,
+ const u8 *hwmp_ie, enum mpath_frame_type action)
{
struct ieee80211_local *local = sdata->local;
struct mesh_path *mpath;
struct sta_info *sta;
bool fresh_info;
- u8 *orig_addr, *ta;
+ const u8 *orig_addr, *ta;
u32 orig_sn, orig_metric;
unsigned long orig_lifetime, exp_time;
u32 last_hop_metric, new_metric;
@@ -509,11 +514,11 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
- u8 *preq_elem, u32 metric)
+ const u8 *preq_elem, u32 metric)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath = NULL;
- u8 *target_addr, *orig_addr;
+ const u8 *target_addr, *orig_addr;
const u8 *da;
u8 target_flags, ttl, flags;
u32 orig_sn, target_sn, lifetime, orig_metric;
@@ -646,11 +651,11 @@ next_hop_deref_protected(struct mesh_path *mpath)
static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
- u8 *prep_elem, u32 metric)
+ const u8 *prep_elem, u32 metric)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
- u8 *target_addr, *orig_addr;
+ const u8 *target_addr, *orig_addr;
u8 ttl, hopcount, flags;
u8 next_hop[ETH_ALEN];
u32 target_sn, orig_sn, lifetime;
@@ -709,12 +714,13 @@ fail:
}
static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, u8 *perr_elem)
+ struct ieee80211_mgmt *mgmt,
+ const u8 *perr_elem)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
u8 ttl;
- u8 *ta, *target_addr;
+ const u8 *ta, *target_addr;
u32 target_sn;
u16 target_rcode;
@@ -756,15 +762,15 @@ endperr:
}
static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- struct ieee80211_rann_ie *rann)
+ struct ieee80211_mgmt *mgmt,
+ const struct ieee80211_rann_ie *rann)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct mesh_path *mpath;
u8 ttl, flags, hopcount;
- u8 *orig_addr;
+ const u8 *orig_addr;
u32 orig_sn, metric, metric_txsta, interval;
bool root_is_gate;
@@ -1080,6 +1086,10 @@ int mesh_nexthop_resolve(struct sk_buff *skb,
u8 *target_addr = hdr->addr3;
int err = 0;
+ /* Nulls are only sent to peers for PS and should be pre-addressed */
+ if (ieee80211_is_qos_nullfunc(hdr->frame_control))
+ return 0;
+
rcu_read_lock();
err = mesh_nexthop_lookup(skb, sdata);
if (!err)
@@ -1151,6 +1161,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
if (next_hop) {
memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
+ ieee80211_mps_set_frame_flags(sdata, next_hop, hdr);
err = 0;
}
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index aa749818860e..2ce4c4023a97 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -181,7 +181,7 @@ errcopy:
return -ENOMEM;
}
-static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
+static u32 mesh_table_hash(const u8 *addr, struct ieee80211_sub_if_data *sdata,
struct mesh_table *tbl)
{
/* Use last four bytes of hw addr and interface index as hash index */
@@ -212,6 +212,7 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
hdr = (struct ieee80211_hdr *) skb->data;
memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN);
+ ieee80211_mps_set_frame_flags(sta->sdata, sta, hdr);
}
spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
@@ -325,8 +326,8 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,
}
-static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst,
- struct ieee80211_sub_if_data *sdata)
+static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst,
+ struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath;
struct hlist_node *n;
@@ -358,7 +359,8 @@ static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst,
*
* Locking: must be called within a read rcu section.
*/
-struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
+struct mesh_path *mesh_path_lookup(const u8 *dst,
+ struct ieee80211_sub_if_data *sdata)
{
return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata);
}
@@ -493,7 +495,7 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata)
*
* State: the initial state of the new path is set to 0
*/
-int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
+int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 81e612682bc3..f7526e509aa8 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -55,6 +55,66 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
sta->plink_retries = 0;
}
+/*
+ * mesh_set_short_slot_time - enable / disable ERP short slot time.
+ *
+ * The standard indirectly mandates mesh STAs to turn off short slot time by
+ * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we
+ * can't be sneaky about it. Enable short slot time if all mesh STAs in the
+ * MBSS support ERP rates.
+ *
+ * Returns BSS_CHANGED_ERP_SLOT or 0 for no change.
+ */
+static u32 mesh_set_short_slot_time(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 = local->hw.wiphy->bands[band];
+ struct sta_info *sta;
+ u32 erp_rates = 0, changed = 0;
+ int i;
+ bool short_slot = false;
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ /* (IEEE 802.11-2012 19.4.5) */
+ short_slot = true;
+ goto out;
+ } else if (band != IEEE80211_BAND_2GHZ ||
+ (band == IEEE80211_BAND_2GHZ &&
+ local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+ goto out;
+
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
+ erp_rates |= BIT(i);
+
+ if (!erp_rates)
+ goto out;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (sdata != sta->sdata ||
+ sta->plink_state != NL80211_PLINK_ESTAB)
+ continue;
+
+ short_slot = false;
+ if (erp_rates & sta->sta.supp_rates[band])
+ short_slot = true;
+ else
+ break;
+ }
+ rcu_read_unlock();
+
+out:
+ if (sdata->vif.bss_conf.use_short_slot != short_slot) {
+ sdata->vif.bss_conf.use_short_slot = short_slot;
+ changed = BSS_CHANGED_ERP_SLOT;
+ mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n",
+ sdata->vif.addr, short_slot);
+ }
+ return changed;
+}
+
/**
* mesh_set_ht_prot_mode - set correct HT protection mode
*
@@ -141,6 +201,9 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
+ ieee80211_mps_sta_status_update(sta);
+ changed |= ieee80211_mps_local_status_update(sdata);
+
return changed;
}
@@ -151,7 +214,7 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
*
* All mesh paths with this peer as next hop will be flushed
*/
-void mesh_plink_deactivate(struct sta_info *sta)
+u32 mesh_plink_deactivate(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed;
@@ -164,7 +227,7 @@ void mesh_plink_deactivate(struct sta_info *sta)
sta->reason);
spin_unlock_bh(&sta->lock);
- ieee80211_bss_info_change_notify(sdata, changed);
+ return changed;
}
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
@@ -310,8 +373,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
if (elems->ht_cap_elem &&
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);
+ elems->ht_cap_elem, sta);
else
memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
@@ -320,8 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
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->sta.bandwidth = IEEE80211_STA_RX_BW_20;
ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
elems->ht_operation, &chandef);
if (sta->ch_width != chandef.width)
@@ -431,6 +492,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct sta_info *sta;
+ u32 changed = 0;
sta = mesh_sta_info_get(sdata, hw_addr, elems);
if (!sta)
@@ -441,10 +503,12 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks &&
rssi_threshold_check(sta, sdata))
- mesh_plink_open(sta);
+ changed = mesh_plink_open(sta);
+ ieee80211_mps_frame_release(sta, elems);
out:
rcu_read_unlock();
+ ieee80211_mbss_info_change_notify(sdata, changed);
}
static void mesh_plink_timer(unsigned long data)
@@ -528,6 +592,13 @@ static void mesh_plink_timer(unsigned long data)
#ifdef CONFIG_PM
void mesh_plink_quiesce(struct sta_info *sta)
{
+ if (!ieee80211_vif_is_mesh(&sta->sdata->vif))
+ return;
+
+ /* no kernel mesh sta timers have been initialized */
+ if (sta->sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
+ return;
+
if (del_timer_sync(&sta->plink_timer))
sta->plink_timer_was_running = true;
}
@@ -550,13 +621,14 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
add_timer(&sta->plink_timer);
}
-int mesh_plink_open(struct sta_info *sta)
+u32 mesh_plink_open(struct sta_info *sta)
{
__le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u32 changed;
if (!test_sta_flag(sta, WLAN_STA_AUTH))
- return -EPERM;
+ return 0;
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
@@ -564,7 +636,7 @@ int mesh_plink_open(struct sta_info *sta)
if (sta->plink_state != NL80211_PLINK_LISTEN &&
sta->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->lock);
- return -EBUSY;
+ return 0;
}
sta->plink_state = NL80211_PLINK_OPN_SNT;
mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
@@ -573,13 +645,16 @@ int mesh_plink_open(struct sta_info *sta)
"Mesh plink: starting establishment with %pM\n",
sta->sta.addr);
- return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
+ /* set the non-peer mode to active during peering */
+ changed = ieee80211_mps_local_status_update(sdata);
+
+ mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
+ sta->sta.addr, llid, 0, 0);
+ return changed;
}
-void mesh_plink_block(struct sta_info *sta)
+u32 mesh_plink_block(struct sta_info *sta)
{
- struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed;
spin_lock_bh(&sta->lock);
@@ -587,7 +662,7 @@ void mesh_plink_block(struct sta_info *sta)
sta->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->lock);
- ieee80211_bss_info_change_notify(sdata, changed);
+ return changed;
}
@@ -806,6 +881,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->llid = llid;
mesh_plink_timer_set(sta,
mshcfg->dot11MeshRetryTimeout);
+
+ /* set the non-peer mode to active during peering */
+ changed |= ieee80211_mps_local_status_update(sdata);
+
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_OPEN,
@@ -896,8 +975,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
spin_unlock_bh(&sta->lock);
changed |= mesh_plink_inc_estab_count(sdata);
changed |= mesh_set_ht_prot_mode(sdata);
+ changed |= mesh_set_short_slot_time(sdata);
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
sta->sta.addr);
+ ieee80211_mps_sta_status_update(sta);
+ changed |= ieee80211_mps_set_sta_local_pm(sta,
+ mshcfg->power_mode);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -931,11 +1014,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
spin_unlock_bh(&sta->lock);
changed |= mesh_plink_inc_estab_count(sdata);
changed |= mesh_set_ht_prot_mode(sdata);
+ changed |= mesh_set_short_slot_time(sdata);
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
sta->sta.addr);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_CONFIRM,
sta->sta.addr, llid, plid, 0);
+ ieee80211_mps_sta_status_update(sta);
+ changed |= ieee80211_mps_set_sta_local_pm(sta,
+ mshcfg->power_mode);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -954,6 +1041,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
changed |= mesh_set_ht_prot_mode(sdata);
+ changed |= mesh_set_short_slot_time(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, llid, plid, reason);
break;
@@ -1002,5 +1090,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
rcu_read_unlock();
if (changed)
- ieee80211_bss_info_change_notify(sdata, changed);
+ ieee80211_mbss_info_change_notify(sdata, changed);
}
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
new file mode 100644
index 000000000000..3b7bfc01ee36
--- /dev/null
+++ b/net/mac80211/mesh_ps.c
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2012-2013, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
+ * Copyright 2012-2013, cozybit Inc.
+ *
+ * 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 "mesh.h"
+#include "wme.h"
+
+
+/* mesh PS management */
+
+/**
+ * mps_qos_null_get - create pre-addressed QoS Null frame for mesh powersave
+ */
+static struct sk_buff *mps_qos_null_get(struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hdr *nullfunc; /* use 4addr header */
+ struct sk_buff *skb;
+ int size = sizeof(*nullfunc);
+ __le16 fc;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2);
+ if (!skb)
+ return NULL;
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ nullfunc = (struct ieee80211_hdr *) skb_put(skb, size);
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
+ ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr,
+ sdata->vif.addr);
+ nullfunc->frame_control = fc;
+ nullfunc->duration_id = 0;
+ /* no address resolution for this frame -> set addr 1 immediately */
+ memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
+ memset(skb_put(skb, 2), 0, 2); /* append QoS control field */
+ ieee80211_mps_set_frame_flags(sdata, sta, nullfunc);
+
+ return skb;
+}
+
+/**
+ * mps_qos_null_tx - send a QoS Null to indicate link-specific power mode
+ */
+static void mps_qos_null_tx(struct sta_info *sta)
+{
+ struct sk_buff *skb;
+
+ skb = mps_qos_null_get(sta);
+ if (!skb)
+ return;
+
+ mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n",
+ sta->sta.addr);
+
+ /* don't unintentionally start a MPSP */
+ if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
+ u8 *qc = ieee80211_get_qos_ctl((void *) skb->data);
+
+ qc[0] |= IEEE80211_QOS_CTL_EOSP;
+ }
+
+ ieee80211_tx_skb(sta->sdata, skb);
+}
+
+/**
+ * ieee80211_mps_local_status_update - track status of local link-specific PMs
+ *
+ * @sdata: local mesh subif
+ *
+ * sets the non-peer power mode and triggers the driver PS (re-)configuration
+ * Return BSS_CHANGED_BEACON if a beacon update is necessary.
+ */
+u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct sta_info *sta;
+ bool peering = false;
+ int light_sleep_cnt = 0;
+ int deep_sleep_cnt = 0;
+ u32 changed = 0;
+ enum nl80211_mesh_power_mode nonpeer_pm;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+
+ switch (sta->plink_state) {
+ case NL80211_PLINK_OPN_SNT:
+ case NL80211_PLINK_OPN_RCVD:
+ case NL80211_PLINK_CNF_RCVD:
+ peering = true;
+ break;
+ case NL80211_PLINK_ESTAB:
+ if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
+ light_sleep_cnt++;
+ else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
+ deep_sleep_cnt++;
+ break;
+ default:
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ /*
+ * Set non-peer mode to active during peering/scanning/authentication
+ * (see IEEE802.11-2012 13.14.8.3). The non-peer mesh power mode is
+ * deep sleep if the local STA is in light or deep sleep towards at
+ * least one mesh peer (see 13.14.3.1). Otherwise, set it to the
+ * user-configured default value.
+ */
+ if (peering) {
+ mps_dbg(sdata, "setting non-peer PM to active for peering\n");
+ nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
+ } else if (light_sleep_cnt || deep_sleep_cnt) {
+ mps_dbg(sdata, "setting non-peer PM to deep sleep\n");
+ nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ } else {
+ mps_dbg(sdata, "setting non-peer PM to user value\n");
+ nonpeer_pm = ifmsh->mshcfg.power_mode;
+ }
+
+ /* need update if sleep counts move between 0 and non-zero */
+ if (ifmsh->nonpeer_pm != nonpeer_pm ||
+ !ifmsh->ps_peers_light_sleep != !light_sleep_cnt ||
+ !ifmsh->ps_peers_deep_sleep != !deep_sleep_cnt)
+ changed = BSS_CHANGED_BEACON;
+
+ ifmsh->nonpeer_pm = nonpeer_pm;
+ ifmsh->ps_peers_light_sleep = light_sleep_cnt;
+ ifmsh->ps_peers_deep_sleep = deep_sleep_cnt;
+
+ return changed;
+}
+
+/**
+ * ieee80211_mps_set_sta_local_pm - set local PM towards a mesh STA
+ *
+ * @sta: mesh STA
+ * @pm: the power mode to set
+ * Return BSS_CHANGED_BEACON if a beacon update is in order.
+ */
+u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
+ enum nl80211_mesh_power_mode pm)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
+ pm, sta->sta.addr);
+
+ sta->local_pm = pm;
+
+ /*
+ * announce peer-specific power mode transition
+ * (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
+ */
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ mps_qos_null_tx(sta);
+
+ return ieee80211_mps_local_status_update(sdata);
+}
+
+/**
+ * ieee80211_mps_set_frame_flags - set mesh PS flags in FC (and QoS Control)
+ *
+ * @sdata: local mesh subif
+ * @sta: mesh STA
+ * @hdr: 802.11 frame header
+ *
+ * see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11
+ *
+ * NOTE: sta must be given when an individually-addressed QoS frame header
+ * is handled, for group-addressed and management frames it is not used
+ */
+void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ enum nl80211_mesh_power_mode pm;
+ u8 *qc;
+
+ if (WARN_ON(is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control) &&
+ !sta))
+ return;
+
+ if (is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control) &&
+ sta->plink_state == NL80211_PLINK_ESTAB)
+ pm = sta->local_pm;
+ else
+ pm = sdata->u.mesh.nonpeer_pm;
+
+ if (pm == NL80211_MESH_POWER_ACTIVE)
+ hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM);
+ else
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return;
+
+ qc = ieee80211_get_qos_ctl(hdr);
+
+ if ((is_unicast_ether_addr(hdr->addr1) &&
+ pm == NL80211_MESH_POWER_DEEP_SLEEP) ||
+ (is_multicast_ether_addr(hdr->addr1) &&
+ sdata->u.mesh.ps_peers_deep_sleep > 0))
+ qc[1] |= (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8);
+ else
+ qc[1] &= ~(IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8);
+}
+
+/**
+ * ieee80211_mps_sta_status_update - update buffering status of neighbor STA
+ *
+ * @sta: mesh STA
+ *
+ * called after change of peering status or non-peer/peer-specific power mode
+ */
+void ieee80211_mps_sta_status_update(struct sta_info *sta)
+{
+ enum nl80211_mesh_power_mode pm;
+ bool do_buffer;
+
+ /*
+ * use peer-specific power mode if peering is established and the
+ * peer's power mode is known
+ */
+ if (sta->plink_state == NL80211_PLINK_ESTAB &&
+ sta->peer_pm != NL80211_MESH_POWER_UNKNOWN)
+ pm = sta->peer_pm;
+ else
+ pm = sta->nonpeer_pm;
+
+ do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
+
+ /* Don't let the same PS state be set twice */
+ if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
+ return;
+
+ if (do_buffer) {
+ set_sta_flag(sta, WLAN_STA_PS_STA);
+ atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps);
+ mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n",
+ sta->sta.addr);
+ } else {
+ ieee80211_sta_ps_deliver_wakeup(sta);
+ }
+
+ /* clear the MPSP flags for non-peers or active STA */
+ if (sta->plink_state != NL80211_PLINK_ESTAB) {
+ clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+ clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+ } else if (!do_buffer) {
+ clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+ }
+}
+
+static void mps_set_sta_peer_pm(struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ enum nl80211_mesh_power_mode pm;
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+
+ /*
+ * Test Power Management field of frame control (PW) and
+ * mesh power save level subfield of QoS control field (PSL)
+ *
+ * | PM | PSL| Mesh PM |
+ * +----+----+---------+
+ * | 0 |Rsrv| Active |
+ * | 1 | 0 | Light |
+ * | 1 | 1 | Deep |
+ */
+ if (ieee80211_has_pm(hdr->frame_control)) {
+ if (qc[1] & (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8))
+ pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ else
+ pm = NL80211_MESH_POWER_LIGHT_SLEEP;
+ } else {
+ pm = NL80211_MESH_POWER_ACTIVE;
+ }
+
+ if (sta->peer_pm == pm)
+ return;
+
+ mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
+ sta->sta.addr, pm);
+
+ sta->peer_pm = pm;
+
+ ieee80211_mps_sta_status_update(sta);
+}
+
+static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ enum nl80211_mesh_power_mode pm;
+
+ if (ieee80211_has_pm(hdr->frame_control))
+ pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ else
+ pm = NL80211_MESH_POWER_ACTIVE;
+
+ if (sta->nonpeer_pm == pm)
+ return;
+
+ mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
+ sta->sta.addr, pm);
+
+ sta->nonpeer_pm = pm;
+
+ ieee80211_mps_sta_status_update(sta);
+}
+
+/**
+ * ieee80211_mps_rx_h_sta_process - frame receive handler for mesh powersave
+ *
+ * @sta: STA info that transmitted the frame
+ * @hdr: IEEE 802.11 (QoS) Header
+ */
+void ieee80211_mps_rx_h_sta_process(struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ if (is_unicast_ether_addr(hdr->addr1) &&
+ ieee80211_is_data_qos(hdr->frame_control)) {
+ /*
+ * individually addressed QoS Data/Null frames contain
+ * peer link-specific PS mode towards the local STA
+ */
+ mps_set_sta_peer_pm(sta, hdr);
+
+ /* check for mesh Peer Service Period trigger frames */
+ ieee80211_mpsp_trigger_process(ieee80211_get_qos_ctl(hdr),
+ sta, false, false);
+ } else {
+ /*
+ * can only determine non-peer PS mode
+ * (see IEEE802.11-2012 8.2.4.1.7)
+ */
+ mps_set_sta_nonpeer_pm(sta, hdr);
+ }
+}
+
+
+/* mesh PS frame release */
+
+static void mpsp_trigger_send(struct sta_info *sta, bool rspi, bool eosp)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct sk_buff *skb;
+ struct ieee80211_hdr *nullfunc;
+ struct ieee80211_tx_info *info;
+ u8 *qc;
+
+ skb = mps_qos_null_get(sta);
+ if (!skb)
+ return;
+
+ nullfunc = (struct ieee80211_hdr *) skb->data;
+ if (!eosp)
+ nullfunc->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ /*
+ * | RSPI | EOSP | MPSP triggering |
+ * +------+------+--------------------+
+ * | 0 | 0 | local STA is owner |
+ * | 0 | 1 | no MPSP (MPSP end) |
+ * | 1 | 0 | both STA are owner |
+ * | 1 | 1 | peer STA is owner | see IEEE802.11-2012 13.14.9.2
+ */
+ qc = ieee80211_get_qos_ctl(nullfunc);
+ if (rspi)
+ qc[1] |= (IEEE80211_QOS_CTL_RSPI >> 8);
+ if (eosp)
+ qc[0] |= IEEE80211_QOS_CTL_EOSP;
+
+ info = IEEE80211_SKB_CB(skb);
+
+ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+ mps_dbg(sdata, "sending MPSP trigger%s%s to %pM\n",
+ rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr);
+
+ ieee80211_tx_skb(sdata, skb);
+}
+
+/**
+ * mpsp_qos_null_append - append QoS Null frame to MPSP skb queue if needed
+ *
+ * To properly end a mesh MPSP the last transmitted frame has to set the EOSP
+ * flag in the QoS Control field. In case the current tailing frame is not a
+ * QoS Data frame, append a QoS Null to carry the flag.
+ */
+static void mpsp_qos_null_append(struct sta_info *sta,
+ struct sk_buff_head *frames)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct sk_buff *new_skb, *skb = skb_peek_tail(frames);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ return;
+
+ new_skb = mps_qos_null_get(sta);
+ if (!new_skb)
+ return;
+
+ mps_dbg(sdata, "appending QoS Null in MPSP towards %pM\n",
+ sta->sta.addr);
+ /*
+ * This frame has to be transmitted last. Assign lowest priority to
+ * make sure it cannot pass other frames when releasing multiple ACs.
+ */
+ new_skb->priority = 1;
+ skb_set_queue_mapping(new_skb, IEEE80211_AC_BK);
+ ieee80211_set_qos_hdr(sdata, new_skb);
+
+ info = IEEE80211_SKB_CB(new_skb);
+ info->control.vif = &sdata->vif;
+ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+
+ __skb_queue_tail(frames, new_skb);
+}
+
+/**
+ * mps_frame_deliver - transmit frames during mesh powersave
+ *
+ * @sta: STA info to transmit to
+ * @n_frames: number of frames to transmit. -1 for all
+ */
+static void mps_frame_deliver(struct sta_info *sta, int n_frames)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ int ac;
+ struct sk_buff_head frames;
+ struct sk_buff *skb;
+ bool more_data = false;
+
+ skb_queue_head_init(&frames);
+
+ /* collect frame(s) from buffers */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ while (n_frames != 0) {
+ skb = skb_dequeue(&sta->tx_filtered[ac]);
+ if (!skb) {
+ skb = skb_dequeue(
+ &sta->ps_tx_buf[ac]);
+ if (skb)
+ local->total_ps_buffered--;
+ }
+ if (!skb)
+ break;
+ n_frames--;
+ __skb_queue_tail(&frames, skb);
+ }
+
+ if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+ !skb_queue_empty(&sta->ps_tx_buf[ac]))
+ more_data = true;
+ }
+
+ /* nothing to send? -> EOSP */
+ if (skb_queue_empty(&frames)) {
+ mpsp_trigger_send(sta, false, true);
+ return;
+ }
+
+ /* in a MPSP make sure the last skb is a QoS Data frame */
+ if (test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
+ mpsp_qos_null_append(sta, &frames);
+
+ mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n",
+ skb_queue_len(&frames), sta->sta.addr);
+
+ /* prepare collected frames for transmission */
+ skb_queue_walk(&frames, skb) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *) skb->data;
+
+ /*
+ * Tell TX path to send this frame even though the
+ * STA may still remain is PS mode after this frame
+ * exchange.
+ */
+ info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+
+ if (more_data || !skb_queue_is_last(&frames, skb))
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ else
+ hdr->frame_control &=
+ cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+
+ if (skb_queue_is_last(&frames, skb) &&
+ ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qoshdr = ieee80211_get_qos_ctl(hdr);
+
+ /* MPSP trigger frame ends service period */
+ *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ }
+ }
+
+ ieee80211_add_pending_skbs(local, &frames);
+ sta_info_recalc_tim(sta);
+}
+
+/**
+ * ieee80211_mpsp_trigger_process - track status of mesh Peer Service Periods
+ *
+ * @qc: QoS Control field
+ * @sta: peer to start a MPSP with
+ * @tx: frame was transmitted by the local STA
+ * @acked: frame has been transmitted successfully
+ *
+ * NOTE: active mode STA may only serve as MPSP owner
+ */
+void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
+ bool tx, bool acked)
+{
+ u8 rspi = qc[1] & (IEEE80211_QOS_CTL_RSPI >> 8);
+ u8 eosp = qc[0] & IEEE80211_QOS_CTL_EOSP;
+
+ if (tx) {
+ if (rspi && acked)
+ set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+
+ if (eosp)
+ clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
+ else if (acked &&
+ test_sta_flag(sta, WLAN_STA_PS_STA) &&
+ !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
+ mps_frame_deliver(sta, -1);
+ } else {
+ if (eosp)
+ clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+ else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE)
+ set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
+
+ if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
+ mps_frame_deliver(sta, -1);
+ }
+}
+
+/**
+ * ieee80211_mps_frame_release - release buffered frames in response to beacon
+ *
+ * @sta: mesh STA
+ * @elems: beacon IEs
+ *
+ * For peers if we have individually-addressed frames buffered or the peer
+ * indicates buffered frames, send a corresponding MPSP trigger frame. Since
+ * we do not evaluate the awake window duration, QoS Nulls are used as MPSP
+ * trigger frames. If the neighbour STA is not a peer, only send single frames.
+ */
+void ieee80211_mps_frame_release(struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ int ac, buffer_local = 0;
+ bool has_buffered = false;
+
+ /* TIM map only for LLID <= IEEE80211_MAX_AID */
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
+ le16_to_cpu(sta->llid) % IEEE80211_MAX_AID);
+
+ if (has_buffered)
+ mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
+ sta->sta.addr);
+
+ /* only transmit to PS STA with announced, non-zero awake window */
+ if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+ (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
+ return;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
+ skb_queue_len(&sta->tx_filtered[ac]);
+
+ if (!has_buffered && !buffer_local)
+ return;
+
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ mpsp_trigger_send(sta, has_buffered, !buffer_local);
+ else
+ mps_frame_deliver(sta, 1);
+}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 72924399077e..9f6464f3e05f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -30,11 +30,13 @@
#include "rate.h"
#include "led.h"
-#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
-#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
-#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
+#define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
+#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
+#define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10)
+#define IEEE80211_ASSOC_MAX_TRIES 3
static int max_nullfunc_tries = 2;
module_param(max_nullfunc_tries, int, 0644);
@@ -112,6 +114,9 @@ enum rx_mgmt_action {
/* caller must call cfg80211_send_assoc_timeout() */
RX_MGMT_CFG80211_ASSOC_TIMEOUT,
+
+ /* used when a processed beacon causes a deauth */
+ RX_MGMT_CFG80211_TX_DEAUTH,
};
/* utils */
@@ -172,79 +177,331 @@ static int ecw2cw(int ecw)
return (1 << ecw) - 1;
}
-static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_ht_operation *ht_oper,
- const u8 *bssid, bool reconfig)
+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, bool verbose)
+{
+ 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.
+ */
+ if (verbose)
+ 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:
+ if (verbose)
+ 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)) {
+ if (verbose)
+ 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)) {
+ if (verbose)
+ 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;
+
+out:
+ /* don't print the message below for VHT mismatch if VHT is disabled */
+ if (ret & IEEE80211_STA_DISABLE_VHT)
+ vht_chandef = *chandef;
+
+ 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 && verbose)
+ sdata_info(sdata,
+ "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n");
+
+ WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
+ return ret;
+}
+
+static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ const struct ieee80211_ht_operation *ht_oper,
+ const struct ieee80211_vht_operation *vht_oper,
+ const u8 *bssid, u32 *changed)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_supported_band *sband;
- struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
- struct sta_info *sta;
- u32 changed = 0;
+ struct cfg80211_chan_def chandef;
u16 ht_opmode;
- bool disable_40 = false;
+ u32 flags;
+ enum ieee80211_sta_rx_bandwidth new_sta_bw;
+ int ret;
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (WARN_ON(!chanctx_conf)) {
- rcu_read_unlock();
+ /* if HT was/is disabled, don't track any bandwidth changes */
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || !ht_oper)
return 0;
- }
- chan = chanctx_conf->def.chan;
- rcu_read_unlock();
+
+ /* don't check VHT if we associated as non-VHT station */
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
+ vht_oper = NULL;
+
+ if (WARN_ON_ONCE(!sta))
+ return -EINVAL;
+
+ chan = sdata->vif.bss_conf.chandef.chan;
sband = local->hw.wiphy->bands[chan->band];
- switch (sdata->vif.bss_conf.chandef.width) {
+ /* calculate new channel (type) based on HT/VHT operation IEs */
+ flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper,
+ vht_oper, &chandef, false);
+
+ /*
+ * Downgrade the new channel if we associated with restricted
+ * capabilities. For example, if we associated as a 20 MHz STA
+ * to a 40 MHz AP (due to regulatory, capabilities or config
+ * reasons) then switching to a 40 MHz channel now won't do us
+ * any good -- we couldn't use it with the AP.
+ */
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
+ chandef.width == NL80211_CHAN_WIDTH_80P80)
+ flags |= chandef_downgrade(&chandef);
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
+ chandef.width == NL80211_CHAN_WIDTH_160)
+ flags |= chandef_downgrade(&chandef);
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
+ chandef.width > NL80211_CHAN_WIDTH_20)
+ flags |= chandef_downgrade(&chandef);
+
+ if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
+ return 0;
+
+ sdata_info(sdata,
+ "AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n",
+ ifmgd->bssid, chandef.chan->center_freq, chandef.width,
+ chandef.center_freq1, chandef.center_freq2);
+
+ if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
+ IEEE80211_STA_DISABLE_VHT |
+ IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ)) ||
+ !cfg80211_chandef_valid(&chandef)) {
+ sdata_info(sdata,
+ "AP %pM changed bandwidth in a way we can't support - disconnect\n",
+ ifmgd->bssid);
+ return -EINVAL;
+ }
+
+ switch (chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ new_sta_bw = IEEE80211_STA_RX_BW_20;
+ break;
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_HT40MINUS)
- disable_40 = true;
- 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;
+ new_sta_bw = IEEE80211_STA_RX_BW_40;
break;
- default:
+ case NL80211_CHAN_WIDTH_80:
+ new_sta_bw = IEEE80211_STA_RX_BW_80;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ new_sta_bw = IEEE80211_STA_RX_BW_160;
break;
+ default:
+ return -EINVAL;
}
- /* This can change during the lifetime of the BSS */
- if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
- disable_40 = true;
-
- mutex_lock(&local->sta_mtx);
- sta = sta_info_get(sdata, bssid);
-
- WARN_ON_ONCE(!sta);
-
- if (sta && !sta->supports_40mhz)
- disable_40 = true;
+ if (new_sta_bw > sta->cur_max_bandwidth)
+ new_sta_bw = sta->cur_max_bandwidth;
- if (sta && (!reconfig ||
- (disable_40 != !(sta->sta.ht_cap.cap &
- IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) {
+ if (new_sta_bw < sta->sta.bandwidth) {
+ sta->sta.bandwidth = new_sta_bw;
+ rate_control_rate_update(local, sband, sta,
+ IEEE80211_RC_BW_CHANGED);
+ }
- if (disable_40)
- sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- else
- sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed);
+ if (ret) {
+ sdata_info(sdata,
+ "AP %pM changed bandwidth to incompatible one - disconnect\n",
+ ifmgd->bssid);
+ return ret;
+ }
+ if (new_sta_bw > sta->sta.bandwidth) {
+ sta->sta.bandwidth = new_sta_bw;
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_BW_CHANGED);
}
- mutex_unlock(&local->sta_mtx);
ht_opmode = le16_to_cpu(ht_oper->operation_mode);
/* if bss configuration changed store the new one */
- if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) {
- changed |= BSS_CHANGED_HT;
+ if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
+ *changed |= BSS_CHANGED_HT;
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
}
- return changed;
+ return 0;
}
/* frame sending functions */
@@ -644,6 +901,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
drv_mgd_prepare_tx(local, sdata);
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_MLME_CONN_TX;
ieee80211_tx_skb(sdata, skb);
}
@@ -680,7 +940,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
if (powersave)
nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL))
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
@@ -784,10 +1045,10 @@ static void ieee80211_chswitch_timer(unsigned long data)
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
-void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel_sw_ie *sw_elem,
- struct ieee80211_bss *bss,
- u64 timestamp)
+void
+ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_channel_sw_ie *sw_elem,
+ struct ieee80211_bss *bss, u64 timestamp)
{
struct cfg80211_bss *cbss =
container_of((void *)bss, struct cfg80211_bss, priv);
@@ -946,39 +1207,6 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
-{
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local->hw.conf;
-
- WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
- !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) ||
- (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS));
-
- local->disable_dynamic_ps = false;
- conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout;
-}
-EXPORT_SYMBOL(ieee80211_enable_dyn_ps);
-
-void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif)
-{
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local->hw.conf;
-
- WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
- !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) ||
- (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS));
-
- local->disable_dynamic_ps = true;
- conf->dynamic_ps_timeout = 0;
- del_timer_sync(&local->dynamic_ps_timer);
- ieee80211_queue_work(&local->hw,
- &local->dynamic_ps_enable_work);
-}
-EXPORT_SYMBOL(ieee80211_disable_dyn_ps);
-
/* powersave */
static void ieee80211_enable_ps(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
@@ -1081,7 +1309,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
}
if (count == 1 && ieee80211_powersave_allowed(found)) {
- struct ieee80211_conf *conf = &local->hw.conf;
s32 beaconint_us;
if (latency < 0)
@@ -1105,10 +1332,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
else
timeout = 100;
}
- local->dynamic_ps_user_timeout = timeout;
- if (!local->disable_dynamic_ps)
- conf->dynamic_ps_timeout =
- local->dynamic_ps_user_timeout;
+ local->hw.conf.dynamic_ps_timeout = timeout;
if (beaconint_us > latency) {
local->ps_sdata = NULL;
@@ -1178,8 +1402,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
- if (!local->disable_dynamic_ps &&
- local->hw.conf.dynamic_ps_timeout > 0) {
+ if (local->hw.conf.dynamic_ps_timeout > 0) {
/* don't enter PS if TX frames are pending */
if (drv_tx_frames_pending(local)) {
mod_timer(&local->dynamic_ps_timer, jiffies +
@@ -1244,16 +1467,30 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
}
+void ieee80211_dfs_cac_timer_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work =
+ container_of(work, struct delayed_work, work);
+ struct ieee80211_sub_if_data *sdata =
+ container_of(delayed_work, struct ieee80211_sub_if_data,
+ dfs_cac_timer_work);
+
+ ieee80211_vif_release_channel(sdata);
+
+ cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+}
+
/* MLME */
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- u8 *wmm_param, size_t wmm_param_len)
+ const u8 *wmm_param, size_t wmm_param_len)
{
struct ieee80211_tx_queue_params params;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
size_t left;
int count;
- u8 *pos, uapsd_queues = 0;
+ const u8 *pos;
+ u8 uapsd_queues = 0;
if (!local->ops->conf_tx)
return false;
@@ -1445,7 +1682,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_led_assoc(local, 1);
- if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+ if (sdata->u.mgd.assoc_data->have_beacon) {
/*
* If the AP is buggy we may get here with no DTIM period
* known, so assume it's 1 which is the only safe assumption
@@ -1453,6 +1690,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
* probably just won't work at all.
*/
bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
+ bss_info_changed |= BSS_CHANGED_DTIM_PERIOD;
} else {
bss_conf->dtim_period = 0;
}
@@ -1655,17 +1893,18 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_is_data(hdr->frame_control))
return;
- if (ack)
- ieee80211_sta_reset_conn_monitor(sdata);
-
if (ieee80211_is_nullfunc(hdr->frame_control) &&
sdata->u.mgd.probe_send_count > 0) {
if (ack)
- sdata->u.mgd.probe_send_count = 0;
+ ieee80211_sta_reset_conn_monitor(sdata);
else
sdata->u.mgd.nullfunc_failed = true;
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+ return;
}
+
+ if (ack)
+ ieee80211_sta_reset_conn_monitor(sdata);
}
static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
@@ -1706,7 +1945,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ssid_len = ssid[1];
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
- 0, (u32) -1, true, false,
+ 0, (u32) -1, true, 0,
ifmgd->associated->channel, false);
rcu_read_unlock();
}
@@ -1740,7 +1979,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
if (beacon)
mlme_dbg_ratelimited(sdata,
- "detected beacon loss from AP - sending probe request\n");
+ "detected beacon loss from AP - probing\n");
ieee80211_cqm_rssi_notify(&sdata->vif,
NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL);
@@ -1821,11 +2060,9 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
-static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
- bool transmit_frame)
+static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
mutex_lock(&ifmgd->mtx);
@@ -1836,8 +2073,10 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
- transmit_frame, frame_buf);
+ true, frame_buf);
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
mutex_unlock(&ifmgd->mtx);
/*
@@ -1845,10 +2084,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
* but that's not a problem.
*/
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
-
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
}
static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@ -1867,10 +2102,10 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
rcu_read_unlock();
}
- if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
+ if (ifmgd->connection_loss) {
sdata_info(sdata, "Connection to AP %pM lost\n",
ifmgd->bssid);
- __ieee80211_disconnect(sdata, false);
+ __ieee80211_disconnect(sdata);
} else {
ieee80211_mgd_probe_ap(sdata, true);
}
@@ -1882,9 +2117,7 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data,
u.mgd.csa_connection_drop_work);
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- __ieee80211_disconnect(sdata, true);
+ __ieee80211_disconnect(sdata);
}
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@@ -1895,6 +2128,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
trace_api_beacon_loss(sdata);
WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
+ sdata->u.mgd.connection_loss = false;
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_beacon_loss);
@@ -1906,7 +2140,7 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif)
trace_api_connection_loss(sdata);
- WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
+ sdata->u.mgd.connection_loss = true;
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_connection_loss);
@@ -1928,7 +2162,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
ieee80211_vif_release_channel(sdata);
}
- cfg80211_put_bss(auth_data->bss);
+ cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
kfree(auth_data);
sdata->u.mgd.auth_data = NULL;
}
@@ -1936,9 +2170,11 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len)
{
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
u8 *pos;
struct ieee802_11_elems elems;
+ u32 tx_flags = 0;
pos = mgmt->u.auth.variable;
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
@@ -1946,11 +2182,14 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
return;
auth_data->expected_transaction = 4;
drv_mgd_prepare_tx(sdata->local, sdata);
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_MLME_CONN_TX;
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,
- auth_data->key_idx);
+ auth_data->key_idx, tx_flags);
}
static enum rx_mgmt_action __must_check
@@ -2017,6 +2256,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata, "authenticated\n");
ifmgd->auth_data->done = true;
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
+ ifmgd->auth_data->timeout_started = true;
run_again(ifmgd, ifmgd->auth_data->timeout);
if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
@@ -2075,10 +2315,6 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- mutex_lock(&sdata->local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&sdata->local->mtx);
-
return RX_MGMT_CFG80211_DEAUTH;
}
@@ -2106,10 +2342,6 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- mutex_lock(&sdata->local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&sdata->local->mtx);
-
return RX_MGMT_CFG80211_DISASSOC;
}
@@ -2219,6 +2451,24 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ifmgd->aid = aid;
+ /*
+ * We previously checked these in the beacon/probe response, so
+ * they should be present here. This is just a safety net.
+ */
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ (!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) {
+ sdata_info(sdata,
+ "HT AP is missing WMM params or HT capability/operation in AssocResp\n");
+ return false;
+ }
+
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+ (!elems.vht_cap_elem || !elems.vht_operation)) {
+ sdata_info(sdata,
+ "VHT AP is missing VHT capability/operation in AssocResp\n");
+ return false;
+ }
+
mutex_lock(&sdata->local->sta_mtx);
/*
* station info was already allocated and inserted before
@@ -2232,17 +2482,36 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+ /* Set up internal HT/VHT capabilities */
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;
+ elems.ht_cap_elem, sta);
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);
+ elems.vht_cap_elem, sta);
+
+ /*
+ * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
+ * in their association response, so ignore that data for our own
+ * configuration. If it changed since the last beacon, we'll get the
+ * next beacon and update then.
+ */
+
+ /*
+ * If an operating mode notification IE is present, override the
+ * NSS calculation (that would be done in rate_control_rate_init())
+ * and use the # of streams from that element.
+ */
+ if (elems.opmode_notif &&
+ !(*elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
+ u8 nss;
+
+ nss = *elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
+ nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
+ nss += 1;
+ sta->sta.rx_nss = nss;
+ }
rate_control_rate_init(sta);
@@ -2252,9 +2521,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
if (elems.wmm_param)
set_sta_flag(sta, WLAN_STA_WME);
- err = sta_info_move_state(sta, IEEE80211_STA_AUTH);
- if (!err)
- err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
if (err) {
@@ -2283,11 +2550,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_set_wmm_default(sdata, false);
changed |= BSS_CHANGED_QOS;
- if (elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
- changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
- cbss->bssid, false);
-
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
@@ -2361,6 +2623,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
"%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
mgmt->sa, tu, ms);
assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
+ assoc_data->timeout_started = true;
if (ms > IEEE80211_ASSOC_TIMEOUT)
run_again(ifmgd, assoc_data->timeout);
return RX_MGMT_NONE;
@@ -2376,7 +2639,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
ieee80211_destroy_assoc_data(sdata, false);
- cfg80211_put_bss(*bss);
+ cfg80211_put_bss(sdata->local->hw.wiphy, *bss);
return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
}
sdata_info(sdata, "associated\n");
@@ -2412,7 +2675,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
if (elems->tim && !elems->parse_error) {
- struct ieee80211_tim_ie *tim_ie = elems->tim;
+ const struct ieee80211_tim_ie *tim_ie = elems->tim;
sdata->u.mgd.dtim_period = tim_ie->dtim_period;
}
}
@@ -2484,6 +2747,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata, "direct probe responded\n");
ifmgd->auth_data->tries = 0;
ifmgd->auth_data->timeout = jiffies;
+ ifmgd->auth_data->timeout_started = true;
run_again(ifmgd, ifmgd->auth_data->timeout);
}
}
@@ -2509,10 +2773,10 @@ static const u64 care_about_ies =
(1ULL << WLAN_EID_HT_CAPABILITY) |
(1ULL << WLAN_EID_HT_OPERATION);
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+static enum rx_mgmt_action
+ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ u8 *deauth_buf, struct ieee80211_rx_status *rx_status)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
@@ -2521,6 +2785,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
+ struct sta_info *sta;
u32 changed = 0;
bool erp_valid;
u8 erp_value = 0;
@@ -2532,39 +2797,51 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
if (baselen > len)
- return;
+ return RX_MGMT_NONE;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) {
rcu_read_unlock();
- return;
+ return RX_MGMT_NONE;
}
if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
rcu_read_unlock();
- return;
+ return RX_MGMT_NONE;
}
chan = chanctx_conf->def.chan;
rcu_read_unlock();
- if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
+ if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
ieee802_11_parse_elems(mgmt->u.beacon.variable,
len - baselen, &elems);
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ifmgd->assoc_data->have_beacon = true;
- ifmgd->assoc_data->sent_assoc = false;
+ ifmgd->assoc_data->need_beacon = false;
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+ sdata->vif.bss_conf.sync_tsf =
+ le64_to_cpu(mgmt->u.beacon.timestamp);
+ sdata->vif.bss_conf.sync_device_ts =
+ rx_status->device_timestamp;
+ if (elems.tim)
+ sdata->vif.bss_conf.sync_dtim_count =
+ elems.tim->dtim_count;
+ else
+ sdata->vif.bss_conf.sync_dtim_count = 0;
+ }
/* continue assoc process */
ifmgd->assoc_data->timeout = jiffies;
+ ifmgd->assoc_data->timeout_started = true;
run_again(ifmgd, ifmgd->assoc_data->timeout);
- return;
+ return RX_MGMT_NONE;
}
if (!ifmgd->associated ||
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
- return;
+ return RX_MGMT_NONE;
bssid = ifmgd->associated->bssid;
/* Track average RSSI from the Beacon frames of the current AP */
@@ -2630,7 +2907,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
mlme_dbg_ratelimited(sdata,
- "cancelling probereq poll due to a received beacon\n");
+ "cancelling AP probe due to a received beacon\n");
mutex_lock(&local->mtx);
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
ieee80211_run_deferred_scan(local);
@@ -2702,7 +2979,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
- return;
+ return RX_MGMT_NONE;
ifmgd->beacon_crc = ncrc;
ifmgd->beacon_crc_valid = true;
@@ -2712,6 +2989,32 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
elems.wmm_param_len))
changed |= BSS_CHANGED_QOS;
+ /*
+ * If we haven't had a beacon before, tell the driver about the
+ * DTIM period (and beacon timing if desired) now.
+ */
+ if (!bss_conf->dtim_period) {
+ /* a few bogus AP send dtim_period = 0 or no TIM IE */
+ if (elems.tim)
+ bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
+ else
+ bss_conf->dtim_period = 1;
+
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+ sdata->vif.bss_conf.sync_tsf =
+ le64_to_cpu(mgmt->u.beacon.timestamp);
+ sdata->vif.bss_conf.sync_device_ts =
+ rx_status->device_timestamp;
+ if (elems.tim)
+ sdata->vif.bss_conf.sync_dtim_count =
+ elems.tim->dtim_count;
+ else
+ sdata->vif.bss_conf.sync_dtim_count = 0;
+ }
+
+ changed |= BSS_CHANGED_DTIM_PERIOD;
+ }
+
if (elems.erp_info && elems.erp_info_len >= 1) {
erp_valid = true;
erp_value = elems.erp_info[0];
@@ -2722,11 +3025,22 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
le16_to_cpu(mgmt->u.beacon.capab_info),
erp_valid, erp_value);
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, bssid);
- if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
- changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
- bssid, true);
+ if (ieee80211_config_bw(sdata, sta, elems.ht_operation,
+ elems.vht_operation, bssid, &changed)) {
+ mutex_unlock(&local->sta_mtx);
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_DEAUTH_LEAVING,
+ true, deauth_buf);
+ return RX_MGMT_CFG80211_TX_DEAUTH;
+ }
+
+ if (sta && elems.opmode_notif)
+ ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
+ rx_status->band, true);
+ mutex_unlock(&local->sta_mtx);
if (elems.country_elem && elems.pwr_constr_elem &&
mgmt->u.probe_resp.capab_info &
@@ -2737,6 +3051,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed);
+
+ return RX_MGMT_NONE;
}
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -2747,6 +3063,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt;
struct cfg80211_bss *bss = NULL;
enum rx_mgmt_action rma = RX_MGMT_NONE;
+ u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
u16 fc;
rx_status = (struct ieee80211_rx_status *) skb->cb;
@@ -2757,7 +3074,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
+ rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
+ deauth_buf, rx_status);
break;
case IEEE80211_STYPE_PROBE_RESP:
ieee80211_rx_mgmt_probe_resp(sdata, skb);
@@ -2806,6 +3124,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
case RX_MGMT_CFG80211_ASSOC_TIMEOUT:
cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
break;
+ case RX_MGMT_CFG80211_TX_DEAUTH:
+ cfg80211_send_deauth(sdata->dev, deauth_buf,
+ sizeof(deauth_buf));
+ break;
default:
WARN(1, "unexpected: %d", rma);
}
@@ -2827,14 +3149,13 @@ static void ieee80211_sta_timer(unsigned long data)
}
static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
- u8 *bssid, u8 reason)
+ u8 *bssid, u8 reason, bool tx)
{
- struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
- false, frame_buf);
+ tx, frame_buf);
mutex_unlock(&ifmgd->mtx);
/*
@@ -2843,10 +3164,6 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
*/
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
-
mutex_lock(&ifmgd->mtx);
}
@@ -2855,12 +3172,17 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
+ u32 tx_flags = 0;
lockdep_assert_held(&ifmgd->mtx);
if (WARN_ON_ONCE(!auth_data))
return -EINVAL;
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_MLME_CONN_TX;
+
auth_data->tries++;
if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -2897,7 +3219,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
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);
+ auth_data->bss->bssid, NULL, 0, 0,
+ tx_flags);
} else {
const u8 *ssidie;
@@ -2916,13 +3239,18 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
* 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,
+ NULL, 0, (u32) -1, true, tx_flags,
auth_data->bss->channel, false);
rcu_read_unlock();
}
- auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
- run_again(ifmgd, auth_data->timeout);
+ if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+ auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+ ifmgd->auth_data->timeout_started = true;
+ run_again(ifmgd, auth_data->timeout);
+ } else {
+ auth_data->timeout_started = false;
+ }
return 0;
}
@@ -2953,12 +3281,29 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
IEEE80211_ASSOC_MAX_TRIES);
ieee80211_send_assoc(sdata);
- assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
- run_again(&sdata->u.mgd, assoc_data->timeout);
+ if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+ assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+ assoc_data->timeout_started = true;
+ run_again(&sdata->u.mgd, assoc_data->timeout);
+ } else {
+ assoc_data->timeout_started = false;
+ }
return 0;
}
+void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
+ __le16 fc, bool acked)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ sdata->u.mgd.status_fc = fc;
+ sdata->u.mgd.status_acked = acked;
+ sdata->u.mgd.status_received = true;
+
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -2966,7 +3311,36 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
mutex_lock(&ifmgd->mtx);
- if (ifmgd->auth_data &&
+ if (ifmgd->status_received) {
+ __le16 fc = ifmgd->status_fc;
+ bool status_acked = ifmgd->status_acked;
+
+ ifmgd->status_received = false;
+ if (ifmgd->auth_data &&
+ (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) {
+ if (status_acked) {
+ ifmgd->auth_data->timeout =
+ jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
+ run_again(ifmgd, ifmgd->auth_data->timeout);
+ } else {
+ ifmgd->auth_data->timeout = jiffies - 1;
+ }
+ ifmgd->auth_data->timeout_started = true;
+ } else if (ifmgd->assoc_data &&
+ (ieee80211_is_assoc_req(fc) ||
+ ieee80211_is_reassoc_req(fc))) {
+ if (status_acked) {
+ ifmgd->assoc_data->timeout =
+ jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
+ run_again(ifmgd, ifmgd->assoc_data->timeout);
+ } else {
+ ifmgd->assoc_data->timeout = jiffies - 1;
+ }
+ ifmgd->assoc_data->timeout_started = true;
+ }
+ }
+
+ if (ifmgd->auth_data && ifmgd->auth_data->timeout_started &&
time_after(jiffies, ifmgd->auth_data->timeout)) {
if (ifmgd->auth_data->done) {
/*
@@ -2985,12 +3359,13 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
cfg80211_send_auth_timeout(sdata->dev, bssid);
mutex_lock(&ifmgd->mtx);
}
- } else if (ifmgd->auth_data)
+ } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
run_again(ifmgd, ifmgd->auth_data->timeout);
- if (ifmgd->assoc_data &&
+ if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
time_after(jiffies, ifmgd->assoc_data->timeout)) {
- if (!ifmgd->assoc_data->have_beacon ||
+ if ((ifmgd->assoc_data->need_beacon &&
+ !ifmgd->assoc_data->have_beacon) ||
ieee80211_do_assoc(sdata)) {
u8 bssid[ETH_ALEN];
@@ -3002,7 +3377,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
cfg80211_send_assoc_timeout(sdata->dev, bssid);
mutex_lock(&ifmgd->mtx);
}
- } else if (ifmgd->assoc_data)
+ } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(ifmgd, ifmgd->assoc_data->timeout);
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
@@ -3033,7 +3408,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
"No ack for nullfunc frame to AP %pM, disconnecting.\n",
bssid);
ieee80211_sta_connection_lost(sdata, bssid,
- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+ false);
}
} else if (time_is_after_jiffies(ifmgd->probe_timeout))
run_again(ifmgd, ifmgd->probe_timeout);
@@ -3042,7 +3418,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
"Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
bssid, probe_wait_ms);
ieee80211_sta_connection_lost(sdata, bssid,
- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
} else if (ifmgd->probe_send_count < max_tries) {
mlme_dbg(sdata,
"No probe response from AP %pM after %dms, try %d/%i\n",
@@ -3061,15 +3437,11 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
bssid, probe_wait_ms);
ieee80211_sta_connection_lost(sdata, bssid,
- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
}
}
mutex_unlock(&ifmgd->mtx);
-
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(local);
- mutex_unlock(&local->mtx);
}
static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@ -3081,6 +3453,7 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
if (local->quiescing)
return;
+ sdata->u.mgd.connection_loss = false;
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.beacon_connection_loss_work);
}
@@ -3167,7 +3540,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
mlme_dbg(sdata, "driver requested disconnect after resume\n");
ieee80211_sta_connection_lost(sdata,
ifmgd->associated->bssid,
- WLAN_REASON_UNSPECIFIED);
+ WLAN_REASON_UNSPECIFIED,
+ true);
mutex_unlock(&ifmgd->mtx);
return;
}
@@ -3247,201 +3621,6 @@ 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;
-
-out:
- 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,
- "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n");
-
- 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)
{
@@ -3507,16 +3686,22 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
sband->ht_cap.ht_supported) {
- const u8 *ht_oper_ie;
+ const u8 *ht_oper_ie, *ht_cap;
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);
+
+ ht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
+ if (!ht_cap || ht_cap[1] < sizeof(struct ieee80211_ht_cap)) {
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+ ht_oper = NULL;
+ }
}
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
sband->vht_cap.vht_supported) {
- const u8 *vht_oper_ie;
+ const u8 *vht_oper_ie, *vht_cap;
vht_oper_ie = ieee80211_bss_get_ie(cbss,
WLAN_EID_VHT_OPERATION);
@@ -3526,15 +3711,21 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
vht_oper = NULL;
sdata_info(sdata,
"AP advertised VHT without HT, disabling both\n");
- sdata->flags |= IEEE80211_STA_DISABLE_HT;
- sdata->flags |= IEEE80211_STA_DISABLE_VHT;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+ }
+
+ vht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
+ if (!vht_cap || vht_cap[1] < sizeof(struct ieee80211_vht_cap)) {
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+ vht_oper = NULL;
}
}
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
cbss->channel,
ht_oper, vht_oper,
- &chandef);
+ &chandef, true);
sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
local->rx_chains);
@@ -3584,15 +3775,12 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
return -ENOMEM;
}
- mutex_lock(&local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&local->mtx);
-
if (new_sta) {
u32 rates = 0, basic_rates = 0;
bool have_higher_than_11mbit;
int min_rate = INT_MAX, min_rate_index = -1;
struct ieee80211_supported_band *sband;
+ const struct cfg80211_bss_ies *ies;
sband = local->hw.wiphy->bands[cbss->channel->band];
@@ -3636,8 +3824,34 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
/* set timing information */
sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
- sdata->vif.bss_conf.sync_tsf = cbss->tsf;
- sdata->vif.bss_conf.sync_device_ts = bss->device_ts;
+ rcu_read_lock();
+ ies = rcu_dereference(cbss->beacon_ies);
+ if (ies) {
+ const u8 *tim_ie;
+
+ sdata->vif.bss_conf.sync_tsf = ies->tsf;
+ sdata->vif.bss_conf.sync_device_ts =
+ bss->device_ts_beacon;
+ tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
+ ies->data, ies->len);
+ if (tim_ie && tim_ie[1] >= 2)
+ sdata->vif.bss_conf.sync_dtim_count = tim_ie[2];
+ else
+ sdata->vif.bss_conf.sync_dtim_count = 0;
+ } else if (!(local->hw.flags &
+ IEEE80211_HW_TIMING_BEACON_ONLY)) {
+ ies = rcu_dereference(cbss->proberesp_ies);
+ /* must be non-NULL since beacon IEs were NULL */
+ sdata->vif.bss_conf.sync_tsf = ies->tsf;
+ sdata->vif.bss_conf.sync_device_ts =
+ bss->device_ts_presp;
+ sdata->vif.bss_conf.sync_dtim_count = 0;
+ } else {
+ sdata->vif.bss_conf.sync_tsf = 0;
+ sdata->vif.bss_conf.sync_device_ts = 0;
+ sdata->vif.bss_conf.sync_dtim_count = 0;
+ }
+ rcu_read_unlock();
/* tell driver about BSSID, basic rates and timing */
ieee80211_bss_info_change_notify(sdata,
@@ -3757,7 +3971,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
}
/* hold our own reference */
- cfg80211_ref_bss(auth_data->bss);
+ cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
err = 0;
goto out_unlock;
@@ -3780,6 +3994,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss *bss = (void *)req->bss->priv;
struct ieee80211_mgd_assoc_data *assoc_data;
+ const struct cfg80211_bss_ies *beacon_ies;
struct ieee80211_supported_band *sband;
const u8 *ssidie, *ht_ie, *vht_ie;
int i, err;
@@ -3945,40 +4160,48 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (err)
goto err_clear;
- if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
- const struct cfg80211_bss_ies *beacon_ies;
+ rcu_read_lock();
+ beacon_ies = rcu_dereference(req->bss->beacon_ies);
- rcu_read_lock();
- beacon_ies = rcu_dereference(req->bss->beacon_ies);
- if (!beacon_ies) {
- /*
- * Wait up to one beacon interval ...
- * should this be more if we miss one?
- */
- sdata_info(sdata, "waiting for beacon from %pM\n",
- ifmgd->bssid);
- assoc_data->timeout =
- TU_TO_EXP_TIME(req->bss->beacon_interval);
- } else {
- const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
- beacon_ies->data,
- beacon_ies->len);
- if (tim_ie && tim_ie[1] >=
- sizeof(struct ieee80211_tim_ie)) {
- const struct ieee80211_tim_ie *tim;
- tim = (void *)(tim_ie + 2);
- ifmgd->dtim_period = tim->dtim_period;
- }
- assoc_data->have_beacon = true;
- assoc_data->sent_assoc = false;
- assoc_data->timeout = jiffies;
+ if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC &&
+ !beacon_ies) {
+ /*
+ * Wait up to one beacon interval ...
+ * should this be more if we miss one?
+ */
+ sdata_info(sdata, "waiting for beacon from %pM\n",
+ ifmgd->bssid);
+ assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
+ assoc_data->timeout_started = true;
+ assoc_data->need_beacon = true;
+ } else if (beacon_ies) {
+ const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
+ beacon_ies->data,
+ beacon_ies->len);
+ u8 dtim_count = 0;
+
+ if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) {
+ const struct ieee80211_tim_ie *tim;
+ tim = (void *)(tim_ie + 2);
+ ifmgd->dtim_period = tim->dtim_period;
+ dtim_count = tim->dtim_count;
}
- rcu_read_unlock();
- } else {
assoc_data->have_beacon = true;
- assoc_data->sent_assoc = false;
assoc_data->timeout = jiffies;
+ assoc_data->timeout_started = true;
+
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+ sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf;
+ sdata->vif.bss_conf.sync_device_ts =
+ bss->device_ts_beacon;
+ sdata->vif.bss_conf.sync_dtim_count = dtim_count;
+ }
+ } else {
+ assoc_data->timeout = jiffies;
+ assoc_data->timeout_started = true;
}
+ rcu_read_unlock();
+
run_again(ifmgd, assoc_data->timeout);
if (bss->corrupt_data) {
@@ -4045,10 +4268,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx);
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);
@@ -4089,10 +4308,6 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
__cfg80211_send_disassoc(sdata->dev, frame_buf,
IEEE80211_DEAUTH_FRAME_LEN);
- mutex_lock(&sdata->local->mtx);
- ieee80211_recalc_idle(sdata->local);
- mutex_unlock(&sdata->local->mtx);
-
return 0;
}
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 82baf5b6ecf4..cc79b4a2e821 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -113,6 +113,15 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
* notify the AP about us leaving the channel and stop all
* STA interfaces.
*/
+
+ /*
+ * Stop queues and transmit all frames queued by the driver
+ * before sending nullfunc to enable powersave at the AP.
+ */
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
+ drv_flush(local, false);
+
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
@@ -133,12 +142,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
sdata, BSS_CHANGED_BEACON_ENABLED);
}
- if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
- netif_tx_stop_all_queues(sdata->dev);
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- sdata->u.mgd.associated)
- ieee80211_offchannel_ps_enable(sdata);
- }
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ sdata->u.mgd.associated)
+ ieee80211_offchannel_ps_enable(sdata);
}
mutex_unlock(&local->iflist_mtx);
}
@@ -166,20 +172,6 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
sdata->u.mgd.associated)
ieee80211_offchannel_ps_disable(sdata);
- if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
- /*
- * This may wake up queues even though the driver
- * currently has them stopped. This is not very
- * likely, since the driver won't have gotten any
- * (or hardly any) new packets while we weren't
- * on the right channel, and even if it happens
- * it will at most lead to queueing up one more
- * packet per queue in mac80211 rather than on
- * the interface qdisc.
- */
- netif_tx_wake_all_queues(sdata->dev);
- }
-
if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED,
&sdata->state)) {
sdata->vif.bss_conf.enable_beacon = true;
@@ -188,6 +180,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
}
}
mutex_unlock(&local->iflist_mtx);
+
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
}
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index e45b83610e85..d0275f34bf70 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -38,6 +38,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
ieee80211_scan_cancel(local);
+ ieee80211_dfs_cac_cancel(local);
+
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
@@ -228,3 +230,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
* ieee80211_reconfig(), which is also needed for hardware
* hang/firmware failure/etc. recovery.
*/
+
+void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
+ struct cfg80211_wowlan_wakeup *wakeup,
+ gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp);
+}
+EXPORT_SYMBOL(ieee80211_report_wowlan_wakeup);
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 301386dabf88..d35a5dd3fb13 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -68,6 +68,8 @@ static inline void rate_control_rate_init(struct sta_info *sta)
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
rcu_read_unlock();
+ ieee80211_sta_set_rx_nss(sta);
+
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 8c5acdc06226..eea45a2c7c35 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -494,6 +494,33 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
kfree(mi);
}
+static void
+minstrel_init_cck_rates(struct minstrel_priv *mp)
+{
+ static const int bitrates[4] = { 10, 20, 55, 110 };
+ struct ieee80211_supported_band *sband;
+ int i, j;
+
+ sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0, j = 0; i < sband->n_bitrates; i++) {
+ struct ieee80211_rate *rate = &sband->bitrates[i];
+
+ if (rate->flags & IEEE80211_RATE_ERP_G)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
+ if (rate->bitrate != bitrates[j])
+ continue;
+
+ mp->cck_rates[j] = i;
+ break;
+ }
+ }
+}
+
static void *
minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
{
@@ -539,6 +566,8 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx);
#endif
+ minstrel_init_cck_rates(mp);
+
return mp;
}
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 5d278eccaef0..5ecf757817f2 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -79,6 +79,8 @@ struct minstrel_priv {
unsigned int lookaround_rate;
unsigned int lookaround_rate_mrr;
+ u8 cck_rates[4];
+
#ifdef CONFIG_MAC80211_DEBUGFS
/*
* enable fixed rate processing per RC
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 9f9c453bc45d..3af141c69712 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
*
* 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
@@ -63,6 +63,30 @@
} \
}
+#define CCK_DURATION(_bitrate, _short, _len) \
+ (10 /* SIFS */ + \
+ (_short ? 72 + 24 : 144 + 48 ) + \
+ (8 * (_len + 4) * 10) / (_bitrate))
+
+#define CCK_ACK_DURATION(_bitrate, _short) \
+ (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \
+ CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE))
+
+#define CCK_DURATION_LIST(_short) \
+ CCK_ACK_DURATION(10, _short), \
+ CCK_ACK_DURATION(20, _short), \
+ CCK_ACK_DURATION(55, _short), \
+ CCK_ACK_DURATION(110, _short)
+
+#define CCK_GROUP \
+ [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
+ .streams = 0, \
+ .duration = { \
+ CCK_DURATION_LIST(false), \
+ CCK_DURATION_LIST(true) \
+ } \
+ }
+
/*
* To enable sufficiently targeted rate sampling, MCS rates are divided into
* groups, based on the number of streams and flags (HT40, SGI) that they
@@ -95,8 +119,13 @@ const struct mcs_group minstrel_mcs_groups[] = {
#if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP(3, 1, 1),
#endif
+
+ /* must be last */
+ CCK_GROUP
};
+#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
+
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES];
/*
@@ -119,6 +148,29 @@ minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
}
+static struct minstrel_rate_stats *
+minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
+ struct ieee80211_tx_rate *rate)
+{
+ int group, idx;
+
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
+ group = minstrel_ht_get_group_idx(rate);
+ idx = rate->idx % MCS_GROUP_RATES;
+ } else {
+ group = MINSTREL_CCK_GROUP;
+
+ for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++)
+ if (rate->idx == mp->cck_rates[idx])
+ break;
+
+ /* short preamble */
+ if (!(mi->groups[group].supported & BIT(idx)))
+ idx += 4;
+ }
+ return &mi->groups[group].rates[idx];
+}
+
static inline struct minstrel_rate_stats *
minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
{
@@ -159,7 +211,7 @@ static void
minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
{
struct minstrel_rate_stats *mr;
- unsigned int usecs;
+ unsigned int usecs = 0;
mr = &mi->groups[group].rates[rate];
@@ -168,7 +220,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
return;
}
- usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+ if (group != MINSTREL_CCK_GROUP)
+ usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
+
usecs += minstrel_mcs_groups[group].duration[rate];
mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability);
}
@@ -231,10 +285,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (!mr->cur_tp)
continue;
- /* ignore the lowest rate of each single-stream group */
- if (!i && minstrel_mcs_groups[group].streams == 1)
- continue;
-
if ((mr->cur_tp > cur_prob_tp && mr->probability >
MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) {
mg->max_prob_rate = index;
@@ -297,7 +347,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
}
static bool
-minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
+minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate)
{
if (rate->idx < 0)
return false;
@@ -305,7 +355,13 @@ minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate)
if (!rate->count)
return false;
- return !!(rate->flags & IEEE80211_TX_RC_MCS);
+ if (rate->flags & IEEE80211_TX_RC_MCS)
+ return true;
+
+ return rate->idx == mp->cck_rates[0] ||
+ rate->idx == mp->cck_rates[1] ||
+ rate->idx == mp->cck_rates[2] ||
+ rate->idx == mp->cck_rates[3];
}
static void
@@ -390,7 +446,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
bool last;
- int group;
int i;
if (!msp->is_ht)
@@ -419,13 +474,12 @@ 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]);
+ last = !minstrel_ht_txstat_valid(mp, &ar[0]);
for (i = 0; !last; i++) {
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
- !minstrel_ht_txstat_valid(&ar[i + 1]);
+ !minstrel_ht_txstat_valid(mp, &ar[i + 1]);
- group = minstrel_ht_get_group_idx(&ar[i]);
- rate = &mi->groups[group].rates[ar[i].idx % 8];
+ rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
if (last)
rate->success += info->status.ampdu_ack_len;
@@ -451,7 +505,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
minstrel_ht_update_stats(mp, mi);
- if (!(info->flags & IEEE80211_TX_CTL_AMPDU))
+ if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
minstrel_aggr_check(sta, skb);
}
}
@@ -467,6 +522,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
unsigned int ctime = 0;
unsigned int t_slot = 9; /* FIXME */
unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
+ unsigned int overhead = 0, overhead_rtscts = 0;
mr = minstrel_get_ratestats(mi, index);
if (mr->probability < MINSTREL_FRAC(1, 10)) {
@@ -488,9 +544,14 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
ctime += (t_slot * cw) >> 1;
cw = min((cw << 1) | 1, mp->cw_max);
+ if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) {
+ overhead = mi->overhead;
+ overhead_rtscts = mi->overhead_rtscts;
+ }
+
/* Total TX time for data and Contention after first 2 tries */
- tx_time = ctime + 2 * (mi->overhead + tx_time_data);
- tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data);
+ tx_time = ctime + 2 * (overhead + tx_time_data);
+ tx_time_rtscts = ctime + 2 * (overhead_rtscts + tx_time_data);
/* See how many more tries we can fit inside segment size */
do {
@@ -499,8 +560,8 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
cw = min((cw << 1) | 1, mp->cw_max);
/* Total TX time after this try */
- tx_time += ctime + mi->overhead + tx_time_data;
- tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data;
+ tx_time += ctime + overhead + tx_time_data;
+ tx_time_rtscts += ctime + overhead_rtscts + tx_time_data;
if (tx_time_rtscts < mp->segment_size)
mr->retry_count_rtscts++;
@@ -530,9 +591,16 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
else
rate->count = mr->retry_count;
- rate->flags = IEEE80211_TX_RC_MCS | group->flags;
+ rate->flags = 0;
if (rtscts)
rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
+
+ if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
+ rate->idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
+ return;
+ }
+
+ rate->flags |= IEEE80211_TX_RC_MCS | group->flags;
rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES;
}
@@ -596,6 +664,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
}
static void
+minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp,
+ struct minstrel_ht_sta *mi, bool val)
+{
+ u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported;
+
+ if (!supported || !mi->cck_supported_short)
+ return;
+
+ if (supported & (mi->cck_supported_short << (val * 4)))
+ return;
+
+ supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4);
+ mi->groups[MINSTREL_CCK_GROUP].supported = supported;
+}
+
+static void
minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
struct ieee80211_tx_rate_control *txrc)
{
@@ -614,6 +698,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
info->flags |= mi->tx_flags;
+ minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
/* Don't use EAPOL frames for sampling on non-mrr hw */
if (mp->hw->max_rates == 1 &&
@@ -687,6 +772,30 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
}
static void
+minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta)
+{
+ int i;
+
+ if (sband->band != IEEE80211_BAND_2GHZ)
+ return;
+
+ mi->cck_supported = 0;
+ mi->cck_supported_short = 0;
+ for (i = 0; i < 4; i++) {
+ if (!rate_supported(sta, sband->band, mp->cck_rates[i]))
+ continue;
+
+ mi->cck_supported |= BIT(i);
+ if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE)
+ mi->cck_supported_short |= BIT(i);
+ }
+
+ mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported;
+}
+
+static void
minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta)
{
@@ -699,14 +808,13 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
int ack_dur;
int stbc;
int i;
- unsigned int smps;
/* fall back to the old minstrel for legacy stations */
if (!sta->ht_cap.ht_supported)
goto use_legacy;
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
- MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS);
+ MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
msp->is_ht = true;
memset(mi, 0, sizeof(*mi));
@@ -735,28 +843,29 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
- smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >>
- IEEE80211_HT_CAP_SM_PS_SHIFT;
-
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
- u16 req = 0;
-
mi->groups[i].supported = 0;
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- req |= IEEE80211_HT_CAP_SGI_40;
- else
- req |= IEEE80211_HT_CAP_SGI_20;
+ if (i == MINSTREL_CCK_GROUP) {
+ minstrel_ht_update_cck(mp, mi, sband, sta);
+ continue;
}
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
+ if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
+ continue;
+ } else {
+ if (!(sta_cap & IEEE80211_HT_CAP_SGI_20))
+ continue;
+ }
+ }
- if ((sta_cap & req) != req)
+ if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
+ sta->bandwidth < IEEE80211_STA_RX_BW_40)
continue;
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
- if (smps == WLAN_HT_CAP_SM_PS_STATIC &&
+ if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
minstrel_mcs_groups[i].streams > 1)
continue;
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index 462d2b227ed5..302dbd52180d 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -107,8 +107,11 @@ struct minstrel_ht_sta {
/* current MCS group to be sampled */
u8 sample_group;
+ u8 cck_supported;
+ u8 cck_supported_short;
+
/* MCS rate group info and statistics */
- struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS];
+ struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];
};
struct minstrel_ht_sta_priv {
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index e788f76a1dfe..df44a5ad8270 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -15,13 +15,76 @@
#include "rc80211_minstrel.h"
#include "rc80211_minstrel_ht.h"
+static char *
+minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
+{
+ unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
+ const struct mcs_group *mg;
+ unsigned int j, tp, prob, eprob;
+ char htmode = '2';
+ char gimode = 'L';
+
+ if (!mi->groups[i].supported)
+ return p;
+
+ mg = &minstrel_mcs_groups[i];
+ if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ htmode = '4';
+ if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
+ gimode = 'S';
+
+ for (j = 0; j < MCS_GROUP_RATES; j++) {
+ struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
+ static const int bitrates[4] = { 10, 20, 55, 110 };
+ int idx = i * MCS_GROUP_RATES + j;
+
+ if (!(mi->groups[i].supported & BIT(j)))
+ continue;
+
+ if (i == max_mcs)
+ p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S');
+ else
+ p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
+
+ *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' ';
+ *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' ';
+ *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
+
+ if (i == max_mcs) {
+ int r = bitrates[j % 4];
+ p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
+ } else {
+ p += sprintf(p, " MCS%-2u", (mg->streams - 1) *
+ MCS_GROUP_RATES + j);
+ }
+
+ tp = mr->cur_tp / 10;
+ prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
+ eprob = MINSTREL_TRUNC(mr->probability * 1000);
+
+ p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
+ "%3u %3u(%3u) %8llu %8llu\n",
+ tp / 10, tp % 10,
+ eprob / 10, eprob % 10,
+ prob / 10, prob % 10,
+ mr->retry_count,
+ mr->last_success,
+ mr->last_attempts,
+ (unsigned long long)mr->succ_hist,
+ (unsigned long long)mr->att_hist);
+ }
+
+ return p;
+}
+
static int
minstrel_ht_stats_open(struct inode *inode, struct file *file)
{
struct minstrel_ht_sta_priv *msp = inode->i_private;
struct minstrel_ht_sta *mi = &msp->ht;
struct minstrel_debugfs_info *ms;
- unsigned int i, j, tp, prob, eprob;
+ unsigned int i;
+ unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
char *p;
int ret;
@@ -38,50 +101,13 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
file->private_data = ms;
p = ms->buf;
- p += sprintf(p, "type rate throughput ewma prob this prob "
- "this succ/attempt success attempts\n");
- for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) {
- char htmode = '2';
- char gimode = 'L';
-
- if (!mi->groups[i].supported)
- continue;
-
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- htmode = '4';
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI)
- gimode = 'S';
+ p += sprintf(p, "type rate throughput ewma prob this prob "
+ "retry this succ/attempt success attempts\n");
- for (j = 0; j < MCS_GROUP_RATES; j++) {
- struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
- int idx = i * MCS_GROUP_RATES + j;
+ p = minstrel_ht_stats_dump(mi, max_mcs, p);
+ for (i = 0; i < max_mcs; i++)
+ p = minstrel_ht_stats_dump(mi, i, p);
- if (!(mi->groups[i].supported & BIT(j)))
- continue;
-
- p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
-
- *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' ';
- *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' ';
- *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
- p += sprintf(p, "MCS%-2u", (minstrel_mcs_groups[i].streams - 1) *
- MCS_GROUP_RATES + j);
-
- tp = mr->cur_tp / 10;
- prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
- eprob = MINSTREL_TRUNC(mr->probability * 1000);
-
- p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
- "%3u(%3u) %8llu %8llu\n",
- tp / 10, tp % 10,
- eprob / 10, eprob % 10,
- prob / 10, prob % 10,
- mr->last_success,
- mr->last_attempts,
- (unsigned long long)mr->succ_hist,
- (unsigned long long)mr->att_hist);
- }
- }
p += sprintf(p, "\nTotal packet count:: ideal %d "
"lookaround %d\n",
max(0, (int) mi->total_packets - (int) mi->sample_packets),
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index a19089565c4b..3acb70b73e22 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -668,9 +668,9 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
- int index)
+ int index,
+ struct sk_buff_head *frames)
{
- struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
struct ieee80211_rx_status *status;
@@ -684,7 +684,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
tid_agg_rx->reorder_buf[index] = NULL;
status = IEEE80211_SKB_RXCB(skb);
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
- skb_queue_tail(&local->rx_skb_queue, skb);
+ __skb_queue_tail(frames, skb);
no_frame:
tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
@@ -692,7 +692,8 @@ no_frame:
static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
- u16 head_seq_num)
+ u16 head_seq_num,
+ struct sk_buff_head *frames)
{
int index;
@@ -701,7 +702,8 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata
while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
- ieee80211_release_reorder_frame(sdata, tid_agg_rx, index);
+ ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
+ frames);
}
}
@@ -717,7 +719,8 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata
#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10)
static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
- struct tid_ampdu_rx *tid_agg_rx)
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff_head *frames)
{
int index, j;
@@ -746,7 +749,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
ht_dbg_ratelimited(sdata,
"release an RX reorder frame due to timeout on earlier frames\n");
- ieee80211_release_reorder_frame(sdata, tid_agg_rx, j);
+ ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
+ frames);
/*
* Increment the head seq# also for the skipped slots.
@@ -756,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
skipped = 0;
}
} else while (tid_agg_rx->reorder_buf[index]) {
- ieee80211_release_reorder_frame(sdata, tid_agg_rx, index);
+ ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
+ frames);
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
}
@@ -788,7 +793,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
*/
static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ struct sk_buff_head *frames)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 sc = le16_to_cpu(hdr->seq_ctrl);
@@ -816,7 +822,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
/* release stored frames up to new head to stack */
ieee80211_release_reorder_frames(sdata, tid_agg_rx,
- head_seq_num);
+ head_seq_num, frames);
}
/* Now the new frame is always in the range of the reordering buffer */
@@ -846,7 +852,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
tid_agg_rx->reorder_buf[index] = skb;
tid_agg_rx->reorder_time[index] = jiffies;
tid_agg_rx->stored_mpdu_num++;
- ieee80211_sta_reorder_release(sdata, tid_agg_rx);
+ ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
out:
spin_unlock(&tid_agg_rx->reorder_lock);
@@ -857,7 +863,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
* Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns
* true if the MPDU was buffered, false if it should be processed.
*/
-static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
+static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
+ struct sk_buff_head *frames)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_local *local = rx->local;
@@ -922,11 +929,12 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
* sure that we cannot get to it any more before doing
* anything with it.
*/
- if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb))
+ if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb,
+ frames))
return;
dont_reorder:
- skb_queue_tail(&local->rx_skb_queue, skb);
+ __skb_queue_tail(frames, skb);
}
static ieee80211_rx_result debug_noinline
@@ -1452,6 +1460,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
}
}
+ /* mesh power save support */
+ if (ieee80211_vif_is_mesh(&rx->sdata->vif))
+ ieee80211_mps_rx_h_sta_process(sta, hdr);
+
/*
* Drop (qos-)data::nullfunc frames silently, since they
* are used only to control station power saving mode.
@@ -2090,7 +2102,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
if (is_multicast_ether_addr(fwd_hdr->addr1)) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
+ /* update power mode indication when forwarding */
+ ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
} else if (!mesh_nexthop_lookup(fwd_skb, sdata)) {
+ /* mesh power mode flags updated in mesh_nexthop_lookup */
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
} else {
/* unable to resolve next hop */
@@ -2177,7 +2192,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
}
static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data;
@@ -2216,7 +2231,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
spin_lock(&tid_agg_rx->reorder_lock);
/* release stored frames up to start of BAR */
ieee80211_release_reorder_frames(rx->sdata, tid_agg_rx,
- start_seq_num);
+ start_seq_num, frames);
spin_unlock(&tid_agg_rx->reorder_lock);
kfree_skb(skb);
@@ -2360,31 +2375,27 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
switch (mgmt->u.action.u.ht_smps.action) {
case WLAN_HT_ACTION_SMPS: {
struct ieee80211_supported_band *sband;
- u8 smps;
+ enum ieee80211_smps_mode smps_mode;
/* convert to HT capability */
switch (mgmt->u.action.u.ht_smps.smps_control) {
case WLAN_HT_SMPS_CONTROL_DISABLED:
- smps = WLAN_HT_CAP_SM_PS_DISABLED;
+ smps_mode = IEEE80211_SMPS_OFF;
break;
case WLAN_HT_SMPS_CONTROL_STATIC:
- smps = WLAN_HT_CAP_SM_PS_STATIC;
+ smps_mode = IEEE80211_SMPS_STATIC;
break;
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
- smps = WLAN_HT_CAP_SM_PS_DYNAMIC;
+ smps_mode = IEEE80211_SMPS_DYNAMIC;
break;
default:
goto invalid;
}
- smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
/* if no change do nothing */
- if ((rx->sta->sta.ht_cap.cap &
- IEEE80211_HT_CAP_SM_PS) == smps)
+ if (rx->sta->sta.smps_mode == smps_mode)
goto handled;
-
- rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS;
- rx->sta->sta.ht_cap.cap |= smps;
+ rx->sta->sta.smps_mode = smps_mode;
sband = rx->local->hw.wiphy->bands[status->band];
@@ -2395,26 +2406,21 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
struct ieee80211_supported_band *sband;
u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
- bool old_40mhz, new_40mhz;
+ enum ieee80211_sta_rx_bandwidth new_bw;
/* If it doesn't support 40 MHz it can't change ... */
- if (!rx->sta->supports_40mhz)
+ if (!(rx->sta->sta.ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40))
goto handled;
- old_40mhz = rx->sta->sta.ht_cap.cap &
- IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY;
+ if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
+ new_bw = IEEE80211_STA_RX_BW_20;
+ else
+ new_bw = ieee80211_sta_cur_vht_bw(rx->sta);
- if (old_40mhz == new_40mhz)
+ if (rx->sta->sta.bandwidth == new_bw)
goto handled;
- if (new_40mhz)
- rx->sta->sta.ht_cap.cap |=
- IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- else
- rx->sta->sta.ht_cap.cap &=
- ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-
sband = rx->local->hw.wiphy->bands[status->band];
rate_control_rate_update(local, sband, rx->sta,
@@ -2426,6 +2432,37 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
}
break;
+ case WLAN_CATEGORY_VHT:
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+ sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ break;
+
+ /* verify action code is present */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+ goto invalid;
+
+ switch (mgmt->u.action.u.vht_opmode_notif.action_code) {
+ case WLAN_VHT_ACTION_OPMODE_NOTIF: {
+ u8 opmode;
+
+ /* verify opmode is present */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 2)
+ goto invalid;
+
+ opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
+
+ ieee80211_vht_handle_opmode(rx->sdata, rx->sta,
+ opmode, status->band,
+ false);
+ goto handled;
+ }
+ default:
+ break;
+ }
+ break;
case WLAN_CATEGORY_BACK:
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
@@ -2677,8 +2714,9 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
break;
case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ):
- /* process only for ibss */
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ /* process only for ibss and mesh */
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return RX_DROP_MONITOR;
break;
default:
@@ -2801,7 +2839,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
}
}
-static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
+static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
+ struct sk_buff_head *frames)
{
ieee80211_rx_result res = RX_DROP_MONITOR;
struct sk_buff *skb;
@@ -2813,15 +2852,9 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
goto rxh_next; \
} while (0);
- spin_lock(&rx->local->rx_skb_queue.lock);
- if (rx->local->running_rx_handler)
- goto unlock;
-
- rx->local->running_rx_handler = true;
-
- while ((skb = __skb_dequeue(&rx->local->rx_skb_queue))) {
- spin_unlock(&rx->local->rx_skb_queue.lock);
+ spin_lock_bh(&rx->local->rx_path_lock);
+ while ((skb = __skb_dequeue(frames))) {
/*
* all the other fields are valid across frames
* that belong to an aMPDU since they are on the
@@ -2842,7 +2875,12 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
#endif
CALL_RXH(ieee80211_rx_h_amsdu)
CALL_RXH(ieee80211_rx_h_data)
- CALL_RXH(ieee80211_rx_h_ctrl);
+
+ /* special treatment -- needs the queue */
+ res = ieee80211_rx_h_ctrl(rx, frames);
+ if (res != RX_CONTINUE)
+ goto rxh_next;
+
CALL_RXH(ieee80211_rx_h_mgmt_check)
CALL_RXH(ieee80211_rx_h_action)
CALL_RXH(ieee80211_rx_h_userspace_mgmt)
@@ -2851,20 +2889,20 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
rxh_next:
ieee80211_rx_handlers_result(rx, res);
- spin_lock(&rx->local->rx_skb_queue.lock);
+
#undef CALL_RXH
}
- rx->local->running_rx_handler = false;
-
- unlock:
- spin_unlock(&rx->local->rx_skb_queue.lock);
+ spin_unlock_bh(&rx->local->rx_path_lock);
}
static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
{
+ struct sk_buff_head reorder_release;
ieee80211_rx_result res = RX_DROP_MONITOR;
+ __skb_queue_head_init(&reorder_release);
+
#define CALL_RXH(rxh) \
do { \
res = rxh(rx); \
@@ -2874,9 +2912,9 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
CALL_RXH(ieee80211_rx_h_check)
- ieee80211_rx_reorder_ampdu(rx);
+ ieee80211_rx_reorder_ampdu(rx, &reorder_release);
- ieee80211_rx_handlers(rx);
+ ieee80211_rx_handlers(rx, &reorder_release);
return;
rxh_next:
@@ -2891,6 +2929,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
*/
void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
{
+ struct sk_buff_head frames;
struct ieee80211_rx_data rx = {
.sta = sta,
.sdata = sta->sdata,
@@ -2906,11 +2945,13 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
if (!tid_agg_rx)
return;
+ __skb_queue_head_init(&frames);
+
spin_lock(&tid_agg_rx->reorder_lock);
- ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx);
+ ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames);
spin_unlock(&tid_agg_rx->reorder_lock);
- ieee80211_rx_handlers(&rx);
+ ieee80211_rx_handlers(&rx, &frames);
}
/* main receive path */
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 607684c47d55..43a45cf00e06 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -27,22 +27,15 @@
#define IEEE80211_PROBE_DELAY (HZ / 33)
#define IEEE80211_CHANNEL_TIME (HZ / 33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8)
-
-static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss)
-{
- struct ieee80211_bss *bss = (void *)cbss->priv;
-
- kfree(bss_mesh_id(bss));
- kfree(bss_mesh_cfg(bss));
-}
+#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9)
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss)
{
if (!bss)
return;
- cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv));
+ cfg80211_put_bss(local->hw.wiphy,
+ container_of((void *)bss, struct cfg80211_bss, priv));
}
static bool is_uapsd_supported(struct ieee802_11_elems *elems)
@@ -85,10 +78,12 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
if (!cbss)
return NULL;
- cbss->free_priv = ieee80211_rx_bss_free;
bss = (void *)cbss->priv;
- bss->device_ts = rx_status->device_timestamp;
+ if (beacon)
+ bss->device_ts_beacon = rx_status->device_timestamp;
+ else
+ bss->device_ts_presp = rx_status->device_timestamp;
if (elems->parse_error) {
if (beacon)
@@ -146,9 +141,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_WMM;
}
- if (!beacon)
- bss->last_probe_resp = jiffies;
-
return bss;
}
@@ -342,6 +334,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_offchannel_stop_vifs(local);
+ /* ensure nullfunc is transmitted before leaving operating channel */
+ drv_flush(local, false);
+
ieee80211_configure_filter(local);
/* We need to set power level at maximum rate for scanning. */
@@ -356,6 +351,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
static bool ieee80211_can_scan(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
+ if (local->radar_detect_enabled)
+ return false;
+
if (!list_empty(&local->roc_list))
return false;
@@ -390,6 +388,11 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
int i;
struct ieee80211_sub_if_data *sdata;
enum ieee80211_band band = local->hw.conf.channel->band;
+ u32 tx_flags;
+
+ tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+ if (local->scan_req->no_cck)
+ tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));
@@ -401,8 +404,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
local->scan_req->ssids[i].ssid_len,
local->scan_req->ie, local->scan_req->ie_len,
local->scan_req->rates[band], false,
- local->scan_req->no_cck,
- local->hw.conf.channel, true);
+ tx_flags, local->hw.conf.channel, true);
/*
* After sending probe requests, wait for probe responses
@@ -546,8 +548,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
bool associated = false;
bool tx_empty = true;
bool bad_latency;
- bool listen_int_exceeded;
- unsigned long min_beacon_int = 0;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
enum mac80211_scan_state next_scan_state;
@@ -566,11 +566,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
if (sdata->u.mgd.associated) {
associated = true;
- if (sdata->vif.bss_conf.beacon_int <
- min_beacon_int || min_beacon_int == 0)
- min_beacon_int =
- sdata->vif.bss_conf.beacon_int;
-
if (!qdisc_all_tx_empty(sdata->dev)) {
tx_empty = false;
break;
@@ -587,34 +582,19 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
* see if we can scan another channel without interfering
* with the current traffic situation.
*
- * Since we don't know if the AP has pending frames for us
- * we can only check for our tx queues and use the current
- * pm_qos requirements for rx. Hence, if no tx traffic occurs
- * at all we will scan as many channels in a row as the pm_qos
- * latency allows us to. Additionally we also check for the
- * currently negotiated listen interval to prevent losing
- * frames unnecessarily.
- *
- * Otherwise switch back to the operating channel.
+ * Keep good latency, do not stay off-channel more than 125 ms.
*/
bad_latency = time_after(jiffies +
- ieee80211_scan_get_channel_time(next_chan),
- local->leave_oper_channel_time +
- usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY)));
-
- listen_int_exceeded = time_after(jiffies +
- ieee80211_scan_get_channel_time(next_chan),
- local->leave_oper_channel_time +
- usecs_to_jiffies(min_beacon_int * 1024) *
- local->hw.conf.listen_interval);
+ ieee80211_scan_get_channel_time(next_chan),
+ local->leave_oper_channel_time + HZ / 8);
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)) {
+ } else if (associated && bad_latency) {
next_scan_state = SCAN_SUSPEND;
} else {
next_scan_state = SCAN_SET_CHANNEL;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 227233c3ff7f..a79ce820cb50 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -120,6 +120,8 @@ static void cleanup_single_sta(struct sta_info *sta)
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
+ else if (ieee80211_vif_is_mesh(&sdata->vif))
+ ps = &sdata->u.mesh.ps;
else
return;
@@ -135,13 +137,8 @@ static void cleanup_single_sta(struct sta_info *sta)
ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);
}
-#ifdef CONFIG_MAC80211_MESH
- if (ieee80211_vif_is_mesh(&sdata->vif)) {
- mesh_accept_plinks_update(sdata);
- mesh_plink_deactivate(sta);
- del_timer_sync(&sta->plink_timer);
- }
-#endif
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ mesh_sta_cleanup(sta);
cancel_work_sync(&sta->drv_unblock_wk);
@@ -378,6 +375,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
+ sta->sta.smps_mode = IEEE80211_SMPS_OFF;
+
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
return sta;
@@ -574,7 +573,6 @@ void sta_info_recalc_tim(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ps_data *ps;
- unsigned long flags;
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
@@ -587,6 +585,12 @@ void sta_info_recalc_tim(struct sta_info *sta)
ps = &sta->sdata->bss->ps;
id = sta->sta.aid;
+#ifdef CONFIG_MAC80211_MESH
+ } else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
+ ps = &sta->sdata->u.mesh.ps;
+ /* TIM map only for PLID <= IEEE80211_MAX_AID */
+ id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
+#endif
} else {
return;
}
@@ -625,7 +629,7 @@ void sta_info_recalc_tim(struct sta_info *sta)
}
done:
- spin_lock_irqsave(&local->tim_lock, flags);
+ spin_lock_bh(&local->tim_lock);
if (indicate_tim)
__bss_tim_set(ps->tim, id);
@@ -638,7 +642,7 @@ void sta_info_recalc_tim(struct sta_info *sta)
local->tim_in_locked_section = false;
}
- spin_unlock_irqrestore(&local->tim_lock, flags);
+ spin_unlock_bh(&local->tim_lock);
}
static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb)
@@ -745,8 +749,9 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
bool have_buffered = false;
int ac;
- /* This is only necessary for stations on BSS interfaces */
- if (!sta->sdata->bss)
+ /* This is only necessary for stations on BSS/MBSS interfaces */
+ if (!sta->sdata->bss &&
+ !ieee80211_vif_is_mesh(&sta->sdata->vif))
return false;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
@@ -934,6 +939,11 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
if (time_after(jiffies, sta->last_rx + exp_time)) {
sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
sta->sta.addr);
+
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
+ test_sta_flag(sta, WLAN_STA_PS_STA))
+ atomic_dec(&sdata->u.mesh.ps.num_sta_ps);
+
WARN_ON(__sta_info_destroy(sta));
}
}
@@ -992,6 +1002,8 @@ static void clear_sta_ps_flags(void *_sta)
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
ps = &sdata->bss->ps;
+ else if (ieee80211_vif_is_mesh(&sdata->vif))
+ ps = &sdata->u.mesh.ps;
else
return;
@@ -1109,6 +1121,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
+ skb->dev = sdata->dev;
+
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index af7d78aa5523..63dfdb5e91da 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -56,6 +56,8 @@
* @WLAN_STA_INSERTED: This station is inserted into the hash table.
* @WLAN_STA_RATE_CONTROL: rate control was initialized for this station.
* @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
+ * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
+ * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH,
@@ -78,6 +80,8 @@ enum ieee80211_sta_info_flags {
WLAN_STA_INSERTED,
WLAN_STA_RATE_CONTROL,
WLAN_STA_TOFFSET_KNOWN,
+ WLAN_STA_MPSP_OWNER,
+ WLAN_STA_MPSP_RECIPIENT,
};
#define ADDBA_RESP_INTERVAL HZ
@@ -282,6 +286,9 @@ struct sta_ampdu_mlme {
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
* @ch_width: peer's channel width
+ * @local_pm: local link-specific power save mode
+ * @peer_pm: peer-specific power save mode towards local STA
+ * @nonpeer_pm: STA power save mode towards non-peer neighbors
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -289,8 +296,9 @@ struct sta_ampdu_mlme {
* @sta: station information we share with the driver
* @sta_state: duplicates information about station state (for debug)
* @beacon_loss_count: number of times beacon loss has triggered
- * @supports_40mhz: tracks whether the station advertised 40 MHz support
- * as we overwrite its HT parameters with the currently used value
+ * @rcu_head: RCU head used for freeing this station struct
+ * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
+ * taken from HT/VHT capabilities or VHT operating mode notification
*/
struct sta_info {
/* General information, mostly static */
@@ -379,6 +387,10 @@ struct sta_info {
s64 t_offset;
s64 t_offset_setpoint;
enum nl80211_chan_width ch_width;
+ /* mesh power save */
+ enum nl80211_mesh_power_mode local_pm;
+ enum nl80211_mesh_power_mode peer_pm;
+ enum nl80211_mesh_power_mode nonpeer_pm;
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -388,11 +400,11 @@ struct sta_info {
} debugfs;
#endif
+ enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
+
unsigned int lost_packets;
unsigned int beacon_loss_count;
- bool supports_40mhz;
-
/* keep last! */
struct ieee80211_sta sta;
};
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 07d99578a2b1..43439203f4e4 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -335,7 +335,8 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
if (dropped)
acked = false;
- if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+ if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_INTFL_MLME_CONN_TX)) {
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_sub_if_data *iter_sdata;
u64 cookie = (unsigned long)skb;
@@ -357,10 +358,13 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
sdata = rcu_dereference(local->p2p_sdata);
}
- if (!sdata)
+ if (!sdata) {
skb->dev = NULL;
- else if (ieee80211_is_nullfunc(hdr->frame_control) ||
- ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+ } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
+ ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control,
+ acked);
+ } 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 {
@@ -468,6 +472,13 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}
+ /* mesh Peer Service Period support */
+ if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
+ ieee80211_is_data_qos(fc))
+ ieee80211_mpsp_trigger_process(
+ ieee80211_get_qos_ctl(hdr),
+ sta, true, acked);
+
if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
(rates_idx != -1))
sta->last_tx_rate = info->status.rates[rates_idx];
@@ -502,11 +513,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
- 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);
+ ieee80211_set_bar_pending(sta, tid, ssn);
}
}
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 57e14d59e12f..3ed801d90f1e 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -177,12 +177,11 @@ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf,
struct ieee80211_key *key = (struct ieee80211_key *)
container_of(keyconf, struct ieee80211_key, conf);
struct tkip_ctx *ctx = &key->u.tkip.tx;
- unsigned long flags;
- spin_lock_irqsave(&key->u.tkip.txlock, flags);
+ spin_lock_bh(&key->u.tkip.txlock);
ieee80211_compute_tkip_p1k(key, iv32);
memcpy(p1k, ctx->p1k, sizeof(ctx->p1k));
- spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
+ spin_unlock_bh(&key->u.tkip.txlock);
}
EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv);
@@ -208,12 +207,11 @@ void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
u32 iv32 = get_unaligned_le32(&data[4]);
u16 iv16 = data[2] | (data[0] << 8);
- unsigned long flags;
- spin_lock_irqsave(&key->u.tkip.txlock, flags);
+ spin_lock_bh(&key->u.tkip.txlock);
ieee80211_compute_tkip_p1k(key, iv32);
tkip_mixing_phase2(tk, ctx, iv16, p2k);
- spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
+ spin_unlock_bh(&key->u.tkip.txlock);
}
EXPORT_SYMBOL(ieee80211_get_tkip_p2k);
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 6ca53d64cb28..1183c4a4fee5 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -36,7 +36,7 @@
__entry->control_freq = (c)->chan->center_freq; \
__entry->chan_width = (c)->width; \
__entry->center_freq1 = (c)->center_freq1; \
- __entry->center_freq1 = (c)->center_freq2;
+ __entry->center_freq2 = (c)->center_freq2;
#define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz"
#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \
__entry->center_freq1, __entry->center_freq2
@@ -340,6 +340,7 @@ TRACE_EVENT(drv_bss_info_changed,
__field(u16, assoc_cap)
__field(u64, sync_tsf)
__field(u32, sync_device_ts)
+ __field(u8, sync_dtim_count)
__field(u32, basic_rates)
__array(int, mcast_rate, IEEE80211_NUM_BANDS)
__field(u16, ht_operation_mode)
@@ -379,6 +380,7 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->assoc_cap = info->assoc_capability;
__entry->sync_tsf = info->sync_tsf;
__entry->sync_device_ts = info->sync_device_ts;
+ __entry->sync_dtim_count = info->sync_dtim_count;
__entry->basic_rates = info->basic_rates;
memcpy(__entry->mcast_rate, info->mcast_rate,
sizeof(__entry->mcast_rate));
@@ -1860,6 +1862,25 @@ TRACE_EVENT(drv_set_default_unicast_key,
LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
);
+TRACE_EVENT(api_radar_detected,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " radar detected",
+ LOCAL_PR_ARG
+ )
+);
+
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index a2cb6a302cc7..fe644f91ae05 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -329,6 +329,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
if (sdata->vif.type == NL80211_IFTYPE_AP)
ps = &sdata->u.ap.ps;
+ else if (ieee80211_vif_is_mesh(&sdata->vif))
+ ps = &sdata->u.mesh.ps;
else
continue;
@@ -372,18 +374,20 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
/*
* broadcast/multicast frame
*
- * If any of the associated stations is in power save mode,
+ * If any of the associated/peer stations is in power save mode,
* the frame is buffered to be sent after DTIM beacon frame.
* This is done either by the hardware or us.
*/
- /* powersaving STAs currently only in AP/VLAN mode */
+ /* powersaving STAs currently only in AP/VLAN/mesh 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 if (ieee80211_vif_is_mesh(&tx->sdata->vif)) {
+ ps = &tx->sdata->u.mesh.ps;
} else {
return TX_CONTINUE;
}
@@ -594,7 +598,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
break;
}
- if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED))
+ if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED &&
+ !ieee80211_is_deauth(hdr->frame_control)))
return TX_DROP;
if (!skip_hw && tx->key &&
@@ -1225,6 +1230,21 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||
(!txpending && !skb_queue_empty(&local->pending[q]))) {
+ if (unlikely(info->flags &
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK &&
+ local->queue_stop_reasons[q] &
+ ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) {
+ /*
+ * Drop off-channel frames if queues are stopped
+ * for any reason other than off-channel
+ * operation. Never queue them.
+ */
+ spin_unlock_irqrestore(
+ &local->queue_stop_reason_lock, flags);
+ ieee80211_purge_tx_queue(&local->hw, skbs);
+ return true;
+ }
+
/*
* Since queue is stopped, queue up frames for later
* transmission from the tx-pending tasklet when the
@@ -1472,12 +1492,14 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
hdr = (struct ieee80211_hdr *) skb->data;
info->control.vif = &sdata->vif;
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- ieee80211_is_data(hdr->frame_control) &&
- !is_multicast_ether_addr(hdr->addr1) &&
- mesh_nexthop_resolve(skb, sdata)) {
- /* skb queued: don't free */
- return;
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ if (ieee80211_is_data(hdr->frame_control) &&
+ is_unicast_ether_addr(hdr->addr1)) {
+ if (mesh_nexthop_resolve(skb, sdata))
+ return; /* skb queued: don't free */
+ } else {
+ ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
+ }
}
ieee80211_set_qos_hdr(sdata, skb);
@@ -2342,11 +2364,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
if (local->tim_in_locked_section) {
__ieee80211_beacon_add_tim(sdata, ps, skb);
} else {
- unsigned long flags;
-
- spin_lock_irqsave(&local->tim_lock, flags);
+ spin_lock(&local->tim_lock);
__ieee80211_beacon_add_tim(sdata, ps, skb);
- spin_unlock_irqrestore(&local->tim_lock, flags);
+ spin_unlock(&local->tim_lock);
}
return 0;
@@ -2424,66 +2444,26 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_BEACON);
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
- struct ieee80211_mgmt *mgmt;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u8 *pos;
- int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
- sizeof(mgmt->u.beacon);
+ struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
-#ifdef CONFIG_MAC80211_MESH
- if (!sdata->u.mesh.mesh_id_len)
+ if (!bcn)
goto out;
-#endif
if (ifmsh->sync_ops)
ifmsh->sync_ops->adjust_tbtt(
sdata);
skb = dev_alloc_skb(local->tx_headroom +
- hdr_len +
- 2 + /* NULL SSID */
- 2 + 8 + /* supported rates */
- 2 + 3 + /* DS params */
- 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
- 2 + sizeof(struct ieee80211_ht_cap) +
- 2 + sizeof(struct ieee80211_ht_operation) +
- 2 + sdata->u.mesh.mesh_id_len +
- 2 + sizeof(struct ieee80211_meshconf_ie) +
- sdata->u.mesh.ie_len);
+ bcn->head_len +
+ 256 + /* TIM IE */
+ bcn->tail_len);
if (!skb)
goto out;
-
- skb_reserve(skb, local->hw.extra_tx_headroom);
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
- memset(mgmt, 0, hdr_len);
- mgmt->frame_control =
- cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
- eth_broadcast_addr(mgmt->da);
- memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
- memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
- mgmt->u.beacon.beacon_int =
- cpu_to_le16(sdata->vif.bss_conf.beacon_int);
- mgmt->u.beacon.capab_info |= cpu_to_le16(
- sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
-
- pos = skb_put(skb, 2);
- *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) ||
- mesh_add_rsn_ie(skb, sdata) ||
- mesh_add_ht_cap_ie(skb, sdata) ||
- mesh_add_ht_oper_ie(skb, sdata) ||
- mesh_add_meshid_ie(skb, sdata) ||
- mesh_add_meshconf_ie(skb, sdata) ||
- mesh_add_vendor_ies(skb, sdata)) {
- pr_err("o11s: couldn't add ies!\n");
- goto out;
- }
+ skb_reserve(skb, local->tx_headroom);
+ memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
+ ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb);
+ memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
} else {
WARN_ON(1);
goto out;
@@ -2733,6 +2713,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
goto out;
ps = &sdata->u.ap.ps;
+ } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ ps = &sdata->u.mesh.ps;
} else {
goto out;
}
@@ -2756,6 +2738,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
+ sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
if (!ieee80211_tx_prepare(sdata, &tx, skb))
break;
dev_kfree_skb_any(skb);
@@ -2788,6 +2771,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
skb_set_queue_mapping(skb, ac);
skb->priority = tid;
+ skb->dev = sdata->dev;
+
/*
* The other path calling ieee80211_xmit is from the tasklet,
* and while we can handle concurrent transmissions locking
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 7519018ff71a..0f38f43ac62e 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -739,11 +739,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
if (calc_crc)
crc = crc32_be(crc, pos - 2, elen + 2);
- if (pos[3] == 1) {
- /* OUI Type 1 - WPA IE */
- elems->wpa = pos;
- elems->wpa_len = elen;
- } else if (elen >= 5 && pos[3] == 2) {
+ if (elen >= 5 && pos[3] == 2) {
/* OUI Type 2 - WMM IE */
if (pos[4] == 0) {
elems->wmm_info = pos;
@@ -791,6 +787,12 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
else
elem_parse_failed = true;
break;
+ case WLAN_EID_OPMODE_NOTIF:
+ if (elen > 0)
+ elems->opmode_notif = pos;
+ else
+ elem_parse_failed = true;
+ break;
case WLAN_EID_MESH_ID:
elems->mesh_id = pos;
elems->mesh_id_len = elen;
@@ -805,6 +807,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
elems->peering = pos;
elems->peering_len = elen;
break;
+ case WLAN_EID_MESH_AWAKE_WINDOW:
+ if (elen >= 2)
+ elems->awake_window = (void *)pos;
+ break;
case WLAN_EID_PREQ:
elems->preq = pos;
elems->preq_len = elen;
@@ -1029,8 +1035,9 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
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)
+ const u8 *extra, size_t extra_len, const u8 *da,
+ const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
+ u32 tx_flags)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -1063,7 +1070,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
WARN_ON(err);
}
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
+ tx_flags;
ieee80211_tx_skb(sdata, skb);
}
@@ -1277,7 +1285,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
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,
+ u32 ratemask, bool directed, u32 tx_flags,
struct ieee80211_channel *channel, bool scan)
{
struct sk_buff *skb;
@@ -1286,9 +1294,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
ssid, ssid_len,
ie, ie_len, directed);
if (skb) {
- if (no_cck)
- IEEE80211_SKB_CB(skb)->flags |=
- IEEE80211_TX_CTL_NO_CCK_RATE;
+ IEEE80211_SKB_CB(skb)->flags |= tx_flags;
if (scan)
ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
else
@@ -1538,6 +1544,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
changed |= BSS_CHANGED_ASSOC |
BSS_CHANGED_ARP_FILTER |
BSS_CHANGED_PS;
+
+ if (sdata->u.mgd.dtim_period)
+ changed |= BSS_CHANGED_DTIM_PERIOD;
+
mutex_lock(&sdata->u.mgd.mtx);
ieee80211_bss_info_change_notify(sdata, changed);
mutex_unlock(&sdata->u.mgd.mtx);
@@ -1937,7 +1947,7 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
}
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
- struct ieee80211_ht_operation *ht_oper,
+ const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef)
{
enum nl80211_channel_type channel_type;
@@ -2125,3 +2135,49 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
return ts;
}
+
+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+
+ if (sdata->wdev.cac_started) {
+ ieee80211_vif_release_channel(sdata);
+ cfg80211_cac_event(sdata->dev,
+ NL80211_RADAR_CAC_ABORTED,
+ GFP_KERNEL);
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+}
+
+void ieee80211_dfs_radar_detected_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, radar_detected_work);
+ struct cfg80211_chan_def chandef;
+
+ ieee80211_dfs_cac_cancel(local);
+
+ if (local->use_chanctx)
+ /* currently not handled */
+ WARN_ON(1);
+ else {
+ cfg80211_chandef_create(&chandef, local->hw.conf.channel,
+ local->hw.conf.channel_type);
+ cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
+ }
+}
+
+void ieee80211_radar_detected(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_radar_detected(local);
+
+ ieee80211_queue_work(hw, &local->radar_detected_work);
+}
+EXPORT_SYMBOL(ieee80211_radar_detected);
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index f311388aeedf..a2c2258bc84e 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -10,21 +10,29 @@
#include <linux/export.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
+#include "rate.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)
+void
+ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_vht_cap *vht_cap_ie,
+ struct sta_info *sta)
{
- if (WARN_ON_ONCE(!vht_cap))
- return;
+ struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap;
memset(vht_cap, 0, sizeof(*vht_cap));
+ if (!sta->sta.ht_cap.ht_supported)
+ return;
+
if (!vht_cap_ie || !sband->vht_cap.vht_supported)
return;
+ /* A VHT STA must support 40 MHz */
+ if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ return;
+
vht_cap->vht_supported = true;
vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
@@ -32,4 +40,156 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
/* 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));
+
+ switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+ case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
+ break;
+ default:
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
+ }
+
+ sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
+}
+
+enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u32 cap = sta->sta.vht_cap.cap;
+ enum ieee80211_sta_rx_bandwidth bw;
+
+ if (!sta->sta.vht_cap.vht_supported) {
+ bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+ IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
+ goto check_max;
+ }
+
+ switch (sdata->vif.bss_conf.chandef.width) {
+ default:
+ WARN_ON_ONCE(1);
+ /* fall through */
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+ IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
+ bw = IEEE80211_STA_RX_BW_160;
+ break;
+ }
+ /* fall through */
+ case NL80211_CHAN_WIDTH_80P80:
+ if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+ bw = IEEE80211_STA_RX_BW_160;
+ break;
+ }
+ /* fall through */
+ case NL80211_CHAN_WIDTH_80:
+ bw = IEEE80211_STA_RX_BW_80;
+ }
+
+ check_max:
+ if (bw > sta->cur_max_bandwidth)
+ bw = sta->cur_max_bandwidth;
+ return bw;
+}
+
+void ieee80211_sta_set_rx_nss(struct sta_info *sta)
+{
+ u8 ht_rx_nss = 0, vht_rx_nss = 0;
+
+ /* if we received a notification already don't overwrite it */
+ if (sta->sta.rx_nss)
+ return;
+
+ if (sta->sta.ht_cap.ht_supported) {
+ if (sta->sta.ht_cap.mcs.rx_mask[0])
+ ht_rx_nss++;
+ if (sta->sta.ht_cap.mcs.rx_mask[1])
+ ht_rx_nss++;
+ if (sta->sta.ht_cap.mcs.rx_mask[2])
+ ht_rx_nss++;
+ if (sta->sta.ht_cap.mcs.rx_mask[3])
+ ht_rx_nss++;
+ /* FIXME: consider rx_highest? */
+ }
+
+ if (sta->sta.vht_cap.vht_supported) {
+ int i;
+ u16 rx_mcs_map;
+
+ rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map);
+
+ for (i = 7; i >= 0; i--) {
+ u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
+
+ if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ vht_rx_nss = i + 1;
+ break;
+ }
+ }
+ /* FIXME: consider rx_highest? */
+ }
+
+ ht_rx_nss = max(ht_rx_nss, vht_rx_nss);
+ sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
+}
+
+void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, u8 opmode,
+ enum ieee80211_band band, bool nss_only)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ enum ieee80211_sta_rx_bandwidth new_bw;
+ u32 changed = 0;
+ u8 nss;
+
+ sband = local->hw.wiphy->bands[band];
+
+ /* ignore - no support for BF yet */
+ if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
+ return;
+
+ nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
+ nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
+ nss += 1;
+
+ if (sta->sta.rx_nss != nss) {
+ sta->sta.rx_nss = nss;
+ changed |= IEEE80211_RC_NSS_CHANGED;
+ }
+
+ if (nss_only)
+ goto change;
+
+ switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
+ case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
+ break;
+ case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
+ break;
+ case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
+ break;
+ case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
+ sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
+ break;
+ }
+
+ new_bw = ieee80211_sta_cur_vht_bw(sta);
+ if (new_bw != sta->sta.bandwidth) {
+ sta->sta.bandwidth = new_bw;
+ changed |= IEEE80211_RC_NSS_CHANGED;
+ }
+
+ change:
+ if (changed)
+ rate_control_rate_update(local, sband, sta, changed);
}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 906f00cd6d2f..afba19cb6f87 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -191,6 +191,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
/* 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 (ieee80211_vif_is_mesh(&sdata->vif)) {
+ /* preserve RSPI and Mesh PS Level bit */
+ *p &= ((IEEE80211_QOS_CTL_RSPI |
+ IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8);
+
+ /* Nulls don't have a mesh header (frame body) */
+ if (!ieee80211_is_qos_nullfunc(hdr->frame_control))
+ *p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8);
+ } else {
+ *p = 0;
+ }
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index c175ee866ff4..c7c6d644486f 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -181,7 +181,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_key *key = tx->key;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- unsigned long flags;
unsigned int hdrlen;
int len, tail;
u8 *pos;
@@ -216,12 +215,12 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0;
/* Increase IV for the frame */
- spin_lock_irqsave(&key->u.tkip.txlock, flags);
+ spin_lock(&key->u.tkip.txlock);
key->u.tkip.tx.iv16++;
if (key->u.tkip.tx.iv16 == 0)
key->u.tkip.tx.iv32++;
pos = ieee80211_tkip_add_iv(pos, key);
- spin_unlock_irqrestore(&key->u.tkip.txlock, flags);
+ spin_unlock(&key->u.tkip.txlock);
/* hwaccel - with software IV */
if (info->control.hw_key)