]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/mac80211/cfg.c
Merge tag 'for-v3.18' of git://git.infradead.org/battery-2.6
[karo-tx-linux.git] / net / mac80211 / cfg.c
index 592f4b152ba8a51e4d1c3b3bc3360286959ff1e0..fb6a1502b6dfa64980305c3bba380408ef3dde14 100644 (file)
@@ -2,6 +2,7 @@
  * mac80211 configuration hooks for cfg80211
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014  Intel Mobile Communications GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -468,330 +469,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
-static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
-{
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
-       struct rate_control_ref *ref = NULL;
-       struct timespec uptime;
-       u64 packets = 0;
-       u32 thr = 0;
-       int i, ac;
-
-       if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
-               ref = local->rate_ctrl;
-
-       sinfo->generation = sdata->local->sta_generation;
-
-       sinfo->filled = STATION_INFO_INACTIVE_TIME |
-                       STATION_INFO_RX_BYTES64 |
-                       STATION_INFO_TX_BYTES64 |
-                       STATION_INFO_RX_PACKETS |
-                       STATION_INFO_TX_PACKETS |
-                       STATION_INFO_TX_RETRIES |
-                       STATION_INFO_TX_FAILED |
-                       STATION_INFO_TX_BITRATE |
-                       STATION_INFO_RX_BITRATE |
-                       STATION_INFO_RX_DROP_MISC |
-                       STATION_INFO_BSS_PARAM |
-                       STATION_INFO_CONNECTED_TIME |
-                       STATION_INFO_STA_FLAGS |
-                       STATION_INFO_BEACON_LOSS_COUNT;
-
-       do_posix_clock_monotonic_gettime(&uptime);
-       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
-
-       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-       sinfo->tx_bytes = 0;
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               sinfo->tx_bytes += sta->tx_bytes[ac];
-               packets += sta->tx_packets[ac];
-       }
-       sinfo->tx_packets = packets;
-       sinfo->rx_bytes = sta->rx_bytes;
-       sinfo->rx_packets = sta->rx_packets;
-       sinfo->tx_retries = sta->tx_retry_count;
-       sinfo->tx_failed = sta->tx_retry_failed;
-       sinfo->rx_dropped_misc = sta->rx_dropped;
-       sinfo->beacon_loss_count = sta->beacon_loss_count;
-
-       if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
-           (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
-               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
-               if (!local->ops->get_rssi ||
-                   drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
-                       sinfo->signal = (s8)sta->last_signal;
-               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
-       }
-       if (sta->chains) {
-               sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
-                                STATION_INFO_CHAIN_SIGNAL_AVG;
-
-               sinfo->chains = sta->chains;
-               for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
-                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
-                       sinfo->chain_signal_avg[i] =
-                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
-               }
-       }
-
-       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-       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_LOCAL_PM |
-                                STATION_INFO_PEER_PM |
-                                STATION_INFO_NONPEER_PM;
-
-               sinfo->llid = sta->llid;
-               sinfo->plid = sta->plid;
-               sinfo->plink_state = sta->plink_state;
-               if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
-                       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
-       }
-
-       sinfo->bss_param.flags = 0;
-       if (sdata->vif.bss_conf.use_cts_prot)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
-       if (sdata->vif.bss_conf.use_short_preamble)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
-       if (sdata->vif.bss_conf.use_short_slot)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
-       sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
-       sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
-
-       sinfo->sta_flags.set = 0;
-       sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                               BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
-                               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);
-       if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (test_sta_flag(sta, WLAN_STA_WME))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
-       if (test_sta_flag(sta, WLAN_STA_MFP))
-               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);
-
-       /* check if the driver has a SW RC implementation */
-       if (ref && ref->ops->get_expected_throughput)
-               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
-       else
-               thr = drv_get_expected_throughput(local, &sta->sta);
-
-       if (thr != 0) {
-               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
-               sinfo->expected_throughput = thr;
-       }
-}
-
-static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
-       "rx_packets", "rx_bytes", "wep_weak_iv_count",
-       "rx_duplicates", "rx_fragments", "rx_dropped",
-       "tx_packets", "tx_bytes", "tx_fragments",
-       "tx_filtered", "tx_retry_failed", "tx_retries",
-       "beacon_loss", "sta_state", "txrate", "rxrate", "signal",
-       "channel", "noise", "ch_time", "ch_time_busy",
-       "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
-};
-#define STA_STATS_LEN  ARRAY_SIZE(ieee80211_gstrings_sta_stats)
-
-static int ieee80211_get_et_sset_count(struct wiphy *wiphy,
-                                      struct net_device *dev,
-                                      int sset)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int rv = 0;
-
-       if (sset == ETH_SS_STATS)
-               rv += STA_STATS_LEN;
-
-       rv += drv_get_et_sset_count(sdata, sset);
-
-       if (rv == 0)
-               return -EOPNOTSUPP;
-       return rv;
-}
-
-static void ieee80211_get_et_stats(struct wiphy *wiphy,
-                                  struct net_device *dev,
-                                  struct ethtool_stats *stats,
-                                  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;
-       struct survey_info survey;
-       int i, q;
-#define STA_STATS_SURVEY_LEN 7
-
-       memset(data, 0, sizeof(u64) * STA_STATS_LEN);
-
-#define ADD_STA_STATS(sta)                             \
-       do {                                            \
-               data[i++] += sta->rx_packets;           \
-               data[i++] += sta->rx_bytes;             \
-               data[i++] += sta->wep_weak_iv_count;    \
-               data[i++] += sta->num_duplicates;       \
-               data[i++] += sta->rx_fragments;         \
-               data[i++] += sta->rx_dropped;           \
-                                                       \
-               data[i++] += sinfo.tx_packets;          \
-               data[i++] += sinfo.tx_bytes;            \
-               data[i++] += sta->tx_fragments;         \
-               data[i++] += sta->tx_filtered_count;    \
-               data[i++] += sta->tx_retry_failed;      \
-               data[i++] += sta->tx_retry_count;       \
-               data[i++] += sta->beacon_loss_count;    \
-       } while (0)
-
-       /* For Managed stations, find the single station based on BSSID
-        * and use that.  For interface types, iterate through all available
-        * stations and add stats for any station that is assigned to this
-        * network device.
-        */
-
-       mutex_lock(&local->sta_mtx);
-
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
-
-               if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
-                       goto do_survey;
-
-               sinfo.filled = 0;
-               sta_set_sinfo(sta, &sinfo);
-
-               i = 0;
-               ADD_STA_STATS(sta);
-
-               data[i++] = sta->sta_state;
-
-
-               if (sinfo.filled & STATION_INFO_TX_BITRATE)
-                       data[i] = 100000 *
-                               cfg80211_calculate_bitrate(&sinfo.txrate);
-               i++;
-               if (sinfo.filled & STATION_INFO_RX_BITRATE)
-                       data[i] = 100000 *
-                               cfg80211_calculate_bitrate(&sinfo.rxrate);
-               i++;
-
-               if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
-                       data[i] = (u8)sinfo.signal_avg;
-               i++;
-       } else {
-               list_for_each_entry(sta, &local->sta_list, list) {
-                       /* Make sure this station belongs to the proper dev */
-                       if (sta->sdata->dev != dev)
-                               continue;
-
-                       sinfo.filled = 0;
-                       sta_set_sinfo(sta, &sinfo);
-                       i = 0;
-                       ADD_STA_STATS(sta);
-               }
-       }
-
-do_survey:
-       i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
-       /* Get survey stats for current channel */
-       survey.filled = 0;
-
-       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)
-               data[i++] = survey.channel->center_freq;
-       else
-               data[i++] = 0;
-       if (survey.filled & SURVEY_INFO_NOISE_DBM)
-               data[i++] = (u8)survey.noise;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
-               data[i++] = survey.channel_time;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
-               data[i++] = survey.channel_time_busy;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
-               data[i++] = survey.channel_time_ext_busy;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
-               data[i++] = survey.channel_time_rx;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
-               data[i++] = survey.channel_time_tx;
-       else
-               data[i++] = -1LL;
-
-       mutex_unlock(&local->sta_mtx);
-
-       if (WARN_ON(i != STA_STATS_LEN))
-               return;
-
-       drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
-}
-
-static void ieee80211_get_et_strings(struct wiphy *wiphy,
-                                    struct net_device *dev,
-                                    u32 sset, u8 *data)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int sz_sta_stats = 0;
-
-       if (sset == ETH_SS_STATS) {
-               sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
-               memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
-       }
-       drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
-}
-
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
                                  int idx, u8 *mac, struct station_info *sinfo)
 {
@@ -878,7 +555,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 }
 
 static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
-                                   const u8 *resp, size_t resp_len)
+                                   const u8 *resp, size_t resp_len,
+                                   const struct ieee80211_csa_settings *csa)
 {
        struct probe_resp *new, *old;
 
@@ -894,6 +572,11 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
        new->len = resp_len;
        memcpy(new->data, resp, resp_len);
 
+       if (csa)
+               memcpy(new->csa_counter_offsets, csa->counter_offsets_presp,
+                      csa->n_counter_offsets_presp *
+                      sizeof(new->csa_counter_offsets[0]));
+
        rcu_assign_pointer(sdata->u.ap.probe_resp, new);
        if (old)
                kfree_rcu(old, rcu_head);
@@ -902,7 +585,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
 }
 
 static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-                                  struct cfg80211_beacon_data *params)
+                                  struct cfg80211_beacon_data *params,
+                                  const struct ieee80211_csa_settings *csa)
 {
        struct beacon_data *new, *old;
        int new_head_len, new_tail_len;
@@ -946,6 +630,13 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
        new->head_len = new_head_len;
        new->tail_len = new_tail_len;
 
+       if (csa) {
+               new->csa_current_counter = csa->count;
+               memcpy(new->csa_counter_offsets, csa->counter_offsets_beacon,
+                      csa->n_counter_offsets_beacon *
+                      sizeof(new->csa_counter_offsets[0]));
+       }
+
        /* copy in head */
        if (params->head)
                memcpy(new->head, params->head, new_head_len);
@@ -960,7 +651,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
                        memcpy(new->tail, old->tail, new_tail_len);
 
        err = ieee80211_set_probe_resp(sdata, params->probe_resp,
-                                      params->probe_resp_len);
+                                      params->probe_resp_len, csa);
        if (err < 0)
                return err;
        if (err == 0)
@@ -992,8 +683,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        if (old)
                return -EALREADY;
 
-       /* TODO: make hostapd tell us what it wants */
-       sdata->smps_mode = IEEE80211_SMPS_OFF;
+       switch (params->smps_mode) {
+       case NL80211_SMPS_OFF:
+               sdata->smps_mode = IEEE80211_SMPS_OFF;
+               break;
+       case NL80211_SMPS_STATIC:
+               sdata->smps_mode = IEEE80211_SMPS_STATIC;
+               break;
+       case NL80211_SMPS_DYNAMIC:
+               sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+               break;
+       default:
+               return -EINVAL;
+       }
        sdata->needed_rx_chains = sdata->local->rx_chains;
 
        mutex_lock(&local->mtx);
@@ -1045,7 +747,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
                                        IEEE80211_P2P_OPPPS_ENABLE_BIT;
 
-       err = ieee80211_assign_beacon(sdata, &params->beacon);
+       err = ieee80211_assign_beacon(sdata, &params->beacon, NULL);
        if (err < 0) {
                ieee80211_vif_release_channel(sdata);
                return err;
@@ -1093,38 +795,13 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        if (!old)
                return -ENOENT;
 
-       err = ieee80211_assign_beacon(sdata, params);
+       err = ieee80211_assign_beacon(sdata, params, NULL);
        if (err < 0)
                return err;
        ieee80211_bss_info_change_notify(sdata, err);
        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);
@@ -1144,10 +821,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
+
        mutex_unlock(&local->mtx);
 
        kfree(sdata->u.ap.next_beacon);
@@ -1330,9 +1009,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                }
        }
 
-       ret = sta_apply_auth_flags(local, sta, mask, set);
-       if (ret)
-               return ret;
+       /* auth flags will be set later for TDLS stations */
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               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))
@@ -1341,15 +1023,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                        clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
        }
 
-       if (mask & BIT(NL80211_STA_FLAG_WME)) {
-               if (set & BIT(NL80211_STA_FLAG_WME)) {
-                       set_sta_flag(sta, WLAN_STA_WME);
-                       sta->sta.wme = true;
-               } else {
-                       clear_sta_flag(sta, WLAN_STA_WME);
-                       sta->sta.wme = false;
-               }
-       }
+       if (mask & BIT(NL80211_STA_FLAG_WME))
+               sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
 
        if (mask & BIT(NL80211_STA_FLAG_MFP)) {
                if (set & BIT(NL80211_STA_FLAG_MFP))
@@ -1469,6 +1144,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 #endif
        }
 
+       /* set the STA state after all sta info from usermode has been set */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               ret = sta_apply_auth_flags(local, sta, mask, set);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -2307,8 +1989,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
                        return err;
        }
 
-       if (changed & WIPHY_PARAM_COVERAGE_CLASS) {
-               err = drv_set_coverage_class(local, wiphy->coverage_class);
+       if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
+           (changed & WIPHY_PARAM_DYN_ACK)) {
+               s16 coverage_class;
+
+               coverage_class = changed & WIPHY_PARAM_COVERAGE_CLASS ?
+                                       wiphy->coverage_class : -1;
+               err = drv_set_coverage_class(local, coverage_class);
 
                if (err)
                        return err;
@@ -2681,6 +2368,58 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
        return 0;
 }
 
+static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
+                                          struct ieee80211_roc_work *new_roc,
+                                          struct ieee80211_roc_work *cur_roc)
+{
+       unsigned long j = jiffies;
+       unsigned long cur_roc_end = cur_roc->hw_start_time +
+                                   msecs_to_jiffies(cur_roc->duration);
+       struct ieee80211_roc_work *next_roc;
+       int new_dur;
+
+       if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
+               return false;
+
+       if (time_after(j + IEEE80211_ROC_MIN_LEFT, cur_roc_end))
+               return false;
+
+       ieee80211_handle_roc_started(new_roc);
+
+       new_dur = new_roc->duration - jiffies_to_msecs(cur_roc_end - j);
+
+       /* cur_roc is long enough - add new_roc to the dependents list. */
+       if (new_dur <= 0) {
+               list_add_tail(&new_roc->list, &cur_roc->dependents);
+               return true;
+       }
+
+       new_roc->duration = new_dur;
+
+       /*
+        * if cur_roc was already coalesced before, we might
+        * want to extend the next roc instead of adding
+        * a new one.
+        */
+       next_roc = list_entry(cur_roc->list.next,
+                             struct ieee80211_roc_work, list);
+       if (&next_roc->list != &local->roc_list &&
+           next_roc->chan == new_roc->chan &&
+           next_roc->sdata == new_roc->sdata &&
+           !WARN_ON(next_roc->started)) {
+               list_add_tail(&new_roc->list, &next_roc->dependents);
+               next_roc->duration = max(next_roc->duration,
+                                        new_roc->duration);
+               next_roc->type = max(next_roc->type, new_roc->type);
+               return true;
+       }
+
+       /* add right after cur_roc */
+       list_add(&new_roc->list, &cur_roc->list);
+
+       return true;
+}
+
 static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_channel *channel,
@@ -2786,8 +2525,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 
                /* If it has already started, it's more difficult ... */
                if (local->ops->remain_on_channel) {
-                       unsigned long j = jiffies;
-
                        /*
                         * In the offloaded ROC case, if it hasn't begun, add
                         * this new one to the dependent list to be handled
@@ -2810,28 +2547,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                break;
                        }
 
-                       if (time_before(j + IEEE80211_ROC_MIN_LEFT,
-                                       tmp->hw_start_time +
-                                       msecs_to_jiffies(tmp->duration))) {
-                               int new_dur;
-
-                               ieee80211_handle_roc_started(roc);
-
-                               new_dur = roc->duration -
-                                         jiffies_to_msecs(tmp->hw_start_time +
-                                                          msecs_to_jiffies(
-                                                               tmp->duration) -
-                                                          j);
-
-                               if (new_dur > 0) {
-                                       /* add right after tmp */
-                                       list_add(&roc->list, &tmp->list);
-                               } else {
-                                       list_add_tail(&roc->list,
-                                                     &tmp->dependents);
-                               }
+                       if (ieee80211_coalesce_started_roc(local, roc, tmp))
                                queued = true;
-                       }
                } else if (del_timer_sync(&tmp->work.timer)) {
                        unsigned long new_end;
 
@@ -3076,7 +2793,8 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
-               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
+                                             NULL);
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
 
@@ -3114,17 +2832,35 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 
        sdata_assert_lock(sdata);
        lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       sdata->radar_required = sdata->csa_radar_required;
-       err = ieee80211_vif_change_channel(sdata, &changed);
-       if (err < 0)
-               return err;
+       /*
+        * using reservation isn't immediate as it may be deferred until later
+        * with multi-vif. once reservation is complete it will re-schedule the
+        * work with no reserved_chanctx so verify chandef to check if it
+        * completed successfully
+        */
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               ieee80211_hw_config(local, 0);
+       if (sdata->reserved_chanctx) {
+               /*
+                * with multi-vif csa driver may call ieee80211_csa_finish()
+                * many times while waiting for other interfaces to use their
+                * reservations
+                */
+               if (sdata->reserved_ready)
+                       return 0;
+
+               err = ieee80211_vif_use_reserved_context(sdata);
+               if (err)
+                       return err;
+
+               return 0;
        }
 
+       if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
+                                       &sdata->csa_chandef))
+               return -EINVAL;
+
        sdata->vif.csa_active = false;
 
        err = ieee80211_set_after_csa_beacon(sdata, &changed);
@@ -3134,10 +2870,11 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
        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);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
 
        return 0;
 }
@@ -3160,6 +2897,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
 
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
+       mutex_lock(&local->chanctx_mtx);
 
        /* AP might have been stopped while waiting for the lock. */
        if (!sdata->vif.csa_active)
@@ -3171,6 +2909,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        ieee80211_csa_finalize(sdata);
 
 unlock:
+       mutex_unlock(&local->chanctx_mtx);
        mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
@@ -3179,6 +2918,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                                    struct cfg80211_csa_settings *params,
                                    u32 *changed)
 {
+       struct ieee80211_csa_settings csa = {};
        int err;
 
        switch (sdata->vif.type) {
@@ -3213,20 +2953,13 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                     IEEE80211_MAX_CSA_COUNTERS_NUM))
                        return -EINVAL;
 
-               /* make sure we don't have garbage in other counters */
-               memset(sdata->csa_counter_offset_beacon, 0,
-                      sizeof(sdata->csa_counter_offset_beacon));
-               memset(sdata->csa_counter_offset_presp, 0,
-                      sizeof(sdata->csa_counter_offset_presp));
+               csa.counter_offsets_beacon = params->counter_offsets_beacon;
+               csa.counter_offsets_presp = params->counter_offsets_presp;
+               csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
+               csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
+               csa.count = params->count;
 
-               memcpy(sdata->csa_counter_offset_beacon,
-                      params->counter_offsets_beacon,
-                      params->n_counter_offsets_beacon * sizeof(u16));
-               memcpy(sdata->csa_counter_offset_presp,
-                      params->counter_offsets_presp,
-                      params->n_counter_offsets_presp * sizeof(u16));
-
-               err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+               err = ieee80211_assign_beacon(sdata, &params->beacon_csa, &csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
@@ -3322,7 +3055,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
-       int err, num_chanctx, changed = 0;
+       int err, changed = 0;
 
        sdata_assert_lock(sdata);
        lockdep_assert_held(&local->mtx);
@@ -3337,46 +3070,50 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                       &sdata->vif.bss_conf.chandef))
                return -EINVAL;
 
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
        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;
+               err = -EBUSY;
+               goto out;
        }
 
-       /* 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;
+       if (!chanctx) {
+               err = -EBUSY;
+               goto out;
        }
-       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;
+       err = ieee80211_vif_reserve_chanctx(sdata, &params->chandef,
+                                           chanctx->mode,
+                                           params->radar_required);
+       if (err)
+               goto out;
 
-       /* don't allow another channel switch if one is already active. */
-       if (sdata->vif.csa_active)
-               return -EBUSY;
+       /* if reservation is invalid then this will fail */
+       err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+               goto out;
+       }
 
        err = ieee80211_set_csa_beacon(sdata, params, &changed);
-       if (err)
-               return err;
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+               goto out;
+       }
 
-       sdata->csa_radar_required = params->radar_required;
        sdata->csa_chandef = params->chandef;
        sdata->csa_block_tx = params->block_tx;
-       sdata->csa_current_counter = params->count;
        sdata->vif.csa_active = true;
 
        if (sdata->csa_block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+               ieee80211_stop_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
 
        if (changed) {
                ieee80211_bss_info_change_notify(sdata, changed);
@@ -3386,7 +3123,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                ieee80211_csa_finalize(sdata);
        }
 
-       return 0;
+out:
+       mutex_unlock(&local->chanctx_mtx);
+       return err;
 }
 
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
@@ -3518,10 +3257,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
             sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            params->n_csa_offsets) {
                int i;
-               u8 c = sdata->csa_current_counter;
+               struct beacon_data *beacon = NULL;
+
+               rcu_read_lock();
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       beacon = rcu_dereference(sdata->u.ap.beacon);
+               else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                       beacon = rcu_dereference(sdata->u.ibss.presp);
+               else if (ieee80211_vif_is_mesh(&sdata->vif))
+                       beacon = rcu_dereference(sdata->u.mesh.beacon);
 
-               for (i = 0; i < params->n_csa_offsets; i++)
-                       data[params->csa_offsets[i]] = c;
+               if (beacon)
+                       for (i = 0; i < params->n_csa_offsets; i++)
+                               data[params->csa_offsets[i]] =
+                                       beacon->csa_current_counter;
+
+               rcu_read_unlock();
        }
 
        IEEE80211_SKB_CB(skb)->flags = flags;
@@ -3601,21 +3353,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
        return drv_get_antenna(local, tx_ant, rx_ant);
 }
 
-static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
-{
-       struct ieee80211_local *local = wiphy_priv(wiphy);
-
-       return drv_set_ringparam(local, tx, rx);
-}
-
-static void ieee80211_get_ringparam(struct wiphy *wiphy,
-                                   u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
-{
-       struct ieee80211_local *local = wiphy_priv(wiphy);
-
-       drv_get_ringparam(local, tx, tx_max, rx, rx_max);
-}
-
 static int ieee80211_set_rekey_data(struct wiphy *wiphy,
                                    struct net_device *dev,
                                    struct cfg80211_gtk_rekey_data *data)
@@ -3655,7 +3392,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        band = chanctx_conf->def.chan->band;
        sta = sta_info_get_bss(sdata, peer);
        if (sta) {
-               qos = test_sta_flag(sta, WLAN_STA_WME);
+               qos = sta->sta.wme;
        } else {
                rcu_read_unlock();
                return -ENOLINK;
@@ -3847,8 +3584,6 @@ const struct cfg80211_ops mac80211_config_ops = {
        .mgmt_frame_register = ieee80211_mgmt_frame_register,
        .set_antenna = ieee80211_set_antenna,
        .get_antenna = ieee80211_get_antenna,
-       .set_ringparam = ieee80211_set_ringparam,
-       .get_ringparam = ieee80211_get_ringparam,
        .set_rekey_data = ieee80211_set_rekey_data,
        .tdls_oper = ieee80211_tdls_oper,
        .tdls_mgmt = ieee80211_tdls_mgmt,
@@ -3857,9 +3592,6 @@ const struct cfg80211_ops mac80211_config_ops = {
 #ifdef CONFIG_PM
        .set_wakeup = ieee80211_set_wakeup,
 #endif
-       .get_et_sset_count = ieee80211_get_et_sset_count,
-       .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,
        .channel_switch = ieee80211_channel_switch,