Commit 55de908a authored by Johannes Berg's avatar Johannes Berg

mac80211: use channel contexts

Instead of operating on a single channel only,
use the new channel context infrastructure in
all mac80211 code.

This enables drivers that want to use the new
channel context infrastructure to use multiple
channels, while nothing should change for all
the other drivers that don't support it.

Right now this disables both TX power settings
and spatial multiplexing powersave. Both need
to be re-enabled on a channel context basis.

Additionally, when channel contexts are used
drop the connection when channel switch is
received rather than trying to handle it. This
will have to be improved later.

[With fixes from Eliad and Emmanuel incorporated]
Signed-off-by: default avatarEliad Peller <eliad@wizery.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent fe57d9f5
This diff is collapsed.
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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_flags);
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
......@@ -1282,11 +1286,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
if (type == ieee80211_vif_type_p2p(&sdata->vif))
return 0;
/* Setting ad-hoc mode on non-IBSS channel is not supported. */
if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
type == NL80211_IFTYPE_ADHOC)
return -EOPNOTSUPP;
if (ieee80211_sdata_running(sdata)) {
ret = ieee80211_runtime_change_iftype(sdata, type);
if (ret)
......@@ -1298,9 +1297,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
}
/* reset some values that shouldn't be kept across type changes */
sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
sdata->local->oper_channel->band);
sdata->drop_unencrypted = 0;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false;
......
......@@ -93,23 +93,21 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
ieee80211_configure_filter(local);
}
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
{
struct ieee80211_channel *chan;
int ret = 0;
u32 changed = 0;
int power;
enum nl80211_channel_type channel_type;
u32 offchannel_flag;
might_sleep();
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) {
chan = local->scan_channel;
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
if (chan == local->oper_channel)
if (chan == local->_oper_channel)
channel_type = local->_oper_channel_type;
else
channel_type = NL80211_CHAN_NO_HT;
......@@ -117,11 +115,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
chan = local->tmp_channel;
channel_type = local->tmp_channel_type;
} else {
chan = local->oper_channel;
chan = local->_oper_channel;
channel_type = local->_oper_channel_type;
}
if (chan != local->oper_channel ||
if (chan != local->_oper_channel ||
channel_type != local->_oper_channel_type)
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
else
......@@ -164,6 +162,21 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
local->hw.conf.power_level = power;
}
return changed;
}
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
int ret = 0;
might_sleep();
if (!local->use_chanctx)
changed |= ieee80211_hw_conf_chan(local);
else
changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL |
IEEE80211_CONF_CHANGE_POWER);
if (changed && local->open_count) {
ret = drv_config(local, changed);
/*
......@@ -775,12 +788,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
if (!local->oper_channel) {
if (!local->use_chanctx && !local->_oper_channel) {
/* init channel we're on */
local->hw.conf.channel =
local->oper_channel = &sband->channels[0];
local->_oper_channel = &sband->channels[0];
local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
}
if (!local->monitor_channel) {
local->monitor_channel = &sband->channels[0];
local->monitor_channel_type = NL80211_CHAN_NO_HT;
}
channels += sband->n_channels;
if (max_bitrates < sband->n_bitrates)
......@@ -810,19 +827,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
/*
* mac80211 doesn't support more than 1 channel, and also not more
* than one IBSS interface
*/
/* mac80211 doesn't support more than one IBSS interface right now */
for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *c;
int j;
c = &hw->wiphy->iface_combinations[i];
if (c->num_different_channels > 1)
return -EINVAL;
for (j = 0; j < c->n_limits; j++)
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
c->limits[j].max > 1)
......
......@@ -97,7 +97,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
goto mismatch;
ieee80211_sta_get_rates(local, ie, local->oper_channel->band,
ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
&basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates)
......@@ -355,12 +355,22 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan = local->oper_channel;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
u8 *pos;
if (skb_tailroom(skb) < 3)
return -ENOMEM;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {