mac80211: fix sparse warning caused by __ieee80211_channel_switch()
[pandora-kernel.git] / net / mac80211 / cfg.c
index 453e974..7c56445 100644 (file)
@@ -109,6 +109,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
 static int ieee80211_start_p2p_device(struct wiphy *wiphy,
                                      struct wireless_dev *wdev)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       mutex_lock(&sdata->local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&sdata->local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
+
        return ieee80211_do_open(wdev, true);
 }
 
@@ -451,11 +460,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
        if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-       if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
@@ -970,15 +979,15 @@ 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;
 
        mutex_lock(&local->mtx);
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
+       if (!err)
+               ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
        mutex_unlock(&local->mtx);
        if (err)
                return err;
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
 
        /*
         * Apply control port protocol, this allows us to
@@ -1056,6 +1065,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        int err;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       sdata_assert_lock(sdata);
 
        /* don't allow changing the beacon while CSA is in place - offset
         * of channel switch counter may change
@@ -1074,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);
@@ -1083,13 +1118,22 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        struct probe_resp *old_probe_resp;
        struct cfg80211_chan_def chandef;
 
+       sdata_assert_lock(sdata);
+
        old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (!old_beacon)
                return -ENOENT;
        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;
 
@@ -1128,8 +1172,8 @@ 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);
        mutex_lock(&local->mtx);
+       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        ieee80211_vif_release_channel(sdata);
        mutex_unlock(&local->mtx);
 
@@ -1343,6 +1387,15 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    params->vht_capa, sta);
 
+       if (params->opmode_notif_used) {
+               /* returned value is only needed for rc update, but the
+                * rc isn't initialized here yet, so ignore it
+                */
+               __ieee80211_vht_handle_opmode(sdata, sta,
+                                             params->opmode_notif,
+                                             band, false);
+       }
+
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
                u32 changed = 0;
@@ -1438,6 +1491,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
                sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
                sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       } else {
+               sta->sta.tdls = true;
        }
 
        err = sta_apply_parameters(local, sta, params);
@@ -1554,7 +1609,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
                    sta->sdata->u.vlan.sta) {
-                       rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL);
+                       RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
                        prev_4addr = true;
                }
 
@@ -2630,6 +2685,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!roc)
                return -ENOMEM;
 
+       /*
+        * If the duration is zero, then the driver
+        * wouldn't actually do anything. Set it to
+        * 10 for now.
+        *
+        * TODO: cancel the off-channel operation
+        *       when we get the SKB's TX status and
+        *       the wait time was zero before.
+        */
+       if (!duration)
+               duration = 10;
+
        roc->chan = channel;
        roc->duration = duration;
        roc->req_duration = duration;
@@ -2671,18 +2738,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 
        /* otherwise actually kick it off here (for error handling) */
 
-       /*
-        * If the duration is zero, then the driver
-        * wouldn't actually do anything. Set it to
-        * 10 for now.
-        *
-        * TODO: cancel the off-channel operation
-        *       when we get the SKB's TX status and
-        *       the wait time was zero before.
-        */
-       if (!duration)
-               duration = 10;
-
        ret = drv_remain_on_channel(local, sdata, channel, duration, type);
        if (ret) {
                kfree(roc);
@@ -2902,11 +2957,11 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
 
 static int ieee80211_start_radar_detection(struct wiphy *wiphy,
                                           struct net_device *dev,
-                                          struct cfg80211_chan_def *chandef)
+                                          struct cfg80211_chan_def *chandef,
+                                          u32 cac_time_ms)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       unsigned long timeout;
        int err;
 
        mutex_lock(&local->mtx);
@@ -2918,16 +2973,15 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
        /* 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;
 
        err = ieee80211_vif_use_channel(sdata, chandef,
                                        IEEE80211_CHANCTX_SHARED);
        if (err)
                goto out_unlock;
 
-       timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
        ieee80211_queue_delayed_work(&sdata->local->hw,
-                                    &sdata->dfs_cac_timer_work, timeout);
+                                    &sdata->dfs_cac_timer_work,
+                                    msecs_to_jiffies(cac_time_ms));
 
  out_unlock:
        mutex_unlock(&local->mtx);
@@ -2990,136 +3044,163 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
        return new_beacon;
 }
 
-void ieee80211_csa_finalize_work(struct work_struct *work)
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
 {
-       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);
-       /* AP might have been stopped while waiting for the lock. */
-       if (!sdata->vif.csa_active)
-               goto unlock;
-
-       if (!ieee80211_sdata_running(sdata))
-               goto unlock;
-
-       sdata->radar_required = sdata->csa_radar_required;
-       mutex_lock(&local->mtx);
-       err = ieee80211_vif_change_channel(sdata, &changed);
-       mutex_unlock(&local->mtx);
-       if (WARN_ON(err < 0))
-               goto unlock;
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               ieee80211_hw_config(local, 0);
-       }
+       ieee80211_queue_work(&sdata->local->hw,
+                            &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
 
-       ieee80211_bss_info_change_notify(sdata, changed);
+static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                                         u32 *changed)
+{
+       int err;
 
-       sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
-               if (err < 0)
-                       goto unlock;
-
-               changed |= err;
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
 
-               ieee80211_bss_info_change_notify(sdata, err);
+               if (err < 0)
+                       return err;
+               *changed |= err;
                break;
        case NL80211_IFTYPE_ADHOC:
-               ieee80211_ibss_finish_csa(sdata);
+               err = ieee80211_ibss_finish_csa(sdata);
+               if (err < 0)
+                       return err;
+               *changed |= err;
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
-                       goto unlock;
+                       return err;
+               *changed |= err;
                break;
 #endif
        default:
                WARN_ON(1);
-               goto unlock;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       u32 changed = 0;
+       int err;
+
+       sdata_assert_lock(sdata);
+       lockdep_assert_held(&local->mtx);
+
+       sdata->radar_required = sdata->csa_radar_required;
+       err = ieee80211_vif_change_channel(sdata, &changed);
+       if (err < 0)
+               return err;
+
+       if (!local->use_chanctx) {
+               local->_oper_chandef = sdata->csa_chandef;
+               ieee80211_hw_config(local, 0);
        }
 
-       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+       sdata->vif.csa_active = false;
+
+       err = ieee80211_set_after_csa_beacon(sdata, &changed);
+       if (err)
+               return err;
+
+       ieee80211_bss_info_change_notify(sdata, changed);
+       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);
+       return 0;
+}
 
-unlock:
-       sdata_unlock(sdata);
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+       if (__ieee80211_csa_finalize(sdata)) {
+               sdata_info(sdata, "failed to finalize CSA, disconnecting\n");
+               cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev,
+                                   GFP_KERNEL);
+       }
 }
 
-int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
-                            struct cfg80211_csa_settings *params)
+void ieee80211_csa_finalize_work(struct work_struct *work)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            csa_finalize_work);
        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;
-
-       lockdep_assert_held(&sdata->wdev.mtx);
-
-       if (!list_empty(&local->roc_list) || local->scanning)
-               return -EBUSY;
 
-       if (sdata->wdev.cac_started)
-               return -EBUSY;
+       sdata_lock(sdata);
+       mutex_lock(&local->mtx);
 
-       if (cfg80211_chandef_identical(&params->chandef,
-                                      &sdata->vif.bss_conf.chandef))
-               return -EINVAL;
+       /* AP might have been stopped while waiting for the lock. */
+       if (!sdata->vif.csa_active)
+               goto unlock;
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (!chanctx_conf) {
-               rcu_read_unlock();
-               return -EBUSY;
-       }
+       if (!ieee80211_sdata_running(sdata))
+               goto unlock;
 
-       /* don't handle for multi-VIF cases */
-       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
-               rcu_read_unlock();
-               return -EBUSY;
-       }
-       num_chanctx = 0;
-       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
-               num_chanctx++;
-       rcu_read_unlock();
+       ieee80211_csa_finalize(sdata);
 
-       if (num_chanctx > 1)
-               return -EBUSY;
+unlock:
+       mutex_unlock(&local->mtx);
+       sdata_unlock(sdata);
+}
 
-       /* don't allow another channel switch if one is already active. */
-       if (sdata->vif.csa_active)
-               return -EBUSY;
+static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                                   struct cfg80211_csa_settings *params,
+                                   u32 *changed)
+{
+       int err;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
-               sdata->csa_counter_offset_beacon =
-                       params->counter_offset_beacon;
-               sdata->csa_counter_offset_presp = params->counter_offset_presp;
                sdata->u.ap.next_beacon =
                        cfg80211_beacon_dup(&params->beacon_after);
                if (!sdata->u.ap.next_beacon)
                        return -ENOMEM;
 
+               /*
+                * With a count of 0, we don't have to wait for any
+                * TBTT before switching, so complete the CSA
+                * immediately.  In theory, with a count == 1 we
+                * should delay the switch until just before the next
+                * TBTT, but that would complicate things so we switch
+                * immediately too.  If we would delay the switch
+                * until the next TBTT, we would have to set the probe
+                * response here.
+                *
+                * TODO: A channel switch with count <= 1 without
+                * sending a CSA action frame is kind of useless,
+                * because the clients won't know we're changing
+                * channels.  The action frame must be implemented
+                * either here or in the userspace.
+                */
+               if (params->count <= 1)
+                       break;
+
+               sdata->csa_counter_offset_beacon =
+                       params->counter_offset_beacon;
+               sdata->csa_counter_offset_presp = params->counter_offset_presp;
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
                }
+               *changed |= err;
+
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!sdata->vif.bss_conf.ibss_joined)
@@ -3147,16 +3228,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
-               err = ieee80211_ibss_csa_beacon(sdata, params);
-               if (err < 0)
-                       return err;
+               /* see comments in the NL80211_IFTYPE_AP block */
+               if (params->count > 1) {
+                       err = ieee80211_ibss_csa_beacon(sdata, params);
+                       if (err < 0)
+                               return err;
+                       *changed |= err;
+               }
+
+               ieee80211_send_action_csa(sdata, params);
+
                break;
 #ifdef CONFIG_MAC80211_MESH
-       case NL80211_IFTYPE_MESH_POINT:
-               ifmsh = &sdata->u.mesh;
-
-               if (!ifmsh->mesh_id)
-                       return -EINVAL;
+       case NL80211_IFTYPE_MESH_POINT: {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
                if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
                        return -EINVAL;
@@ -3166,39 +3251,125 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
-               ifmsh->chsw_init = true;
-               if (!ifmsh->pre_value)
-                       ifmsh->pre_value = 1;
-               else
-                       ifmsh->pre_value++;
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
+                       ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
+                       if (!ifmsh->pre_value)
+                               ifmsh->pre_value = 1;
+                       else
+                               ifmsh->pre_value++;
+               }
 
-               err = ieee80211_mesh_csa_beacon(sdata, params, true);
-               if (err < 0) {
-                       ifmsh->chsw_init = false;
-                       return err;
+               /* see comments in the NL80211_IFTYPE_AP block */
+               if (params->count > 1) {
+                       err = ieee80211_mesh_csa_beacon(sdata, params);
+                       if (err < 0) {
+                               ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
+                               return err;
+                       }
+                       *changed |= err;
                }
+
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
+                       ieee80211_send_action_csa(sdata, params);
+
                break;
+               }
 #endif
        default:
                return -EOPNOTSUPP;
        }
 
-       sdata->csa_radar_required = params->radar_required;
+       return 0;
+}
 
-       if (params->block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
+static 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;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *chanctx;
+       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;
+
+       if (sdata->wdev.cac_started)
+               return -EBUSY;
 
+       if (cfg80211_chandef_identical(&params->chandef,
+                                      &sdata->vif.bss_conf.chandef))
+               return -EINVAL;
+
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               mutex_unlock(&local->chanctx_mtx);
+               return -EBUSY;
+       }
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(conf, struct ieee80211_chanctx, conf);
+       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
+               mutex_unlock(&local->chanctx_mtx);
+               return -EBUSY;
+       }
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+               num_chanctx++;
+       mutex_unlock(&local->chanctx_mtx);
+
+       if (num_chanctx > 1)
+               return -EBUSY;
+
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
+       err = ieee80211_set_csa_beacon(sdata, params, &changed);
+       if (err)
+               return err;
+
+       sdata->csa_radar_required = params->radar_required;
        sdata->csa_chandef = params->chandef;
+       sdata->csa_block_tx = params->block_tx;
        sdata->vif.csa_active = true;
 
-       ieee80211_bss_info_change_notify(sdata, err);
-       drv_channel_switch_beacon(sdata, &params->chandef);
+       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, &params->chandef);
+       } else {
+               /* if the beacon didn't change, we can finalize immediately */
+               ieee80211_csa_finalize(sdata);
+       }
 
        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)
@@ -3413,320 +3584,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy,
        return 0;
 }
 
-static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
-{
-       u8 *pos = (void *)skb_put(skb, 7);
-
-       *pos++ = WLAN_EID_EXT_CAPABILITY;
-       *pos++ = 5; /* len */
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
-}
-
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-       u16 capab;
-
-       capab = 0;
-       if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
-               return capab;
-
-       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
-
-       return capab;
-}
-
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr,
-                                      u8 *peer, u8 *bssid)
-{
-       struct ieee80211_tdls_lnkie *lnkid;
-
-       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
-
-       lnkid->ie_type = WLAN_EID_LINK_ID;
-       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
-
-       memcpy(lnkid->bssid, bssid, ETH_ALEN);
-       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
-       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
-}
-
-static int
-ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, struct sk_buff *skb)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       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));
-
-       memcpy(tf->da, peer, ETH_ALEN);
-       memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
-       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
-       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
-
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
-
-               skb_put(skb, sizeof(tf->u.setup_req));
-               tf->u.setup_req.dialog_token = dialog_token;
-               tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               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:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
-
-               skb_put(skb, sizeof(tf->u.setup_resp));
-               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
-               tf->u.setup_resp.dialog_token = dialog_token;
-               tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               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:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
-
-               skb_put(skb, sizeof(tf->u.setup_cfm));
-               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
-               tf->u.setup_cfm.dialog_token = dialog_token;
-               break;
-       case WLAN_TDLS_TEARDOWN:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_TEARDOWN;
-
-               skb_put(skb, sizeof(tf->u.teardown));
-               tf->u.teardown.reason_code = cpu_to_le16(status_code);
-               break;
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
-
-               skb_put(skb, sizeof(tf->u.discover_req));
-               tf->u.discover_req.dialog_token = dialog_token;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int
-ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, u8 action_code, u8 dialog_token,
-                          u16 status_code, struct sk_buff *skb)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_mgmt *mgmt;
-
-       mgmt = (void *)skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, peer, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
-
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       switch (action_code) {
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
-               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
-               mgmt->u.action.u.tdls_discover_resp.action_code =
-                       WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
-               mgmt->u.action.u.tdls_discover_resp.dialog_token =
-                       dialog_token;
-               mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               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:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, const u8 *extra_ies,
-                              size_t extra_ies_len)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb = NULL;
-       bool send_direct;
-       int ret;
-
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       /* make sure we are in managed mode, and associated */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
-           !sdata->u.mgd.associated)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
-                action_code, peer);
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           max(sizeof(struct ieee80211_mgmt),
-                               sizeof(struct ieee80211_tdls_data)) +
-                           50 + /* supported rates */
-                           7 + /* ext capab */
-                           extra_ies_len +
-                           sizeof(struct ieee80211_tdls_lnkie));
-       if (!skb)
-               return -ENOMEM;
-
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_RESPONSE:
-       case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
-                                                    action_code, dialog_token,
-                                                    status_code, skb);
-               send_direct = false;
-               break;
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
-                                                dialog_token, status_code,
-                                                skb);
-               send_direct = true;
-               break;
-       default:
-               ret = -ENOTSUPP;
-               break;
-       }
-
-       if (ret < 0)
-               goto fail;
-
-       if (extra_ies_len)
-               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
-
-       /* the TDLS link IE is always added last */
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               /* we are the initiator */
-               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
-                                          sdata->u.mgd.bssid);
-               break;
-       case WLAN_TDLS_SETUP_RESPONSE:
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               /* we are the responder */
-               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
-                                          sdata->u.mgd.bssid);
-               break;
-       default:
-               ret = -ENOTSUPP;
-               goto fail;
-       }
-
-       if (send_direct) {
-               ieee80211_tx_skb(sdata, skb);
-               return 0;
-       }
-
-       /*
-        * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
-        * we should default to AC_VI.
-        */
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_RESPONSE:
-               skb_set_queue_mapping(skb, IEEE80211_AC_BK);
-               skb->priority = 2;
-               break;
-       default:
-               skb_set_queue_mapping(skb, IEEE80211_AC_VI);
-               skb->priority = 5;
-               break;
-       }
-
-       /* disable bottom halves when entering the Tx path */
-       local_bh_disable();
-       ret = ieee80211_subif_start_xmit(skb, dev);
-       local_bh_enable();
-
-       return ret;
-
-fail:
-       dev_kfree_skb(skb);
-       return ret;
-}
-
-static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, enum nl80211_tdls_operation oper)
-{
-       struct sta_info *sta;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
-
-       switch (oper) {
-       case NL80211_TDLS_ENABLE_LINK:
-               rcu_read_lock();
-               sta = sta_info_get(sdata, peer);
-               if (!sta) {
-                       rcu_read_unlock();
-                       return -ENOLINK;
-               }
-
-               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
-               rcu_read_unlock();
-               break;
-       case NL80211_TDLS_DISABLE_LINK:
-               return sta_info_destroy_addr(sdata, peer);
-       case NL80211_TDLS_TEARDOWN:
-       case NL80211_TDLS_SETUP:
-       case NL80211_TDLS_DISCOVERY_REQ:
-               /* We don't support in-driver setup/teardown/discovery */
-               return -ENOTSUPP;
-       default:
-               return -ENOTSUPP;
-       }
-
-       return 0;
-}
-
 static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                                  const u8 *peer, u64 *cookie)
 {
@@ -3865,7 +3722,22 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
        return 0;
 }
 
-struct cfg80211_ops mac80211_config_ops = {
+static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
+                                     struct net_device *dev,
+                                     struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int ret;
+       u32 changed = 0;
+
+       ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed);
+       if (ret == 0)
+               ieee80211_bss_info_change_notify(sdata, changed);
+
+       return ret;
+}
+
+const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
        .change_virtual_intf = ieee80211_change_iface,
@@ -3945,4 +3817,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .start_radar_detection = ieee80211_start_radar_detection,
        .channel_switch = ieee80211_channel_switch,
        .set_qos_map = ieee80211_set_qos_map,
+       .set_ap_chanwidth = ieee80211_set_ap_chanwidth,
 };