Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi...
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / cfg.c
index 7371f676cf412e54481751d36bf757f42dfa5134..808f5fcd1ced92127f8786700a759465e0708060 100644 (file)
@@ -164,7 +164,17 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                        sta = sta_info_get(sdata, mac_addr);
                else
                        sta = sta_info_get_bss(sdata, mac_addr);
-               if (!sta) {
+               /*
+                * The ASSOC test makes sure the driver is ready to
+                * receive the key. When wpa_supplicant has roamed
+                * using FT, it attempts to set the key before
+                * association has completed, this rejects that attempt
+                * so it will set the key again after assocation.
+                *
+                * TODO: accept the key if we have a station entry and
+                *       add it to the device after the station.
+                */
+               if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
                        ieee80211_key_free(sdata->local, key);
                        err = -ENOENT;
                        goto out_unlock;
@@ -370,29 +380,64 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
        return 0;
 }
 
-static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
-{
-       if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
-               struct ieee80211_supported_band *sband;
-               sband = sta->local->hw.wiphy->bands[
-                               sta->local->oper_channel->band];
-               rate->legacy = sband->bitrates[idx].bitrate;
-       } else
-               rate->mcs = idx;
-}
-
 void sta_set_rate_info_tx(struct sta_info *sta,
                          const struct ieee80211_tx_rate *rate,
                          struct rate_info *rinfo)
 {
        rinfo->flags = 0;
-       if (rate->flags & IEEE80211_TX_RC_MCS)
+       if (rate->flags & IEEE80211_TX_RC_MCS) {
                rinfo->flags |= RATE_INFO_FLAGS_MCS;
+               rinfo->mcs = rate->idx;
+       } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+               rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+               rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
+               rinfo->nss = ieee80211_rate_get_vht_nss(rate);
+       } else {
+               struct ieee80211_supported_band *sband;
+               sband = sta->local->hw.wiphy->bands[
+                               ieee80211_get_sdata_band(sta->sdata)];
+               rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+       }
        if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+       if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+               rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+       if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+               rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
        if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-       rate_idx_to_bitrate(rinfo, sta, rate->idx);
+}
+
+void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
+{
+       rinfo->flags = 0;
+
+       if (sta->last_rx_rate_flag & RX_FLAG_HT) {
+               rinfo->flags |= RATE_INFO_FLAGS_MCS;
+               rinfo->mcs = sta->last_rx_rate_idx;
+       } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
+               rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+               rinfo->nss = sta->last_rx_rate_vht_nss;
+               rinfo->mcs = sta->last_rx_rate_idx;
+       } else {
+               struct ieee80211_supported_band *sband;
+
+               sband = sta->local->hw.wiphy->bands[
+                               ieee80211_get_sdata_band(sta->sdata)];
+               rinfo->legacy =
+                       sband->bitrates[sta->last_rx_rate_idx].bitrate;
+       }
+
+       if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
+               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)
+               rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+       if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+               rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+       if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+               rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
 static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
@@ -441,21 +486,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        }
 
        sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-
-       sinfo->rxrate.flags = 0;
-       if (sta->last_rx_rate_flag & RX_FLAG_HT)
-               sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
-       if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
-               sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
-               sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
-       rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx);
+       sta_set_rate_info_rx(sta, &sinfo->rxrate);
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
                sinfo->filled |= STATION_INFO_LLID |
                                 STATION_INFO_PLID |
-                                STATION_INFO_PLINK_STATE;
+                                STATION_INFO_PLINK_STATE |
+                                STATION_INFO_LOCAL_PM |
+                                STATION_INFO_PEER_PM |
+                                STATION_INFO_NONPEER_PM;
 
                sinfo->llid = le16_to_cpu(sta->llid);
                sinfo->plid = le16_to_cpu(sta->plid);
@@ -464,6 +504,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                        sinfo->filled |= STATION_INFO_T_OFFSET;
                        sinfo->t_offset = sta->t_offset;
                }
+               sinfo->local_pm = sta->local_pm;
+               sinfo->peer_pm = sta->peer_pm;
+               sinfo->nonpeer_pm = sta->nonpeer_pm;
 #endif
        }
 
@@ -483,6 +526,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                                BIT(NL80211_STA_FLAG_WME) |
                                BIT(NL80211_STA_FLAG_MFP) |
                                BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                               BIT(NL80211_STA_FLAG_ASSOCIATED) |
                                BIT(NL80211_STA_FLAG_TDLS_PEER);
        if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -494,6 +538,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
        if (test_sta_flag(sta, WLAN_STA_AUTH))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+       if (test_sta_flag(sta, WLAN_STA_ASSOC))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
 }
@@ -532,6 +578,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 +655,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->def.chan;
+       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 +779,37 @@ 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 cfg80211_chan_def *chandef)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
-       struct ieee80211_sub_if_data *sdata = NULL;
+       struct ieee80211_sub_if_data *sdata;
+       int ret = 0;
 
-       if (netdev)
-               sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
+       if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
+               return 0;
 
-       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;
+       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, chandef,
+                                       IEEE80211_CHANCTX_EXCLUSIVE);
+               }
+       } else if (local->open_count == local->monitors) {
+               local->_oper_channel = chandef->chan;
+               local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
+               ieee80211_hw_config(local, 0);
        }
 
-       if (!ieee80211_set_channel_type(local, sdata, channel_type))
-               return -EBUSY;
-
-       local->oper_channel = chan;
-
-       /* auto-detects changes */
-       ieee80211_hw_config(local, 0);
-
-       return 0;
-}
+       if (ret == 0)
+               local->monitor_chandef = *chandef;
+       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,
@@ -872,17 +917,24 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        u32 changed = BSS_CHANGED_BEACON_INT |
                      BSS_CHANGED_BEACON_ENABLED |
                      BSS_CHANGED_BEACON |
-                     BSS_CHANGED_SSID;
+                     BSS_CHANGED_SSID |
+                     BSS_CHANGED_P2P_PS;
        int err;
 
        old = rtnl_dereference(sdata->u.ap.beacon);
        if (old)
                return -EALREADY;
 
-       err = ieee80211_set_channel(wiphy, dev, params->channel,
-                                   params->channel_type);
+       /* 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;
+
+       err = ieee80211_vif_use_channel(sdata, &params->chandef,
+                                       IEEE80211_CHANCTX_SHARED);
        if (err)
                return err;
+       ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
 
        /*
         * Apply control port protocol, this allows us to
@@ -899,6 +951,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
 
        sdata->vif.bss_conf.beacon_int = params->beacon_interval;
        sdata->vif.bss_conf.dtim_period = params->dtim_period;
+       sdata->vif.bss_conf.enable_beacon = true;
 
        sdata->vif.bss_conf.ssid_len = params->ssid_len;
        if (params->ssid_len)
@@ -907,11 +960,23 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        sdata->vif.bss_conf.hidden_ssid =
                (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
 
+       sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
+       sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
+
        err = ieee80211_assign_beacon(sdata, &params->beacon);
        if (err < 0)
                return err;
        changed |= err;
 
+       err = drv_start_ap(sdata->local, sdata);
+       if (err) {
+               old = rtnl_dereference(sdata->u.ap.beacon);
+               if (old)
+                       kfree_rcu(old, rcu_head);
+               RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+               return err;
+       }
+
        ieee80211_bss_info_change_notify(sdata, changed);
 
        netif_carrier_on(dev);
@@ -943,26 +1008,50 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
 
 static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
-       struct ieee80211_sub_if_data *sdata, *vlan;
-       struct beacon_data *old;
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_sub_if_data *vlan;
+       struct ieee80211_local *local = sdata->local;
+       struct beacon_data *old_beacon;
+       struct probe_resp *old_probe_resp;
 
-       old = rtnl_dereference(sdata->u.ap.beacon);
-       if (!old)
+       old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+       if (!old_beacon)
                return -ENOENT;
+       old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
 
+       /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                netif_carrier_off(vlan->dev);
        netif_carrier_off(dev);
 
+       /* remove beacon and probe response */
        RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+       RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
+       kfree_rcu(old_beacon, rcu_head);
+       if (old_probe_resp)
+               kfree_rcu(old_probe_resp, rcu_head);
 
-       kfree_rcu(old, rcu_head);
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               sta_info_flush_defer(vlan);
+       sta_info_flush_defer(sdata);
+       rcu_barrier();
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               sta_info_flush_cleanup(vlan);
+       sta_info_flush_cleanup(sdata);
 
-       sta_info_flush(sdata->local, sdata);
+       sdata->vif.bss_conf.enable_beacon = false;
+       clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
 
+       drv_stop_ap(sdata->local, sdata);
+
+       /* free all potentially still buffered bcast frames */
+       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);
+       ieee80211_vif_release_channel(sdata);
+
        return 0;
 }
 
@@ -1010,41 +1099,26 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
        netif_rx_ni(skb);
 }
 
-static int sta_apply_parameters(struct ieee80211_local *local,
+static int sta_apply_auth_flags(struct ieee80211_local *local,
                                struct sta_info *sta,
-                               struct station_parameters *params)
+                               u32 mask, u32 set)
 {
-       int ret = 0;
-       u32 rates;
-       int i, j;
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       u32 mask, set;
-
-       sband = local->hw.wiphy->bands[local->oper_channel->band];
-
-       mask = params->sta_flags_mask;
-       set = params->sta_flags_set;
+       int ret;
 
-       /*
-        * In mesh mode, we can clear AUTHENTICATED flag but must
-        * also make ASSOCIATED follow appropriately for the driver
-        * API. See also below, after AUTHORIZED changes.
-        */
-       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
-               /* cfg80211 should not allow this in non-mesh modes */
-               if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
-                       return -EINVAL;
+       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+           set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+           !test_sta_flag(sta, WLAN_STA_AUTH)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+               if (ret)
+                       return ret;
+       }
 
-               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
-                   !test_sta_flag(sta, WLAN_STA_AUTH)) {
-                       ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
-                       if (ret)
-                               return ret;
-                       ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
-                       if (ret)
-                               return ret;
-               }
+       if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+           set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+           !test_sta_flag(sta, WLAN_STA_ASSOC)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+               if (ret)
+                       return ret;
        }
 
        if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
@@ -1052,26 +1126,62 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                        ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
                else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
                        ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+               else
+                       ret = 0;
                if (ret)
                        return ret;
        }
 
-       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
-               /* cfg80211 should not allow this in non-mesh modes */
-               if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif)))
-                       return -EINVAL;
+       if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
+           !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
+           test_sta_flag(sta, WLAN_STA_ASSOC)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
+               if (ret)
+                       return ret;
+       }
 
-               if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
-                   test_sta_flag(sta, WLAN_STA_AUTH)) {
-                       ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
-                       if (ret)
-                               return ret;
-                       ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
-                       if (ret)
-                               return ret;
-               }
+       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
+           !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
+           test_sta_flag(sta, WLAN_STA_AUTH)) {
+               ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
+               if (ret)
+                       return ret;
        }
 
+       return 0;
+}
+
+static int sta_apply_parameters(struct ieee80211_local *local,
+                               struct sta_info *sta,
+                               struct station_parameters *params)
+{
+       int ret = 0;
+       u32 rates;
+       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[band];
+
+       mask = params->sta_flags_mask;
+       set = params->sta_flags_set;
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               /*
+                * In mesh mode, ASSOCIATED isn't part of the nl80211
+                * API but must follow AUTHENTICATED for driver state.
+                */
+               if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+                       mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+                       set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+       }
+
+       ret = sta_apply_auth_flags(local, sta, mask, set);
+       if (ret)
+               return ret;
 
        if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
                if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1117,10 +1227,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                sta->sta.aid = params->aid;
 
        /*
-        * FIXME: updating the following information is racy when this
-        *        function is called from ieee80211_change_station().
-        *        However, all this information should be static so
-        *        maybe we should just reject attemps to change it.
+        * Some of the following updates would be racy if called on an
+        * existing station, via ieee80211_change_station(). However,
+        * all such changes are rejected by cfg80211 except for updates
+        * changing the supported rates on an existing but not yet used
+        * TDLS peer.
         */
 
        if (params->listen_interval >= 0)
@@ -1136,36 +1247,67 @@ 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)
                ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
-                                                 params->ht_capa,
-                                                 &sta->sta.ht_cap);
+                                                 params->ht_capa, sta);
+
+       if (params->vht_capa)
+               ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+                                                   params->vht_capa, sta);
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
-               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
+               u32 changed = 0;
+               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) {
                        switch (params->plink_state) {
-                       case NL80211_PLINK_LISTEN:
                        case NL80211_PLINK_ESTAB:
+                               if (sta->plink_state != NL80211_PLINK_ESTAB)
+                                       changed = mesh_plink_inc_estab_count(
+                                                       sdata);
+                               sta->plink_state = params->plink_state;
+
+                               ieee80211_mps_sta_status_update(sta);
+                               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                             sdata->u.mesh.mshcfg.power_mode);
+                               break;
+                       case NL80211_PLINK_LISTEN:
                        case NL80211_PLINK_BLOCKED:
+                       case NL80211_PLINK_OPN_SNT:
+                       case NL80211_PLINK_OPN_RCVD:
+                       case NL80211_PLINK_CNF_RCVD:
+                       case NL80211_PLINK_HOLDING:
+                               if (sta->plink_state == NL80211_PLINK_ESTAB)
+                                       changed = mesh_plink_dec_estab_count(
+                                                       sdata);
                                sta->plink_state = params->plink_state;
+
+                               ieee80211_mps_sta_status_update(sta);
+                               changed |=
+                                     ieee80211_mps_local_status_update(sdata);
                                break;
                        default:
                                /*  nothing  */
                                break;
                        }
-               else
+               } else {
                        switch (params->plink_action) {
                        case PLINK_ACTION_OPEN:
-                               mesh_plink_open(sta);
+                               changed |= mesh_plink_open(sta);
                                break;
                        case PLINK_ACTION_BLOCK:
-                               mesh_plink_block(sta);
+                               changed |= mesh_plink_block(sta);
                                break;
                        }
+               }
+
+               if (params->local_pm)
+                       changed |=
+                             ieee80211_mps_set_sta_local_pm(sta,
+                                                            params->local_pm);
+               ieee80211_bss_info_change_notify(sdata, changed);
 #endif
        }
 
@@ -1200,6 +1342,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (!sta)
                return -ENOMEM;
 
+       /*
+        * defaults -- if userspace wants something else we'll
+        * change it accordingly in sta_apply_parameters()
+        */
        sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
        sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
 
@@ -1236,7 +1382,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac)
 {
-       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1244,7 +1389,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
        if (mac)
                return sta_info_destroy_addr_bss(sdata, mac);
 
-       sta_info_flush(local, sdata);
+       sta_info_flush(sdata);
        return 0;
 }
 
@@ -1267,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                return -ENOENT;
        }
 
-       /* in station mode, supported rates are only valid with TDLS */
+       /* in station mode, some updates are only valid with TDLS */
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-           params->supported_rates &&
+           (params->supported_rates || params->ht_capa || params->vht_capa ||
+            params->sta_modify_mask ||
+            (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
            !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
                mutex_unlock(&local->sta_mtx);
                return -EINVAL;
@@ -1353,13 +1500,13 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
                return -ENOENT;
        }
 
-       err = mesh_path_add(dst, sdata);
+       err = mesh_path_add(sdata, dst);
        if (err) {
                rcu_read_unlock();
                return err;
        }
 
-       mpath = mesh_path_lookup(dst, sdata);
+       mpath = mesh_path_lookup(sdata, dst);
        if (!mpath) {
                rcu_read_unlock();
                return -ENXIO;
@@ -1371,12 +1518,12 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *dst)
+                              u8 *dst)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        if (dst)
-               return mesh_path_del(dst, sdata);
+               return mesh_path_del(sdata, dst);
 
        mesh_path_flush_by_iface(sdata);
        return 0;
@@ -1400,7 +1547,7 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
                return -ENOENT;
        }
 
-       mpath = mesh_path_lookup(dst, sdata);
+       mpath = mesh_path_lookup(sdata, dst);
        if (!mpath) {
                rcu_read_unlock();
                return -ENOENT;
@@ -1464,7 +1611,7 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        rcu_read_lock();
-       mpath = mesh_path_lookup(dst, sdata);
+       mpath = mesh_path_lookup(sdata, dst);
        if (!mpath) {
                rcu_read_unlock();
                return -ENOENT;
@@ -1485,7 +1632,7 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        rcu_read_lock();
-       mpath = mesh_path_lookup_by_idx(idx, sdata);
+       mpath = mesh_path_lookup_by_idx(sdata, idx);
        if (!mpath) {
                rcu_read_unlock();
                return -ENOENT;
@@ -1550,6 +1697,9 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
                                                sizeof(setup->mcast_rate));
 
+       sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
+       sdata->vif.bss_conf.dtim_period = setup->dtim_period;
+
        return 0;
 }
 
@@ -1648,6 +1798,14 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
        if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
                conf->dot11MeshHWMPconfirmationInterval =
                        nconf->dot11MeshHWMPconfirmationInterval;
+       if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
+               conf->power_mode = nconf->power_mode;
+               ieee80211_mps_local_status_update(sdata);
+       }
+       if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
+               conf->dot11MeshAwakeWindowDuration =
+                       nconf->dot11MeshAwakeWindowDuration;
+       ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
 }
 
@@ -1664,14 +1822,16 @@ 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);
+       /* can mesh use other SMPS modes? */
+       sdata->smps_mode = IEEE80211_SMPS_OFF;
+       sdata->needed_rx_chains = sdata->local->rx_chains;
+
+       err = ieee80211_vif_use_channel(sdata, &setup->chandef,
+                                       IEEE80211_CHANCTX_SHARED);
        if (err)
                return err;
 
-       ieee80211_start_mesh(sdata);
-
-       return 0;
+       return ieee80211_start_mesh(sdata);
 }
 
 static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
@@ -1679,6 +1839,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 +1849,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 +1869,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 +1883,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;
@@ -1746,6 +1909,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
                changed |= BSS_CHANGED_HT;
        }
 
+       if (params->p2p_ctwindow >= 0) {
+               sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
+               changed |= BSS_CHANGED_P2P_PS;
+       }
+
+       if (params->p2p_opp_ps >= 0) {
+               sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
+               changed |= BSS_CHANGED_P2P_PS;
+       }
+
        ieee80211_bss_info_change_notify(sdata, changed);
 
        return 0;
@@ -1829,7 +2002,16 @@ static int ieee80211_scan(struct wiphy *wiphy,
                 * beaconing hasn't been configured yet
                 */
        case NL80211_IFTYPE_AP:
-               if (sdata->u.ap.beacon)
+               /*
+                * If the scan has been forced (and the driver supports
+                * forcing), don't care about being beaconing already.
+                * This will create problems to the attached stations (e.g. all
+                * the  frames sent while scanning on other channel will be
+                * lost)
+                */
+               if (sdata->u.ap.beacon &&
+                   (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
+                    !(req->flags & NL80211_SCAN_FLAG_AP)))
                        return -EOPNOTSUPP;
                break;
        default:
@@ -1872,20 +2054,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 +2072,23 @@ 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)
+{
+       return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
+}
+
+static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
+                                   int rate[IEEE80211_NUM_BANDS])
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       return ieee80211_ibss_leave(sdata);
+       memcpy(sdata->vif.bss_conf.mcast_rate, rate,
+              sizeof(int) * IEEE80211_NUM_BANDS);
+
+       return 0;
 }
 
 static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
@@ -1956,10 +2117,16 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
                        return err;
        }
 
-       if (changed & WIPHY_PARAM_RETRY_SHORT)
+       if (changed & WIPHY_PARAM_RETRY_SHORT) {
+               if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
+                       return -EINVAL;
                local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
-       if (changed & WIPHY_PARAM_RETRY_LONG)
+       }
+       if (changed & WIPHY_PARAM_RETRY_LONG) {
+               if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
+                       return -EINVAL;
                local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
+       }
        if (changed &
            (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
@@ -1968,41 +2135,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
 }
 
 static int ieee80211_set_tx_power(struct wiphy *wiphy,
+                                 struct wireless_dev *wdev,
                                  enum nl80211_tx_power_setting type, int mbm)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
-       struct ieee80211_channel *chan = local->oper_channel;
-       u32 changes = 0;
+       struct ieee80211_sub_if_data *sdata;
+
+       if (wdev) {
+               sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+               switch (type) {
+               case NL80211_TX_POWER_AUTOMATIC:
+                       sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+                       break;
+               case NL80211_TX_POWER_LIMITED:
+               case NL80211_TX_POWER_FIXED:
+                       if (mbm < 0 || (mbm % 100))
+                               return -EOPNOTSUPP;
+                       sdata->user_power_level = MBM_TO_DBM(mbm);
+                       break;
+               }
+
+               ieee80211_recalc_txpower(sdata);
+
+               return 0;
+       }
 
        switch (type) {
        case NL80211_TX_POWER_AUTOMATIC:
-               local->user_power_level = -1;
+               local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
                break;
        case NL80211_TX_POWER_LIMITED:
-               if (mbm < 0 || (mbm % 100))
-                       return -EOPNOTSUPP;
-               local->user_power_level = MBM_TO_DBM(mbm);
-               break;
        case NL80211_TX_POWER_FIXED:
                if (mbm < 0 || (mbm % 100))
                        return -EOPNOTSUPP;
-               /* TODO: move to cfg80211 when it knows the channel */
-               if (MBM_TO_DBM(mbm) > chan->max_power)
-                       return -EINVAL;
                local->user_power_level = MBM_TO_DBM(mbm);
                break;
        }
 
-       ieee80211_hw_config(local, changes);
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list)
+               sdata->user_power_level = local->user_power_level;
+       list_for_each_entry(sdata, &local->interfaces, list)
+               ieee80211_recalc_txpower(sdata);
+       mutex_unlock(&local->iflist_mtx);
 
        return 0;
 }
 
-static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
+static int ieee80211_get_tx_power(struct wiphy *wiphy,
+                                 struct wireless_dev *wdev,
+                                 int *dbm)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 
-       *dbm = local->hw.conf.power_level;
+       if (!local->use_chanctx)
+               *dbm = local->hw.conf.power_level;
+       else
+               *dbm = sdata->vif.bss_conf.txpower;
 
        return 0;
 }
@@ -2067,13 +2258,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 
        /*
         * If not associated, or current association is not an HT
-        * association, there's no need to send an action frame.
+        * association, there's no need to do anything, just store
+        * the new value until we associate.
         */
        if (!sdata->u.mgd.associated ||
-           sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
-               ieee80211_recalc_smps(sdata->local);
+           sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
                return 0;
-       }
 
        ap = sdata->u.mgd.associated->bssid;
 
@@ -2099,7 +2289,8 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+       if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                return -EOPNOTSUPP;
 
        if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
@@ -2179,7 +2370,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
 static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_channel *channel,
-                                   enum nl80211_channel_type channel_type,
                                    unsigned int duration, u64 *cookie,
                                    struct sk_buff *txskb)
 {
@@ -2189,12 +2379,14 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 
        lockdep_assert_held(&local->mtx);
 
+       if (local->use_chanctx && !local->ops->remain_on_channel)
+               return -EOPNOTSUPP;
+
        roc = kzalloc(sizeof(*roc), GFP_KERNEL);
        if (!roc)
                return -ENOMEM;
 
        roc->chan = channel;
-       roc->chan_type = channel_type;
        roc->duration = duration;
        roc->req_duration = duration;
        roc->frame = txskb;
@@ -2204,7 +2396,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        INIT_LIST_HEAD(&roc->dependents);
 
        /* if there's one pending or we're scanning, queue this one */
-       if (!list_empty(&local->roc_list) || local->scanning)
+       if (!list_empty(&local->roc_list) ||
+           local->scanning || local->radar_detect_enabled)
                goto out_check_combine;
 
        /* if not HW assist, just queue & schedule work */
@@ -2227,7 +2420,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!duration)
                duration = 10;
 
-       ret = drv_remain_on_channel(local, channel, channel_type, duration);
+       ret = drv_remain_on_channel(local, sdata, channel, duration);
        if (ret) {
                kfree(roc);
                return ret;
@@ -2238,7 +2431,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 
  out_check_combine:
        list_for_each_entry(tmp, &local->roc_list, list) {
-               if (tmp->chan != channel || tmp->chan_type != channel_type)
+               if (tmp->chan != channel || tmp->sdata != sdata)
                        continue;
 
                /*
@@ -2332,13 +2525,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                list_add_tail(&roc->list, &local->roc_list);
 
        /*
-        * cookie is either the roc (for normal roc)
+        * cookie is either the roc cookie (for normal roc)
         * or the SKB (for mgmt TX)
         */
-       if (txskb)
+       if (!txskb) {
+               /* local->mtx protects this */
+               local->roc_cookie_counter++;
+               roc->cookie = local->roc_cookie_counter;
+               /* wow, you wrapped 64 bits ... more likely a bug */
+               if (WARN_ON(roc->cookie == 0)) {
+                       roc->cookie = 1;
+                       local->roc_cookie_counter++;
+               }
+               *cookie = roc->cookie;
+       } else {
                *cookie = (unsigned long)txskb;
-       else
-               *cookie = (unsigned long)roc;
+       }
 
        return 0;
 }
@@ -2346,7 +2548,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 static int ieee80211_remain_on_channel(struct wiphy *wiphy,
                                       struct wireless_dev *wdev,
                                       struct ieee80211_channel *chan,
-                                      enum nl80211_channel_type channel_type,
                                       unsigned int duration,
                                       u64 *cookie)
 {
@@ -2355,7 +2556,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
        int ret;
 
        mutex_lock(&local->mtx);
-       ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+       ret = ieee80211_start_roc_work(local, sdata, chan,
                                       duration, cookie, NULL);
        mutex_unlock(&local->mtx);
 
@@ -2373,7 +2574,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
                struct ieee80211_roc_work *dep, *tmp2;
 
                list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
-                       if (!mgmt_tx && (unsigned long)dep != cookie)
+                       if (!mgmt_tx && dep->cookie != cookie)
                                continue;
                        else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
                                continue;
@@ -2385,7 +2586,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
                        return 0;
                }
 
-               if (!mgmt_tx && (unsigned long)roc != cookie)
+               if (!mgmt_tx && roc->cookie != cookie)
                        continue;
                else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
                        continue;
@@ -2446,12 +2647,41 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
        return ieee80211_cancel_roc(local, cookie, false);
 }
 
+static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+                                          struct net_device *dev,
+                                          struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       unsigned long timeout;
+       int err;
+
+       if (!list_empty(&local->roc_list) || local->scanning)
+               return -EBUSY;
+
+       /* 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;
+
+       mutex_lock(&local->iflist_mtx);
+       err = ieee80211_vif_use_channel(sdata, chandef,
+                                       IEEE80211_CHANCTX_SHARED);
+       mutex_unlock(&local->iflist_mtx);
+       if (err)
+               return err;
+
+       timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+       ieee80211_queue_delayed_work(&sdata->local->hw,
+                                    &sdata->dfs_cac_timer_work, timeout);
+
+       return 0;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct ieee80211_channel *chan, bool offchan,
-                            enum nl80211_channel_type channel_type,
-                            bool channel_type_valid, unsigned int wait,
-                            const u8 *buf, size_t len, bool no_cck,
-                            bool dont_wait_for_ack, u64 *cookie)
+                            unsigned int wait, const u8 *buf, size_t len,
+                            bool no_cck, bool dont_wait_for_ack, u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = sdata->local;
@@ -2515,10 +2745,16 @@ 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->def.chan;
+               else
                        need_offchan = true;
+               rcu_read_unlock();
        }
 
        if (need_offchan && !offchan) {
@@ -2546,13 +2782,14 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                goto out_unlock;
        }
 
-       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
+                                       IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
        if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
                IEEE80211_SKB_CB(skb)->hw_queue =
                        local->hw.offchannel_tx_hw_queue;
 
        /* This will handle all kinds of coalescing and immediate TX */
-       ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+       ret = ieee80211_start_roc_work(local, sdata, chan,
                                       wait, cookie, skb);
        if (ret)
                kfree_skb(skb);
@@ -2670,7 +2907,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 +2939,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 +2959,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 +2973,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 +3012,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 +3035,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:
@@ -2822,7 +3053,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_tx_info *info;
        struct sk_buff *skb = NULL;
        bool send_direct;
        int ret;
@@ -2848,7 +3078,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
        if (!skb)
                return -ENOMEM;
 
-       info = IEEE80211_SKB_CB(skb);
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
        switch (action_code) {
@@ -2985,12 +3214,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->def.chan->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 +3244,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,21 +3272,37 @@ 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;
 }
 
-static struct ieee80211_channel *
-ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
-                         enum nl80211_channel_type *type)
+static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
+                                    struct wireless_dev *wdev,
+                                    struct cfg80211_chan_def *chandef)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       int ret = -ENODATA;
+
+       rcu_read_lock();
+       if (local->use_chanctx) {
+               chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+               if (chanctx_conf) {
+                       *chandef = chanctx_conf->def;
+                       ret = 0;
+               }
+       } else if (local->open_count == local->monitors) {
+               *chandef = local->monitor_chandef;
+               ret = 0;
+       }
+       rcu_read_unlock();
 
-       *type = local->_oper_channel_type;
-       return local->oper_channel;
+       return ret;
 }
 
 #ifdef CONFIG_PM
@@ -3103,6 +3357,7 @@ struct cfg80211_ops mac80211_config_ops = {
        .disassoc = ieee80211_disassoc,
        .join_ibss = ieee80211_join_ibss,
        .leave_ibss = ieee80211_leave_ibss,
+       .set_mcast_rate = ieee80211_set_mcast_rate,
        .set_wiphy_params = ieee80211_set_wiphy_params,
        .set_tx_power = ieee80211_set_tx_power,
        .get_tx_power = ieee80211_get_tx_power,
@@ -3134,4 +3389,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .get_et_stats = ieee80211_get_et_stats,
        .get_et_strings = ieee80211_get_et_strings,
        .get_channel = ieee80211_cfg_get_channel,
+       .start_radar_detection = ieee80211_start_radar_detection,
 };