summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/mac80211/cfg.c248
-rw-r--r--net/mac80211/chan.c152
-rw-r--r--net/mac80211/ibss.c70
-rw-r--r--net/mac80211/ieee80211_i.h74
-rw-r--r--net/mac80211/iface.c28
-rw-r--r--net/mac80211/main.c43
-rw-r--r--net/mac80211/mesh.c41
-rw-r--r--net/mac80211/mesh_plink.c10
-rw-r--r--net/mac80211/mlme.c129
-rw-r--r--net/mac80211/offchannel.c3
-rw-r--r--net/mac80211/pm.c2
-rw-r--r--net/mac80211/rate.h12
-rw-r--r--net/mac80211/scan.c4
-rw-r--r--net/mac80211/sta_info.c12
-rw-r--r--net/mac80211/tx.c183
-rw-r--r--net/mac80211/util.c41
16 files changed, 586 insertions, 466 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 70a5d262815f..09c90627fd19 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -372,10 +372,11 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
{
+ enum ieee80211_band band = ieee80211_get_sdata_band(sta->sdata);
+
if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
struct ieee80211_supported_band *sband;
- sband = sta->local->hw.wiphy->bands[
- sta->local->oper_channel->band];
+ sband = sta->local->hw.wiphy->bands[band];
rate->legacy = sband->bitrates[idx].bitrate;
} else
rate->mcs = idx;
@@ -532,6 +533,8 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
u64 *data)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *channel;
struct sta_info *sta;
struct ieee80211_local *local = sdata->local;
struct station_info sinfo;
@@ -607,19 +610,26 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
do_survey:
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
/* Get survey stats for current channel */
- q = 0;
- while (true) {
- survey.filled = 0;
- if (drv_get_survey(local, q, &survey) != 0) {
- survey.filled = 0;
- break;
- }
+ survey.filled = 0;
- if (survey.channel &&
- (local->oper_channel->center_freq ==
- survey.channel->center_freq))
- break;
- q++;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (chanctx_conf)
+ channel = chanctx_conf->channel;
+ else
+ channel = NULL;
+ rcu_read_unlock();
+
+ if (channel) {
+ q = 0;
+ do {
+ survey.filled = 0;
+ if (drv_get_survey(local, q, &survey) != 0) {
+ survey.filled = 0;
+ break;
+ }
+ q++;
+ } while (channel != survey.channel);
}
if (survey.filled)
@@ -724,47 +734,42 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
-static int ieee80211_set_channel(struct wiphy *wiphy,
- struct net_device *netdev,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = NULL;
-
- if (netdev)
- sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
-
- switch (ieee80211_get_channel_mode(local, NULL)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (local->oper_channel != chan ||
- (!sdata && local->_oper_channel_type != channel_type))
- return -EBUSY;
- if (!sdata && local->_oper_channel_type == channel_type)
- return 0;
- break;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
- if (!ieee80211_set_channel_type(local, sdata, channel_type))
- return -EBUSY;
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 0;
- local->oper_channel = chan;
+ if (local->monitor_channel == chan &&
+ local->monitor_channel_type == channel_type)
+ return 0;
- /* auto-detects changes */
- ieee80211_hw_config(local, 0);
+ mutex_lock(&local->iflist_mtx);
+ if (local->use_chanctx) {
+ sdata = rcu_dereference_protected(
+ local->monitor_sdata,
+ lockdep_is_held(&local->iflist_mtx));
+ if (sdata) {
+ ieee80211_vif_release_channel(sdata);
+ ret = ieee80211_vif_use_channel(
+ sdata, chan, channel_type,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ }
+ } else if (local->open_count == local->monitors) {
+ local->_oper_channel = chan;
+ local->_oper_channel_type = channel_type;
+ ieee80211_hw_config(local, 0);
+ }
- return 0;
-}
+ if (ret == 0) {
+ local->monitor_channel = chan;
+ local->monitor_channel_type = channel_type;
+ }
+ mutex_unlock(&local->iflist_mtx);
-static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
-{
- return ieee80211_set_channel(wiphy, NULL, chan, channel_type);
+ return ret;
}
static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
@@ -879,8 +884,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
if (old)
return -EALREADY;
- err = ieee80211_set_channel(wiphy, dev, params->channel,
- params->channel_type);
+ err = ieee80211_vif_use_channel(sdata, params->channel,
+ params->channel_type,
+ IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -963,6 +969,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
sta_info_flush(sdata->local, sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
+ ieee80211_vif_release_channel(sdata);
+
return 0;
}
@@ -1019,9 +1027,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
int i, j;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
u32 mask, set;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[band];
mask = params->sta_flags_mask;
set = params->sta_flags_set;
@@ -1136,7 +1145,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
rates |= BIT(j);
}
}
- sta->sta.supp_rates[local->oper_channel->band] = rates;
+ sta->sta.supp_rates[band] = rates;
}
if (params->ht_capa)
@@ -1664,8 +1673,9 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
if (err)
return err;
- err = ieee80211_set_channel(wiphy, dev, setup->channel,
- setup->channel_type);
+ err = ieee80211_vif_use_channel(sdata, setup->channel,
+ setup->channel_type,
+ IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -1679,6 +1689,7 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
ieee80211_stop_mesh(sdata);
+ ieee80211_vif_release_channel(sdata);
return 0;
}
@@ -1688,10 +1699,14 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ enum ieee80211_band band;
u32 changed = 0;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (!rtnl_dereference(sdata->u.ap.beacon))
+ return -ENOENT;
+
+ band = ieee80211_get_sdata_band(sdata);
if (params->use_cts_prot >= 0) {
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
@@ -1704,7 +1719,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
}
if (!sdata->vif.bss_conf.use_short_slot &&
- sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) {
+ band == IEEE80211_BAND_5GHZ) {
sdata->vif.bss_conf.use_short_slot = true;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -1718,9 +1733,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
if (params->basic_rates) {
int i, j;
u32 rates = 0;
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_supported_band *sband =
- wiphy->bands[local->oper_channel->band];
+ struct ieee80211_supported_band *sband = wiphy->bands[band];
for (i = 0; i < params->basic_rates_len; i++) {
int rate = (params->basic_rates[i] & 0x7f) * 5;
@@ -1872,20 +1885,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (ieee80211_get_channel_mode(local, sdata)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (local->oper_channel == req->bss->channel)
- break;
- return -EBUSY;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
}
@@ -1904,30 +1903,12 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (ieee80211_get_channel_mode(local, sdata)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (!params->channel_fixed)
- return -EBUSY;
- if (local->oper_channel == params->channel)
- break;
- return -EBUSY;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
- return ieee80211_ibss_join(sdata, params);
+ return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
}
static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- return ieee80211_ibss_leave(sdata);
+ return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
}
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
@@ -1971,9 +1952,13 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
enum nl80211_tx_power_setting type, int mbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_channel *chan = local->oper_channel;
+ struct ieee80211_channel *chan = local->_oper_channel;
u32 changes = 0;
+ /* FIXME */
+ if (local->use_chanctx)
+ return -EOPNOTSUPP;
+
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
local->user_power_level = -1;
@@ -2518,10 +2503,20 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
/* Check if the operating channel is the requested channel */
if (!need_offchan) {
- need_offchan = chan != local->oper_channel;
- if (channel_type_valid &&
- channel_type != local->_oper_channel_type)
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf) {
+ need_offchan = chan != chanctx_conf->channel;
+ if (channel_type_valid &&
+ channel_type != chanctx_conf->channel_type)
+ need_offchan = true;
+ } else {
need_offchan = true;
+ }
+ rcu_read_unlock();
}
if (need_offchan && !offchan) {
@@ -2670,7 +2665,7 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
u16 capab;
capab = 0;
- if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
+ if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
return capab;
if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
@@ -2702,7 +2697,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_tdls_data *tf;
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -2722,10 +2717,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_req.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_RESPONSE:
@@ -2738,10 +2731,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_CONFIRM:
@@ -2779,7 +2770,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_mgmt *mgmt;
mgmt = (void *)skb_put(skb, 24);
@@ -2802,10 +2793,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt->u.action.u.tdls_discover_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
default:
@@ -2985,12 +2974,19 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
bool qos;
struct ieee80211_tx_info *info;
struct sta_info *sta;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_band band;
rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ band = chanctx_conf->channel->band;
sta = sta_info_get(sdata, peer);
if (sta) {
qos = test_sta_flag(sta, WLAN_STA_WME);
- rcu_read_unlock();
} else {
rcu_read_unlock();
return -ENOLINK;
@@ -3008,8 +3004,10 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
- if (!skb)
+ if (!skb) {
+ rcu_read_unlock();
return -ENOMEM;
+ }
skb->dev = dev;
@@ -3034,8 +3032,9 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
local_bh_enable();
+ rcu_read_unlock();
*cookie = (unsigned long) skb;
return 0;
@@ -3045,10 +3044,19 @@ static struct ieee80211_channel *
ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
enum nl80211_channel_type *type)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan = NULL;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (chanctx_conf) {
+ *type = chanctx_conf->channel_type;
+ chan = chanctx_conf->channel;
+ }
+ rcu_read_unlock();
- *type = local->_oper_channel_type;
- return local->oper_channel;
+ return chan;
}
#ifdef CONFIG_PM
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 1a8dee42e546..41e1aa69f7aa 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -7,106 +7,6 @@
#include "ieee80211_i.h"
#include "driver-ops.h"
-static enum ieee80211_chan_mode
-__ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore)
-{
- struct ieee80211_sub_if_data *sdata;
-
- lockdep_assert_held(&local->iflist_mtx);
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata == ignore)
- continue;
-
- if (!ieee80211_sdata_running(sdata))
- continue;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
- continue;
- case NL80211_IFTYPE_STATION:
- if (!sdata->u.mgd.associated)
- continue;
- break;
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->u.ibss.ssid_len)
- continue;
- if (!sdata->u.ibss.fixed_channel)
- return CHAN_MODE_HOPPING;
- break;
- case NL80211_IFTYPE_AP_VLAN:
- /* will also have _AP interface */
- continue;
- case NL80211_IFTYPE_AP:
- if (!sdata->u.ap.beacon)
- continue;
- break;
- case NL80211_IFTYPE_MESH_POINT:
- if (!sdata->wdev.mesh_id_len)
- continue;
- break;
- default:
- break;
- }
-
- return CHAN_MODE_FIXED;
- }
-
- return CHAN_MODE_UNDEFINED;
-}
-
-enum ieee80211_chan_mode
-ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore)
-{
- enum ieee80211_chan_mode mode;
-
- mutex_lock(&local->iflist_mtx);
- mode = __ieee80211_get_channel_mode(local, ignore);
- mutex_unlock(&local->iflist_mtx);
-
- return mode;
-}
-
-static enum nl80211_channel_type
-ieee80211_get_superchan(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
-{
- enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
- struct ieee80211_sub_if_data *tmp;
-
- mutex_lock(&local->iflist_mtx);
- list_for_each_entry(tmp, &local->interfaces, list) {
- if (tmp == sdata)
- continue;
-
- if (!ieee80211_sdata_running(tmp))
- continue;
-
- switch (tmp->vif.bss_conf.channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
- if (superchan > tmp->vif.bss_conf.channel_type)
- break;
-
- superchan = tmp->vif.bss_conf.channel_type;
- break;
- case NL80211_CHAN_HT40PLUS:
- WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
- superchan = NL80211_CHAN_HT40PLUS;
- break;
- case NL80211_CHAN_HT40MINUS:
- WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
- superchan = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
- mutex_unlock(&local->iflist_mtx);
-
- return superchan;
-}
-
static bool
ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
enum nl80211_channel_type chantype2,
@@ -149,26 +49,6 @@ ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
return true;
}
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype)
-{
- enum nl80211_channel_type superchan;
- enum nl80211_channel_type compatchan;
-
- superchan = ieee80211_get_superchan(local, sdata);
- if (!ieee80211_channel_types_are_compatible(superchan, chantype,
- &compatchan))
- return false;
-
- local->_oper_channel_type = compatchan;
-
- if (sdata)
- sdata->vif.bss_conf.channel_type = chantype;
-
- return true;
-}
-
static void ieee80211_change_chantype(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
enum nl80211_channel_type chantype)
@@ -178,6 +58,11 @@ static void ieee80211_change_chantype(struct ieee80211_local *local,
ctx->conf.channel_type = chantype;
drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = chantype;
+ ieee80211_hw_config(local, 0);
+ }
}
static struct ieee80211_chanctx *
@@ -235,10 +120,16 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
ctx->conf.channel_type = channel_type;
ctx->mode = mode;
- err = drv_add_chanctx(local, ctx);
- if (err) {
- kfree(ctx);
- return ERR_PTR(err);
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = channel_type;
+ local->_oper_channel = channel;
+ ieee80211_hw_config(local, 0);
+ } else {
+ err = drv_add_chanctx(local, ctx);
+ if (err) {
+ kfree(ctx);
+ return ERR_PTR(err);
+ }
}
list_add(&ctx->list, &local->chanctx_list);
@@ -253,7 +144,12 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
WARN_ON_ONCE(ctx->refcount != 0);
- drv_remove_chanctx(local, ctx);
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = NL80211_CHAN_NO_HT;
+ ieee80211_hw_config(local, 0);
+ } else {
+ drv_remove_chanctx(local, ctx);
+ }
list_del(&ctx->list);
kfree_rcu(ctx, rcu_head);
@@ -359,6 +255,8 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx *ctx;
int ret;
+ WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
+
mutex_lock(&local->chanctx_mtx);
__ieee80211_vif_release_channel(sdata);
@@ -370,6 +268,8 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
goto out;
}
+ sdata->vif.bss_conf.channel_type = channel_type;
+
ret = ieee80211_assign_vif_chanctx(sdata, ctx);
if (ret) {
/* if assign fails refcount stays the same */
@@ -385,6 +285,8 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
{
+ WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
+
mutex_lock(&sdata->local->chanctx_mtx);
__ieee80211_vif_release_channel(sdata);
mutex_unlock(&sdata->local->chanctx_mtx);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 5f3620f0bc0a..34d9235117d9 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -26,7 +26,6 @@
#include "rate.h"
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
@@ -76,21 +75,22 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
}
- memcpy(ifibss->bssid, bssid, ETH_ALEN);
-
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- local->oper_channel = chan;
channel_type = ifibss->channel_type;
if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
channel_type = NL80211_CHAN_HT20;
- if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- NL80211_CHAN_HT20));
+
+ ieee80211_vif_release_channel(sdata);
+ if (ieee80211_vif_use_channel(sdata, chan, channel_type,
+ ifibss->fixed_channel ?
+ IEEE80211_CHANCTX_SHARED :
+ IEEE80211_CHANCTX_EXCLUSIVE)) {
+ sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+ return;
}
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ memcpy(ifibss->bssid, bssid, ETH_ALEN);
sband = local->hw.wiphy->bands[chan->band];
@@ -294,7 +294,8 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int band = local->oper_channel->band;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int band;
/*
* XXX: Consider removing the least recently used entry and
@@ -317,6 +318,13 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return NULL;
}
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf))
+ return NULL;
+ band = chanctx_conf->channel->band;
+ rcu_read_unlock();
+
sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
if (!sta) {
rcu_read_lock();
@@ -517,7 +525,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
goto put_bss;
/* different channel */
- if (cbss->channel != local->oper_channel)
+ if (sdata->u.ibss.fixed_channel &&
+ sdata->u.ibss.channel != cbss->channel)
goto put_bss;
/* different SSID */
@@ -592,7 +601,8 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int band = local->oper_channel->band;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int band;
/*
* XXX: Consider removing the least recently used entry and
@@ -610,6 +620,15 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
if (!ether_addr_equal(bssid, sdata->u.ibss.bssid))
return;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ band = chanctx_conf->channel->band;
+ rcu_read_unlock();
+
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
if (!sta)
return;
@@ -784,18 +803,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
int interval = IEEE80211_SCAN_INTERVAL;
if (time_after(jiffies, ifibss->ibss_join_req +
- IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) {
- ieee80211_sta_create_ibss(sdata);
- return;
- }
- sdata_info(sdata, "IBSS not allowed on %d MHz\n",
- local->oper_channel->center_freq);
-
- /* No IBSS found - decrease scan interval and continue
- * scanning. */
- interval = IEEE80211_SCAN_INTERVAL_SLOW;
- }
+ IEEE80211_IBSS_JOIN_TIMEOUT))
+ ieee80211_sta_create_ibss(sdata);
mod_timer(&ifibss->timer,
round_jiffies(jiffies + interval));
@@ -1086,17 +1095,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.channel_type = params->channel_type;
sdata->u.ibss.fixed_channel = params->channel_fixed;
- /* fix ourselves to that channel now already */
- if (params->channel_fixed) {
- sdata->local->oper_channel = params->channel;
- if (!ieee80211_set_channel_type(sdata->local, sdata,
- params->channel_type)) {
- mutex_unlock(&sdata->u.ibss.mtx);
- kfree_skb(skb);
- return -EINVAL;
- }
- }
-
if (params->ie) {
sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
GFP_KERNEL);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8fa00adcb8c0..6660118b46b3 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -773,6 +773,21 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
return container_of(p, struct ieee80211_sub_if_data, vif);
}
+static inline enum ieee80211_band
+ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
+{
+ enum ieee80211_band band = IEEE80211_BAND_2GHZ;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!WARN_ON(!chanctx_conf))
+ band = chanctx_conf->channel->band;
+ rcu_read_unlock();
+
+ return band;
+}
+
enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
IEEE80211_SDATA_QUEUE_AGG_START = 1,
@@ -1006,8 +1021,10 @@ struct ieee80211_local {
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data __rcu *scan_sdata;
+ struct ieee80211_channel *csa_channel;
+ /* For backward compatibility only -- do not use */
+ struct ieee80211_channel *_oper_channel;
enum nl80211_channel_type _oper_channel_type;
- struct ieee80211_channel *oper_channel, *csa_channel;
/* Temporary remain-on-channel for off-channel operations */
struct ieee80211_channel *tmp_channel;
@@ -1121,6 +1138,8 @@ struct ieee80211_local {
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
+ struct ieee80211_channel *monitor_channel;
+ enum nl80211_channel_type monitor_channel_type;
};
static inline struct ieee80211_sub_if_data *
@@ -1423,11 +1442,42 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ enum ieee80211_band band);
+
+void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band);
+
+static inline void
+ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band)
+{
+ rcu_read_lock();
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid, band);
+ rcu_read_unlock();
+}
-void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid);
-static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
+static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid,
+ chanctx_conf->channel->band);
+ rcu_read_unlock();
+}
+
+static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
@@ -1494,7 +1544,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, bool no_cck,
- struct ieee80211_channel *channel);
+ struct ieee80211_channel *channel, bool scan);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
@@ -1525,18 +1575,6 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band);
/* channel management */
-enum ieee80211_chan_mode {
- CHAN_MODE_UNDEFINED,
- CHAN_MODE_HOPPING,
- CHAN_MODE_FIXED,
-};
-
-enum ieee80211_chan_mode
-ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore);
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype);
enum nl80211_channel_type
ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 6f8a73c64fb3..7cb8382b19e5 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -380,6 +380,15 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
goto out_unlock;
}
+ ret = ieee80211_vif_use_channel(sdata, local->monitor_channel,
+ local->monitor_channel_type,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ if (ret) {
+ drv_remove_interface(local, sdata);
+ kfree(sdata);
+ goto out_unlock;
+ }
+
rcu_assign_pointer(local->monitor_sdata, sdata);
out_unlock:
mutex_unlock(&local->iflist_mtx);
@@ -403,6 +412,8 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
rcu_assign_pointer(local->monitor_sdata, NULL);
synchronize_net();
+ ieee80211_vif_release_channel(sdata);
+
drv_remove_interface(local, sdata);
kfree(sdata);
@@ -665,7 +676,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
int i;
- enum nl80211_channel_type orig_ct;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -837,14 +847,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
hw_reconf_flags = 0;
}
- /* Re-calculate channel-type, in case there are multiple vifs
- * on different channel types.
- */
- orig_ct = local->_oper_channel_type;
- ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT);
-
/* do after stop to avoid reconfiguring when we stop anyway */
- if (hw_reconf_flags || (orig_ct != local->_oper_channel_type))
+ if (hw_reconf_flags)
ieee80211_hw_config(local, hw_reconf_fla