This implements a fairly simple multi-interface
CSA. It doesn't support multiple channel
contexts so it doesn't support multi-channel.
Once a CSA is started other CSA requests are
denied until the first one is completed. A single
CSA may affect multiple interfaces. CSA can happen
only if it all target CSA chandefs are compatible
and it affects all interfaces are sharing a single
channel context exclusively.
A new worker is introduced: csa_complete_work
which is used to account per-interface countdowns
and issue the actual channel switch after last
interface completes its CSA.
Signed-off-by: Michal Kazior <michal.kazior-++***@public.gmane.org>
---
net/mac80211/cfg.c | 364 ++++++++++++++++++++++++++++++++++++---------
net/mac80211/chan.c | 117 +++++++++++----
net/mac80211/ibss.c | 2 +-
net/mac80211/ieee80211_i.h | 23 ++-
net/mac80211/iface.c | 5 +-
net/mac80211/mlme.c | 9 +-
net/mac80211/tx.c | 10 +-
7 files changed, 419 insertions(+), 111 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 13d1624..873c57d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1093,9 +1093,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
/* abort any running channel switch */
mutex_lock(&local->mtx);
- sdata->vif.csa_active = false;
- kfree(sdata->u.ap.next_beacon);
- sdata->u.ap.next_beacon = NULL;
+ ieee80211_csa_clear(sdata);
+ ieee80211_csa_free(sdata);
mutex_unlock(&local->mtx);
cancel_work_sync(&sdata->u.ap.request_smps_work);
@@ -3001,15 +3000,79 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
return new_beacon;
}
+static int ieee80211_ap_beacon_presp_backup(struct ieee80211_sub_if_data *sdata)
+{
+ struct beacon_data *beacon;
+ struct probe_resp *probe_resp;
+
+ beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
+ if (beacon) {
+ sdata->u.ap.prev_beacon = kmemdup(beacon, sizeof(beacon) +
+ beacon->head_len +
+ beacon->tail_len, GFP_KERNEL);
+ if (!sdata->u.ap.prev_beacon)
+ return -ENOMEM;
+ }
+
+ probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
+ if (probe_resp) {
+ sdata->u.ap.prev_presp = kmemdup(probe_resp,
+ sizeof(probe_resp) +
+ probe_resp->len, GFP_KERNEL);
+ if (!sdata->u.ap.prev_presp) {
+ kfree(sdata->u.ap.prev_beacon);
+ sdata->u.ap.prev_beacon = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int ieee80211_ap_beacon_presp_restore(struct ieee80211_sub_if_data *sdata)
+{
+ struct beacon_data *beacon;
+ struct probe_resp *probe_resp;
+ int changed = 0;
+
+ if (sdata->u.ap.prev_beacon) {
+ beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
+ rcu_assign_pointer(sdata->u.ap.beacon, sdata->u.ap.prev_beacon);
+ if (beacon)
+ kfree_rcu(beacon, rcu_head);
+ sdata->u.ap.prev_beacon = NULL;
+ changed |= BSS_CHANGED_BEACON;
+ }
+
+ if (sdata->u.ap.prev_presp) {
+ probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
+ rcu_assign_pointer(sdata->u.ap.probe_resp, sdata->u.ap.prev_presp);
+ if (probe_resp)
+ kfree_rcu(probe_resp, rcu_head);
+ sdata->u.ap.prev_presp = NULL;
+ changed |= BSS_CHANGED_AP_PROBE_RESP;
+ }
+
+ return changed;
+}
+
static int ieee80211_ap_finish_csa(struct ieee80211_sub_if_data *sdata)
{
- int err;
+ int err = 0;
lockdep_assert_held(&sdata->local->mtx);
- err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+ if (sdata->u.ap.next_beacon)
+ err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+ else
+ err = ieee80211_ap_beacon_presp_restore(sdata);
+
kfree(sdata->u.ap.next_beacon);
+ kfree(sdata->u.ap.prev_beacon);
+ kfree(sdata->u.ap.prev_presp);
sdata->u.ap.next_beacon = NULL;
+ sdata->u.ap.prev_beacon = NULL;
+ sdata->u.ap.prev_presp = NULL;
if (err < 0)
return err;
@@ -3018,88 +3081,227 @@ static int ieee80211_ap_finish_csa(struct ieee80211_sub_if_data *sdata)
return 0;
}
-void ieee80211_csa_finalize_work(struct work_struct *work)
+void ieee80211_csa_clear(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data,
- csa_finalize_work);
struct ieee80211_local *local = sdata->local;
- int err, changed = 0;
- sdata_lock(sdata);
- mutex_lock(&local->mtx);
- /* AP might have been stopped while waiting for the lock. */
- if (!sdata->vif.csa_active)
- goto unlock;
+ sdata_assert_lock(sdata);
+ lockdep_assert_held(&local->mtx);
- if (!ieee80211_sdata_running(sdata))
- goto unlock;
+ sdata->vif.csa_active = false;
+ sdata->csa_complete = false;
+
+ /* unblock queues when last CSA interface is cleared (either finalizes
+ * or is cancelled) */
+ if (!ieee80211_is_csa_active(local))
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+}
- sdata->radar_required = sdata->csa_radar_required;
+void ieee80211_csa_free(struct ieee80211_sub_if_data *sdata)
+{
+ sdata_assert_lock(sdata);
+ lockdep_assert_held(&sdata->local->mtx);
- err = ieee80211_vif_change_channel(sdata, &changed);
- if (WARN_ON(err < 0))
- goto unlock;
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return;
- if (!local->use_chanctx) {
- local->_oper_chandef = sdata->csa_chandef;
- ieee80211_hw_config(local, 0);
- }
+ kfree(sdata->u.ap.next_beacon);
+ kfree(sdata->u.ap.prev_beacon);
+ kfree(sdata->u.ap.prev_presp);
+ sdata->u.ap.next_beacon = NULL;
+ sdata->u.ap.prev_beacon = NULL;
+ sdata->u.ap.prev_presp = NULL;
+}
- ieee80211_bss_info_change_notify(sdata, changed);
+static int ieee80211_csa_finish_beacon(struct ieee80211_sub_if_data *sdata)
+{
+ int err;
+
+ sdata_assert_lock(sdata);
+ lockdep_assert_held(&sdata->local->mtx);
- sdata->vif.csa_active = false;
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
err = ieee80211_ap_finish_csa(sdata);
if (err < 0)
- goto unlock;
+ return err;
break;
case NL80211_IFTYPE_ADHOC:
err = ieee80211_ibss_finish_csa(sdata);
if (err < 0)
- goto unlock;
+ return err;
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
err = ieee80211_mesh_finish_csa(sdata);
if (err < 0)
- goto unlock;
+ return err;
break;
#endif
default:
WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ csa_finalize_work);
+ struct ieee80211_local *local = sdata->local;
+ int err, changed = 0;
+
+ sdata_lock(sdata);
+ mutex_lock(&local->mtx);
+
+ if (!ieee80211_sdata_running(sdata))
+ goto unlock;
+
+ if (!sdata->vif.csa_active)
+ goto unlock;
+
+ if (sdata->vif.bss_conf.chandef.width != sdata->csa_chandef.width)
+ changed |= BSS_CHANGED_BANDWIDTH;
+
+ /* channel switch is called for each sdata csa is being performed, but
+ * this shouldn't be a problem */
+ mutex_lock(&local->chanctx_mtx);
+ err = ieee80211_chanctx_chswitch(local);
+ mutex_unlock(&local->chanctx_mtx);
+
+ if (WARN_ON(err < 0))
goto unlock;
+
+ if (!local->use_chanctx) {
+ local->_oper_chandef = sdata->csa_chandef;
+ ieee80211_hw_config(local, 0);
}
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
- IEEE80211_MAX_QUEUE_MAP,
- IEEE80211_QUEUE_STOP_REASON_CSA);
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ err = ieee80211_csa_finish_beacon(sdata);
+ if (err)
+ goto unlock;
cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
unlock:
+ ieee80211_csa_clear(sdata);
+
mutex_unlock(&local->mtx);
sdata_unlock(sdata);
}
+static bool ieee80211_is_csa_complete(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+ int num_active = 0;
+ int num_complete = 0;
+
+ lockdep_assert_held(&local->mtx);
+ lockdep_assert_held(&local->iflist_mtx);
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.csa_active)
+ num_active++;
+ if (sdata->csa_complete)
+ num_complete++;
+ }
+
+ if (num_active == 0)
+ return false;
+ if (num_active != num_complete)
+ return false;
+
+ return true;
+}
+
+static void ieee80211_queue_csa_finalize(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ lockdep_assert_held(&local->mtx);
+ lockdep_assert_held(&local->iflist_mtx);
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+
+ if (!sdata->vif.csa_active)
+ continue;
+
+ ieee80211_queue_work(&local->hw, &sdata->csa_finalize_work);
+ }
+}
+
+void ieee80211_csa_complete_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ csa_complete_work);
+ struct ieee80211_local *local = sdata->local;
+
+ mutex_lock(&local->mtx);
+ mutex_lock(&local->iflist_mtx);
+
+ if (sdata->vif.csa_active)
+ sdata->csa_complete = true;
+
+ if (ieee80211_is_csa_complete(sdata->local))
+ ieee80211_queue_csa_finalize(sdata->local);
+
+ mutex_unlock(&local->iflist_mtx);
+ mutex_unlock(&local->mtx);
+}
+
+static void ieee80211_channel_switch_abort(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh __maybe_unused *ifmsh;
+
+ sdata_assert_lock(sdata);
+ lockdep_assert_held(&local->mtx);
+
+ ieee80211_csa_clear(sdata);
+
+ /* force to switch to previous AP beacon */
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+
+ ieee80211_csa_finish_beacon(sdata);
+}
+
int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_csa_settings *params)
+ struct cfg80211_csa_settings *params,
+ bool first)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_chanctx *chanctx;
struct ieee80211_if_mesh __maybe_unused *ifmsh;
- int err, num_chanctx;
+ int err;
sdata_assert_lock(sdata);
lockdep_assert_held(&local->mtx);
- if (!list_empty(&local->roc_list) || local->scanning ||
- ieee80211_is_csa_active(local))
+ /* only first csa call-in should check this, otherwise second csa for a
+ * multi-interface csa would always fail */
+ if (first && (!list_empty(&local->roc_list) ||
+ local->scanning ||
+ ieee80211_is_csa_active(local)))
return -EBUSY;
+ if (!ieee80211_sdata_running(sdata))
+ return -ENETDOWN;
+
if (sdata->wdev.cac_started)
return -EBUSY;
@@ -3108,24 +3310,10 @@ int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
return -EINVAL;
mutex_lock(&local->chanctx_mtx);
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf) {
- mutex_unlock(&local->chanctx_mtx);
- return -EBUSY;
- }
-
- /* don't handle for multi-VIF cases */
- chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
- if (chanctx->refcount > 1) {
- mutex_unlock(&local->chanctx_mtx);
- return -EBUSY;
- }
- num_chanctx = 0;
- list_for_each_entry(chanctx, &local->chanctx_list, list)
- num_chanctx++;
+ chanctx = ieee80211_get_csa_chanctx(local);
mutex_unlock(&local->chanctx_mtx);
- if (num_chanctx > 1)
+ if (!chanctx)
return -EBUSY;
/* don't allow another channel switch if one is already active. */
@@ -3142,9 +3330,15 @@ int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
if (!sdata->u.ap.next_beacon)
return -ENOMEM;
+ err = ieee80211_ap_beacon_presp_backup(sdata);
+ if (err) {
+ ieee80211_csa_free(sdata);
+ return -ENOMEM;
+ }
+
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
if (err < 0) {
- kfree(sdata->u.ap.next_beacon);
+ ieee80211_csa_free(sdata);
return err;
}
break;
@@ -3226,26 +3420,62 @@ int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static int ieee80211_csa_allowed_settings(struct cfg80211_csa_settings *params,
+ int num_params)
+{
+ const struct cfg80211_chan_def *chandef;
+ int i;
+
+ if (num_params == 0)
+ return -EINVAL;
+
+ chandef = ¶ms[0].chandef;
+ for (i = 1; i < num_params; i++) {
+ chandef = cfg80211_chandef_compatible(chandef,
+ ¶ms[i].chandef);
+ if (!chandef)
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
int ieee80211_channel_switch(struct wiphy *wiphy,
struct cfg80211_csa_settings *params,
int num_params)
{
struct ieee80211_sub_if_data *sdata;
- int err;
-
- /* multi-vif CSA is not implemented */
- if (num_params > 1)
- return -EOPNOTSUPP;
+ int err, i;
- sdata = IEEE80211_DEV_TO_SUB_IF(params[0].dev);
+ err = ieee80211_csa_allowed_settings(params, num_params);
+ if (err)
+ return err;
- sdata_lock(sdata);
- mutex_lock(&sdata->local->mtx);
- err = __ieee80211_channel_switch(wiphy, params[0].dev, ¶ms[0]);
- mutex_unlock(&sdata->local->mtx);
- sdata_unlock(sdata);
+ for (i = 0; i < num_params; i++) {
+ sdata = IEEE80211_DEV_TO_SUB_IF(params[i].dev);
+
+ sdata_lock(sdata);
+ mutex_lock(&sdata->local->mtx);
+ err = __ieee80211_channel_switch(wiphy, params[i].dev,
+ ¶ms[i], i == 0);
+ mutex_unlock(&sdata->local->mtx);
+ sdata_unlock(sdata);
+
+ if (err) {
+ for (i--; i >= 0; i--) {
+ sdata_lock(sdata);
+ mutex_lock(&sdata->local->mtx);
+ ieee80211_channel_switch_abort(wiphy,
+ params[i].dev,
+ ¶ms[i]);
+ mutex_unlock(&sdata->local->mtx);
+ sdata_unlock(sdata);
+ }
+ return err;
+ }
+ }
- return err;
+ return 0;
}
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index f43613a..71e3e18 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -545,49 +545,103 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
return ret;
}
-int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
- u32 *changed)
+const struct cfg80211_chan_def *
+ieee80211_get_csa_chandef(struct ieee80211_local *local)
{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_chanctx_conf *conf;
- struct ieee80211_chanctx *ctx;
- const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
- int ret;
- u32 chanctx_changed = 0;
+ struct ieee80211_sub_if_data *sdata;
+ const struct cfg80211_chan_def *chandef = NULL;
lockdep_assert_held(&local->mtx);
+ lockdep_assert_held(&local->chanctx_mtx);
- /* should never be called if not performing a channel switch. */
- if (WARN_ON(!sdata->vif.csa_active))
- return -EINVAL;
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->vif.csa_active)
+ continue;
- if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
- IEEE80211_CHAN_DISABLED))
- return -EINVAL;
+ if (!sdata->csa_complete)
+ return NULL;
- mutex_lock(&local->chanctx_mtx);
- conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
- lockdep_is_held(&local->chanctx_mtx));
- if (!conf) {
- ret = -EINVAL;
- goto out;
+ if (chandef == NULL)
+ chandef = &sdata->csa_chandef;
+ else
+ chandef = cfg80211_chandef_compatible(
+ chandef, &sdata->csa_chandef);
+
+ if (!chandef)
+ return NULL;
}
- ctx = container_of(conf, struct ieee80211_chanctx, conf);
- if (ctx->refcount != 1) {
- ret = -EINVAL;
- goto out;
+ return chandef;
+}
+
+static void ieee80211_use_csa_chandef(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->vif.csa_active)
+ continue;
+
+ sdata->radar_required = sdata->csa_radar_required;
+ sdata->vif.bss_conf.chandef = sdata->csa_chandef;
}
+}
- if (sdata->vif.bss_conf.chandef.width != chandef->width) {
- chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
- *changed |= BSS_CHANGED_BANDWIDTH;
+struct ieee80211_chanctx *
+ieee80211_get_csa_chanctx(struct ieee80211_local *local)
+{
+ struct ieee80211_chanctx *chanctx = NULL, *ctx;
+ int num_chanctx = 0;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
+ chanctx = ctx;
+ num_chanctx++;
}
- sdata->vif.bss_conf.chandef = *chandef;
- ctx->conf.def = *chandef;
+ /* multi-channel is not supported, multi-vif is */
+ if (num_chanctx > 1)
+ return NULL;
+
+ return chanctx;
+}
+
+int ieee80211_chanctx_chswitch(struct ieee80211_local *local)
+{
+ u32 chanctx_changed = 0;
+ struct ieee80211_chanctx *ctx;
+ const struct cfg80211_chan_def *chandef;
+
+ lockdep_assert_held(&local->mtx);
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ctx = ieee80211_get_csa_chanctx(local);
+ if (!ctx)
+ return -EBUSY;
+
+ rcu_read_lock();
+ chandef = ieee80211_get_csa_chandef(local);
+ if (!chandef) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ if (!cfg80211_chandef_usable(local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ ieee80211_use_csa_chandef(local);
+ rcu_read_unlock();
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+
+ if (ctx->conf.def.width != chandef->width)
+ chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+
+ ctx->conf.def = *chandef;
drv_change_chanctx(local, ctx, chanctx_changed);
ieee80211_recalc_chanctx_chantype(local, ctx);
@@ -595,10 +649,7 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_radar_chanctx(local, ctx);
ieee80211_recalc_chanctx_min_def(local, ctx);
- ret = 0;
- out:
- mutex_unlock(&local->chanctx_mtx);
- return ret;
+ return 0;
}
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 12c6019..081beba 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -920,7 +920,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
params.block_tx = !!csa_ie.mode;
if (__ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
- ¶ms))
+ ¶ms, true))
goto disconnect;
ieee80211_ibss_csa_mark_radar(sdata);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e523429..3adc5ea 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -262,6 +262,10 @@ struct ieee80211_if_ap {
struct cfg80211_beacon_data *next_beacon;
struct list_head vlans;
+ /* to be used if channel switch fails. */
+ struct beacon_data *prev_beacon;
+ struct probe_resp *prev_presp;
+
struct ps_data ps;
atomic_t num_mcast_sta; /* number of stations receiving multicast */
enum ieee80211_smps_mode req_smps, /* requested smps mode */
@@ -701,6 +705,7 @@ struct mac80211_qos_map {
struct ieee80211_sub_if_data {
struct list_head list;
+ struct list_head csa_list;
struct wireless_dev wdev;
@@ -747,9 +752,11 @@ struct ieee80211_sub_if_data {
struct mac80211_qos_map __rcu *qos_map;
struct work_struct csa_finalize_work;
+ struct work_struct csa_complete_work;
int csa_counter_offset_beacon;
int csa_counter_offset_presp;
bool csa_radar_required;
+ bool csa_complete;
struct cfg80211_chan_def csa_chandef;
/* used to reconfigure hardware SM PS */
@@ -1457,12 +1464,22 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
/* channel switch handling */
void ieee80211_csa_finalize_work(struct work_struct *work);
+void ieee80211_csa_complete_work(struct work_struct *work);
int __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_csa_settings *params);
+ struct cfg80211_csa_settings *params,
+ bool first);
int ieee80211_channel_switch(struct wiphy *wiphy,
struct cfg80211_csa_settings *params,
int num_params);
bool ieee80211_is_csa_active(struct ieee80211_local *local);
+void ieee80211_csa_clear(struct ieee80211_sub_if_data *sdata);
+void ieee80211_csa_free(struct ieee80211_sub_if_data *sdata);
+const struct cfg80211_chan_def *
+ieee80211_get_csa_chandef(struct ieee80211_local *local);
+struct ieee80211_chanctx *
+ieee80211_get_csa_chanctx(struct ieee80211_local *local);
+int ieee80211_chanctx_csa(struct ieee80211_local *local);
+int ieee80211_chanctx_chswitch(struct ieee80211_local *local);
/* interface handling */
int ieee80211_iface_init(void);
@@ -1775,10 +1792,6 @@ int __must_check
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
u32 *changed);
-/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
-int __must_check
-ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
- 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,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 58cc061..0286a73 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -846,9 +846,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&sdata->recalc_smps);
sdata_lock(sdata);
mutex_lock(&local->mtx);
- sdata->vif.csa_active = false;
+ ieee80211_csa_clear(sdata);
+ ieee80211_csa_free(sdata);
mutex_unlock(&local->mtx);
sdata_unlock(sdata);
+ cancel_work_sync(&sdata->csa_complete_work);
cancel_work_sync(&sdata->csa_finalize_work);
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
@@ -1294,6 +1296,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
+ INIT_WORK(&sdata->csa_complete_work, ieee80211_csa_complete_work);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b3fa66a..a898036 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -889,7 +889,14 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated)
goto out;
- ret = ieee80211_vif_change_channel(sdata, &changed);
+ if (sdata->vif.bss_conf.chandef.width !=
+ sdata->csa_chandef.width)
+ changed |= BSS_CHANGED_BANDWIDTH;
+
+ mutex_lock(&local->chanctx_mtx);
+ ret = ieee80211_chanctx_chswitch(local);
+ mutex_unlock(&local->chanctx_mtx);
+
if (ret) {
sdata_info(sdata,
"vif channel switch failed, disconnecting\n");
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index ef3555e..9d4567c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2405,7 +2405,7 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif)
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
ieee80211_queue_work(&sdata->local->hw,
- &sdata->csa_finalize_work);
+ &sdata->csa_complete_work);
}
EXPORT_SYMBOL(ieee80211_csa_finish);
@@ -2437,9 +2437,13 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
if (WARN_ON(counter_offset_beacon >= beacon_data_len))
return;
- /* warn if the driver did not check for/react to csa completeness */
- if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
+ if (beacon_data[counter_offset_beacon] == 0) {
+ /* warn if the driver did not check for/react to csa
+ * completeness. keep in mind that for multi-interface csa some
+ * BSSes may need to wait for others to complete */
+ WARN_ON(!sdata->csa_complete);
return;
+ }
beacon_data[counter_offset_beacon]--;
--
1.8.4.rc3
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html