|
|
@@ -1084,6 +1084,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local)
|
|
|
+{
|
|
|
+ struct ieee80211_sub_if_data *sdata;
|
|
|
+
|
|
|
+ lockdep_assert_held(&local->mtx);
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
|
+ if (!ieee80211_sdata_running(sdata))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!sdata->vif.csa_active)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!sdata->csa_block_tx)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|
|
{
|
|
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
@@ -1101,7 +1126,14 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
|
|
old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
|
|
|
|
|
|
/* abort any running channel switch */
|
|
|
+ mutex_lock(&local->mtx);
|
|
|
sdata->vif.csa_active = false;
|
|
|
+ if (!ieee80211_csa_needs_block_tx(local))
|
|
|
+ ieee80211_wake_queues_by_reason(&local->hw,
|
|
|
+ IEEE80211_MAX_QUEUE_MAP,
|
|
|
+ IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
+
|
|
|
kfree(sdata->u.ap.next_beacon);
|
|
|
sdata->u.ap.next_beacon = NULL;
|
|
|
|
|
|
@@ -3027,11 +3059,10 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
|
|
int err, changed = 0;
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
|
|
+ lockdep_assert_held(&local->mtx);
|
|
|
|
|
|
- mutex_lock(&local->mtx);
|
|
|
sdata->radar_required = sdata->csa_radar_required;
|
|
|
err = ieee80211_vif_change_channel(sdata, &changed);
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
if (WARN_ON(err < 0))
|
|
|
return;
|
|
|
|
|
|
@@ -3072,11 +3103,12 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
|
|
|
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
|
|
|
|
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
|
|
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
|
|
+
|
|
|
+ if (!ieee80211_csa_needs_block_tx(local))
|
|
|
+ ieee80211_wake_queues_by_reason(&local->hw,
|
|
|
IEEE80211_MAX_QUEUE_MAP,
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
-
|
|
|
- cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
|
|
|
}
|
|
|
|
|
|
void ieee80211_csa_finalize_work(struct work_struct *work)
|
|
|
@@ -3084,8 +3116,11 @@ 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;
|
|
|
|
|
|
sdata_lock(sdata);
|
|
|
+ mutex_lock(&local->mtx);
|
|
|
+
|
|
|
/* AP might have been stopped while waiting for the lock. */
|
|
|
if (!sdata->vif.csa_active)
|
|
|
goto unlock;
|
|
|
@@ -3096,6 +3131,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
|
|
ieee80211_csa_finalize(sdata);
|
|
|
|
|
|
unlock:
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
sdata_unlock(sdata);
|
|
|
}
|
|
|
|
|
|
@@ -3222,8 +3258,8 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|
|
- struct cfg80211_csa_settings *params)
|
|
|
+int __ieee80211_channel_switch(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;
|
|
|
@@ -3232,6 +3268,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|
|
int err, num_chanctx, changed = 0;
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
|
|
+ lockdep_assert_held(&local->mtx);
|
|
|
|
|
|
if (!list_empty(&local->roc_list) || local->scanning)
|
|
|
return -EBUSY;
|
|
|
@@ -3274,15 +3311,15 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|
|
return err;
|
|
|
|
|
|
sdata->csa_radar_required = params->radar_required;
|
|
|
-
|
|
|
- if (params->block_tx)
|
|
|
- ieee80211_stop_queues_by_reason(&local->hw,
|
|
|
- IEEE80211_MAX_QUEUE_MAP,
|
|
|
- IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
-
|
|
|
sdata->csa_chandef = params->chandef;
|
|
|
+ sdata->csa_block_tx = params->block_tx;
|
|
|
sdata->vif.csa_active = true;
|
|
|
|
|
|
+ if (sdata->csa_block_tx)
|
|
|
+ ieee80211_stop_queues_by_reason(&local->hw,
|
|
|
+ IEEE80211_MAX_QUEUE_MAP,
|
|
|
+ IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
+
|
|
|
if (changed) {
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
|
drv_channel_switch_beacon(sdata, ¶ms->chandef);
|
|
|
@@ -3294,6 +3331,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+int ieee80211_channel_switch(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;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ mutex_lock(&local->mtx);
|
|
|
+ err = __ieee80211_channel_switch(wiphy, dev, params);
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
struct cfg80211_mgmt_tx_params *params,
|
|
|
u64 *cookie)
|