Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 30 Apr 2014 16:04:27 +0000 (12:04 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 30 Apr 2014 16:04:27 +0000 (12:04 -0400)
Conflicts:
net/mac80211/chan.c

1  2 
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
net/mac80211/chan.c
net/mac80211/main.c

index 8385a7ad02acd626761d4e41ed8f4081d0a9042d,d1df99350147d6145219d34aeaa2e5adb82955eb..0ac5437492fd5c100b95f7f576491d2d6d5d0025
@@@ -165,7 -165,7 +165,7 @@@ static int ath10k_clear_peer_keys(struc
                        first_errno = ret;
  
                if (ret)
 -                      ath10k_warn("could not remove peer wep key %d (%d)\n",
 +                      ath10k_warn("failed to remove peer wep key %d: %d\n",
                                    i, ret);
  
                peer->keys[i] = NULL;
@@@ -213,8 -213,7 +213,8 @@@ static int ath10k_clear_vdev_key(struc
                        first_errno = ret;
  
                if (ret)
 -                      ath10k_warn("could not remove key for %pM\n", addr);
 +                      ath10k_warn("failed to remove key for %pM: %d\n",
 +                                  addr, ret);
        }
  
        return first_errno;
@@@ -324,14 -323,14 +324,14 @@@ static int ath10k_peer_create(struct at
  
        ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
        if (ret) {
 -              ath10k_warn("Failed to create wmi peer %pM on vdev %i: %i\n",
 +              ath10k_warn("failed to create wmi peer %pM on vdev %i: %i\n",
                            addr, vdev_id, ret);
                return ret;
        }
  
        ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
        if (ret) {
 -              ath10k_warn("Failed to wait for created wmi peer %pM on vdev %i: %i\n",
 +              ath10k_warn("failed to wait for created wmi peer %pM on vdev %i: %i\n",
                            addr, vdev_id, ret);
                return ret;
        }
@@@ -352,7 -351,7 +352,7 @@@ static int ath10k_mac_set_kickout(struc
        ret = ath10k_wmi_pdev_set_param(ar, param,
                                        ATH10K_KICKOUT_THRESHOLD);
        if (ret) {
 -              ath10k_warn("Failed to set kickout threshold on vdev %i: %d\n",
 +              ath10k_warn("failed to set kickout threshold on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MIN_IDLE);
        if (ret) {
 -              ath10k_warn("Failed to set keepalive minimum idle time on vdev %i : %d\n",
 +              ath10k_warn("failed to set keepalive minimum idle time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MAX_IDLE);
        if (ret) {
 -              ath10k_warn("Failed to set keepalive maximum idle time on vdev %i: %d\n",
 +              ath10k_warn("failed to set keepalive maximum idle time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
        if (ret) {
 -              ath10k_warn("Failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
 +              ath10k_warn("failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@@ -489,20 -488,92 +489,20 @@@ static inline int ath10k_vdev_setup_syn
        return 0;
  }
  
 -static int ath10k_vdev_start(struct ath10k_vif *arvif)
 +static bool ath10k_monitor_is_enabled(struct ath10k *ar)
  {
 -      struct ath10k *ar = arvif->ar;
 -      struct cfg80211_chan_def *chandef = &ar->chandef;
 -      struct wmi_vdev_start_request_arg arg = {};
 -      int ret = 0;
 -
        lockdep_assert_held(&ar->conf_mutex);
  
 -      reinit_completion(&ar->vdev_setup_done);
 -
 -      arg.vdev_id = arvif->vdev_id;
 -      arg.dtim_period = arvif->dtim_period;
 -      arg.bcn_intval = arvif->beacon_interval;
 -
 -      arg.channel.freq = chandef->chan->center_freq;
 -      arg.channel.band_center_freq1 = chandef->center_freq1;
 -      arg.channel.mode = chan_to_phymode(chandef);
 -
 -      arg.channel.min_power = 0;
 -      arg.channel.max_power = chandef->chan->max_power * 2;
 -      arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
 -      arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
 -
 -      if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
 -              arg.ssid = arvif->u.ap.ssid;
 -              arg.ssid_len = arvif->u.ap.ssid_len;
 -              arg.hidden_ssid = arvif->u.ap.hidden_ssid;
 -
 -              /* For now allow DFS for AP mode */
 -              arg.channel.chan_radar =
 -                      !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
 -      } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
 -              arg.ssid = arvif->vif->bss_conf.ssid;
 -              arg.ssid_len = arvif->vif->bss_conf.ssid_len;
 -      }
 -
        ath10k_dbg(ATH10K_DBG_MAC,
 -                 "mac vdev %d start center_freq %d phymode %s\n",
 -                 arg.vdev_id, arg.channel.freq,
 -                 ath10k_wmi_phymode_str(arg.channel.mode));
 -
 -      ret = ath10k_wmi_vdev_start(ar, &arg);
 -      if (ret) {
 -              ath10k_warn("WMI vdev %i start failed: ret %d\n",
 -                          arg.vdev_id, ret);
 -              return ret;
 -      }
 -
 -      ret = ath10k_vdev_setup_sync(ar);
 -      if (ret) {
 -              ath10k_warn("vdev %i setup failed %d\n",
 -                          arg.vdev_id, ret);
 -              return ret;
 -      }
 -
 -      return ret;
 -}
 -
 -static int ath10k_vdev_stop(struct ath10k_vif *arvif)
 -{
 -      struct ath10k *ar = arvif->ar;
 -      int ret;
 +                 "mac monitor refs: promisc %d monitor %d cac %d\n",
 +                 ar->promisc, ar->monitor,
 +                 test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags));
  
 -      lockdep_assert_held(&ar->conf_mutex);
 -
 -      reinit_completion(&ar->vdev_setup_done);
 -
 -      ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
 -      if (ret) {
 -              ath10k_warn("WMI vdev %i stop failed: ret %d\n",
 -                          arvif->vdev_id, ret);
 -              return ret;
 -      }
 -
 -      ret = ath10k_vdev_setup_sync(ar);
 -      if (ret) {
 -              ath10k_warn("vdev %i setup sync failed %d\n",
 -                          arvif->vdev_id, ret);
 -              return ret;
 -      }
 -
 -      return ret;
 +      return ar->promisc || ar->monitor ||
 +             test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
  }
  
 -static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 +static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
  {
        struct cfg80211_chan_def *chandef = &ar->chandef;
        struct ieee80211_channel *channel = chandef->chan;
  
        lockdep_assert_held(&ar->conf_mutex);
  
 -      if (!ar->monitor_present) {
 -              ath10k_warn("mac montor stop -- monitor is not present\n");
 -              return -EINVAL;
 -      }
 -
        arg.vdev_id = vdev_id;
        arg.channel.freq = channel->center_freq;
        arg.channel.band_center_freq1 = chandef->center_freq1;
  
        ret = ath10k_wmi_vdev_start(ar, &arg);
        if (ret) {
 -              ath10k_warn("Monitor vdev %i start failed: ret %d\n",
 +              ath10k_warn("failed to request monitor vdev %i start: %d\n",
                            vdev_id, ret);
                return ret;
        }
  
        ret = ath10k_vdev_setup_sync(ar);
        if (ret) {
 -              ath10k_warn("Monitor vdev %i setup failed %d\n",
 +              ath10k_warn("failed to synchronize setup for monitor vdev %i: %d\n",
                            vdev_id, ret);
                return ret;
        }
  
        ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
        if (ret) {
 -              ath10k_warn("Monitor vdev %i up failed: %d\n",
 +              ath10k_warn("failed to put up monitor vdev %i: %d\n",
                            vdev_id, ret);
                goto vdev_stop;
        }
  
        ar->monitor_vdev_id = vdev_id;
 -      ar->monitor_enabled = true;
  
 +      ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
 +                 ar->monitor_vdev_id);
        return 0;
  
  vdev_stop:
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
 -              ath10k_warn("Monitor vdev %i stop failed: %d\n",
 +              ath10k_warn("failed to stop monitor vdev %i after start failure: %d\n",
                            ar->monitor_vdev_id, ret);
  
        return ret;
  }
  
 -static int ath10k_monitor_stop(struct ath10k *ar)
 +static int ath10k_monitor_vdev_stop(struct ath10k *ar)
  {
        int ret = 0;
  
        lockdep_assert_held(&ar->conf_mutex);
  
 -      if (!ar->monitor_present) {
 -              ath10k_warn("mac montor stop -- monitor is not present\n");
 -              return -EINVAL;
 -      }
 -
 -      if (!ar->monitor_enabled) {
 -              ath10k_warn("mac montor stop -- monitor is not enabled\n");
 -              return -EINVAL;
 -      }
 -
        ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
        if (ret)
 -              ath10k_warn("Monitor vdev %i down failed: %d\n",
 +              ath10k_warn("failed to put down monitor vdev %i: %d\n",
                            ar->monitor_vdev_id, ret);
  
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
 -              ath10k_warn("Monitor vdev %i stop failed: %d\n",
 +              ath10k_warn("failed to to request monitor vdev %i stop: %d\n",
                            ar->monitor_vdev_id, ret);
  
        ret = ath10k_vdev_setup_sync(ar);
        if (ret)
 -              ath10k_warn("Monitor_down sync failed, vdev %i: %d\n",
 +              ath10k_warn("failed to synchronise monitor vdev %i: %d\n",
                            ar->monitor_vdev_id, ret);
  
 -      ar->monitor_enabled = false;
 +      ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
 +                 ar->monitor_vdev_id);
        return ret;
  }
  
 -static int ath10k_monitor_create(struct ath10k *ar)
 +static int ath10k_monitor_vdev_create(struct ath10k *ar)
  {
        int bit, ret = 0;
  
        lockdep_assert_held(&ar->conf_mutex);
  
 -      if (ar->monitor_present) {
 -              ath10k_warn("Monitor mode already enabled\n");
 -              return 0;
 -      }
 -
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
 -              ath10k_warn("No free VDEV slots\n");
 +              ath10k_warn("failed to find free vdev id for monitor vdev\n");
                return -ENOMEM;
        }
  
                                     WMI_VDEV_TYPE_MONITOR,
                                     0, ar->mac_addr);
        if (ret) {
 -              ath10k_warn("WMI vdev %i monitor create failed: ret %d\n",
 +              ath10k_warn("failed to request monitor vdev %i creation: %d\n",
                            ar->monitor_vdev_id, ret);
                goto vdev_fail;
        }
        ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
                   ar->monitor_vdev_id);
  
 -      ar->monitor_present = true;
        return 0;
  
  vdev_fail:
        return ret;
  }
  
 -static int ath10k_monitor_destroy(struct ath10k *ar)
 +static int ath10k_monitor_vdev_delete(struct ath10k *ar)
  {
        int ret = 0;
  
        lockdep_assert_held(&ar->conf_mutex);
  
 -      if (!ar->monitor_present)
 -              return 0;
 -
        ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
        if (ret) {
 -              ath10k_warn("WMI vdev %i monitor delete failed: %d\n",
 +              ath10k_warn("failed to request wmi monitor vdev %i removal: %d\n",
                            ar->monitor_vdev_id, ret);
                return ret;
        }
  
        ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
 -      ar->monitor_present = false;
  
        ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
                   ar->monitor_vdev_id);
        return ret;
  }
  
 -static int ath10k_start_cac(struct ath10k *ar)
 +static int ath10k_monitor_start(struct ath10k *ar)
  {
        int ret;
  
        lockdep_assert_held(&ar->conf_mutex);
  
 -      set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 +      if (!ath10k_monitor_is_enabled(ar)) {
 +              ath10k_warn("trying to start monitor with no references\n");
 +              return 0;
 +      }
 +
 +      if (ar->monitor_started) {
 +              ath10k_dbg(ATH10K_DBG_MAC, "mac monitor already started\n");
 +              return 0;
 +      }
  
 -      ret = ath10k_monitor_create(ar);
 +      ret = ath10k_monitor_vdev_create(ar);
        if (ret) {
 -              clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 +              ath10k_warn("failed to create monitor vdev: %d\n", ret);
                return ret;
        }
  
 -      ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
 +      ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
        if (ret) {
 +              ath10k_warn("failed to start monitor vdev: %d\n", ret);
 +              ath10k_monitor_vdev_delete(ar);
 +              return ret;
 +      }
 +
 +      ar->monitor_started = true;
 +      ath10k_dbg(ATH10K_DBG_MAC, "mac monitor started\n");
 +
 +      return 0;
 +}
 +
 +static void ath10k_monitor_stop(struct ath10k *ar)
 +{
 +      int ret;
 +
 +      lockdep_assert_held(&ar->conf_mutex);
 +
 +      if (ath10k_monitor_is_enabled(ar)) {
 +              ath10k_dbg(ATH10K_DBG_MAC,
 +                         "mac monitor will be stopped later\n");
 +              return;
 +      }
 +
 +      if (!ar->monitor_started) {
 +              ath10k_dbg(ATH10K_DBG_MAC,
 +                         "mac monitor probably failed to start earlier\n");
 +              return;
 +      }
 +
 +      ret = ath10k_monitor_vdev_stop(ar);
 +      if (ret)
 +              ath10k_warn("failed to stop monitor vdev: %d\n", ret);
 +
 +      ret = ath10k_monitor_vdev_delete(ar);
 +      if (ret)
 +              ath10k_warn("failed to delete monitor vdev: %d\n", ret);
 +
 +      ar->monitor_started = false;
 +      ath10k_dbg(ATH10K_DBG_MAC, "mac monitor stopped\n");
 +}
 +
 +static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif)
 +{
 +      struct ath10k *ar = arvif->ar;
 +      u32 vdev_param, rts_cts = 0;
 +
 +      lockdep_assert_held(&ar->conf_mutex);
 +
 +      vdev_param = ar->wmi.vdev_param->enable_rtscts;
 +
 +      if (arvif->use_cts_prot || arvif->num_legacy_stations > 0)
 +              rts_cts |= SM(WMI_RTSCTS_ENABLED, WMI_RTSCTS_SET);
 +
 +      if (arvif->num_legacy_stations > 0)
 +              rts_cts |= SM(WMI_RTSCTS_ACROSS_SW_RETRIES,
 +                            WMI_RTSCTS_PROFILE);
 +
 +      return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 +                                       rts_cts);
 +}
 +
 +static int ath10k_start_cac(struct ath10k *ar)
 +{
 +      int ret;
 +
 +      lockdep_assert_held(&ar->conf_mutex);
 +
 +      set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 +
 +      ret = ath10k_monitor_start(ar);
 +      if (ret) {
 +              ath10k_warn("failed to start monitor (cac): %d\n", ret);
                clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 -              ath10k_monitor_destroy(ar);
                return ret;
        }
  
@@@ -759,26 -774,58 +759,26 @@@ static int ath10k_stop_cac(struct ath10
        if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
                return 0;
  
 -      ath10k_monitor_stop(ar);
 -      ath10k_monitor_destroy(ar);
        clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 +      ath10k_monitor_stop(ar);
  
        ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
  
        return 0;
  }
  
 -static const char *ath10k_dfs_state(enum nl80211_dfs_state dfs_state)
 -{
 -      switch (dfs_state) {
 -      case NL80211_DFS_USABLE:
 -              return "USABLE";
 -      case NL80211_DFS_UNAVAILABLE:
 -              return "UNAVAILABLE";
 -      case NL80211_DFS_AVAILABLE:
 -              return "AVAILABLE";
 -      default:
 -              WARN_ON(1);
 -              return "bug";
 -      }
 -}
 -
 -static void ath10k_config_radar_detection(struct ath10k *ar)
 +static void ath10k_recalc_radar_detection(struct ath10k *ar)
  {
 -      struct ieee80211_channel *chan = ar->hw->conf.chandef.chan;
 -      bool radar = ar->hw->conf.radar_enabled;
 -      bool chan_radar = !!(chan->flags & IEEE80211_CHAN_RADAR);
 -      enum nl80211_dfs_state dfs_state = chan->dfs_state;
        int ret;
  
        lockdep_assert_held(&ar->conf_mutex);
  
 -      ath10k_dbg(ATH10K_DBG_MAC,
 -                 "mac radar config update: chan %dMHz radar %d chan radar %d chan state %s\n",
 -                 chan->center_freq, radar, chan_radar,
 -                 ath10k_dfs_state(dfs_state));
 -
 -      /*
 -       * It's safe to call it even if CAC is not started.
 -       * This call here guarantees changing channel, etc. will stop CAC.
 -       */
        ath10k_stop_cac(ar);
  
 -      if (!radar)
 -              return;
 -
 -      if (!chan_radar)
 +      if (!ar->radar_enabled)
                return;
  
 -      if (dfs_state != NL80211_DFS_USABLE)
 +      if (ar->num_started_vdevs > 0)
                return;
  
        ret = ath10k_start_cac(ar);
                 * radiation is not allowed, make this channel DFS_UNAVAILABLE
                 * by indicating that radar was detected.
                 */
 -              ath10k_warn("failed to start CAC (%d)\n", ret);
 +              ath10k_warn("failed to start CAC: %d\n", ret);
                ieee80211_radar_detected(ar->hw);
        }
  }
  
 +static int ath10k_vdev_start(struct ath10k_vif *arvif)
 +{
 +      struct ath10k *ar = arvif->ar;
 +      struct cfg80211_chan_def *chandef = &ar->chandef;
 +      struct wmi_vdev_start_request_arg arg = {};
 +      int ret = 0;
 +
 +      lockdep_assert_held(&ar->conf_mutex);
 +
 +      reinit_completion(&ar->vdev_setup_done);
 +
 +      arg.vdev_id = arvif->vdev_id;
 +      arg.dtim_period = arvif->dtim_period;
 +      arg.bcn_intval = arvif->beacon_interval;
 +
 +      arg.channel.freq = chandef->chan->center_freq;
 +      arg.channel.band_center_freq1 = chandef->center_freq1;
 +      arg.channel.mode = chan_to_phymode(chandef);
 +
 +      arg.channel.min_power = 0;
 +      arg.channel.max_power = chandef->chan->max_power * 2;
 +      arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
 +      arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
 +
 +      if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
 +              arg.ssid = arvif->u.ap.ssid;
 +              arg.ssid_len = arvif->u.ap.ssid_len;
 +              arg.hidden_ssid = arvif->u.ap.hidden_ssid;
 +
 +              /* For now allow DFS for AP mode */
 +              arg.channel.chan_radar =
 +                      !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
 +      } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
 +              arg.ssid = arvif->vif->bss_conf.ssid;
 +              arg.ssid_len = arvif->vif->bss_conf.ssid_len;
 +      }
 +
 +      ath10k_dbg(ATH10K_DBG_MAC,
 +                 "mac vdev %d start center_freq %d phymode %s\n",
 +                 arg.vdev_id, arg.channel.freq,
 +                 ath10k_wmi_phymode_str(arg.channel.mode));
 +
 +      ret = ath10k_wmi_vdev_start(ar, &arg);
 +      if (ret) {
 +              ath10k_warn("failed to start WMI vdev %i: %d\n",
 +                          arg.vdev_id, ret);
 +              return ret;
 +      }
 +
 +      ret = ath10k_vdev_setup_sync(ar);
 +      if (ret) {
 +              ath10k_warn("failed to synchronise setup for vdev %i: %d\n",
 +                          arg.vdev_id, ret);
 +              return ret;
 +      }
 +
 +      ar->num_started_vdevs++;
 +      ath10k_recalc_radar_detection(ar);
 +
 +      return ret;
 +}
 +
 +static int ath10k_vdev_stop(struct ath10k_vif *arvif)
 +{
 +      struct ath10k *ar = arvif->ar;
 +      int ret;
 +
 +      lockdep_assert_held(&ar->conf_mutex);
 +
 +      reinit_completion(&ar->vdev_setup_done);
 +
 +      ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
 +      if (ret) {
 +              ath10k_warn("failed to stop WMI vdev %i: %d\n",
 +                          arvif->vdev_id, ret);
 +              return ret;
 +      }
 +
 +      ret = ath10k_vdev_setup_sync(ar);
 +      if (ret) {
 +              ath10k_warn("failed to syncronise setup for vdev %i: %d\n",
 +                          arvif->vdev_id, ret);
 +              return ret;
 +      }
 +
 +      WARN_ON(ar->num_started_vdevs == 0);
 +
 +      if (ar->num_started_vdevs != 0) {
 +              ar->num_started_vdevs--;
 +              ath10k_recalc_radar_detection(ar);
 +      }
 +
 +      return ret;
 +}
 +
  static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                                struct ieee80211_bss_conf *info)
  {
        ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
                                 arvif->bssid);
        if (ret) {
 -              ath10k_warn("Failed to bring up vdev %d: %i\n",
 +              ath10k_warn("failed to bring up vdev %d: %i\n",
                            arvif->vdev_id, ret);
                ath10k_vdev_stop(arvif);
                return;
@@@ -952,7 -904,7 +952,7 @@@ static void ath10k_control_ibss(struct 
        if (!info->ibss_joined) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
                if (ret)
 -                      ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
 +                      ath10k_warn("failed to delete IBSS self peer %pM for vdev %d: %d\n",
                                    self_peer, arvif->vdev_id, ret);
  
                if (is_zero_ether_addr(arvif->bssid))
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
                                         arvif->bssid);
                if (ret) {
 -                      ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
 +                      ath10k_warn("failed to delete IBSS BSSID peer %pM for vdev %d: %d\n",
                                    arvif->bssid, arvif->vdev_id, ret);
                        return;
                }
  
        ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
        if (ret) {
 -              ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n",
 +              ath10k_warn("failed to create IBSS self peer %pM for vdev %d: %d\n",
                            self_peer, arvif->vdev_id, ret);
                return;
        }
        ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
                                        ATH10K_DEFAULT_ATIM);
        if (ret)
 -              ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
 +              ath10k_warn("failed to set IBSS ATIM for vdev %d: %d\n",
                            arvif->vdev_id, ret);
  }
  
@@@ -1009,7 -961,7 +1009,7 @@@ static int ath10k_mac_vif_setup_ps(stru
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
                                                  conf->dynamic_ps_timeout);
                if (ret) {
 -                      ath10k_warn("Failed to set inactivity time for vdev %d: %i\n",
 +                      ath10k_warn("failed to set inactivity time for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
                        return ret;
                }
  
        ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
        if (ret) {
 -              ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
 -                          psmode, arvif->vdev_id);
 +              ath10k_warn("failed to set PS Mode %d for vdev %d: %d\n",
 +                          psmode, arvif->vdev_id, ret);
                return ret;
        }
  
@@@ -1477,7 -1429,7 +1477,7 @@@ static void ath10k_bss_assoc(struct iee
  
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
        if (!ap_sta) {
 -              ath10k_warn("Failed to find station entry for %pM, vdev %i\n",
 +              ath10k_warn("failed to find station entry for bss %pM vdev %i\n",
                            bss_conf->bssid, arvif->vdev_id);
                rcu_read_unlock();
                return;
        ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
                                        bss_conf, &peer_arg);
        if (ret) {
 -              ath10k_warn("Peer assoc prepare failed for %pM vdev %i\n: %d",
 +              ath10k_warn("failed to prepare peer assoc for %pM vdev %i: %d\n",
                            bss_conf->bssid, arvif->vdev_id, ret);
                rcu_read_unlock();
                return;
  
        ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
 -              ath10k_warn("Peer assoc failed for %pM vdev %i\n: %d",
 +              ath10k_warn("failed to run peer assoc for %pM vdev %i: %d\n",
                            bss_conf->bssid, arvif->vdev_id, ret);
                return;
        }
  
        ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
        if (ret) {
 -              ath10k_warn("VDEV: %d up failed: ret %d\n",
 +              ath10k_warn("failed to set vdev %d up: %d\n",
                            arvif->vdev_id, ret);
                return;
        }
@@@ -1572,7 -1524,7 +1572,7 @@@ static void ath10k_bss_disassoc(struct 
  }
  
  static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 -                              struct ieee80211_sta *sta)
 +                              struct ieee80211_sta *sta, bool reassoc)
  {
        struct wmi_peer_assoc_complete_arg peer_arg;
        int ret = 0;
  
        ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
        if (ret) {
 -              ath10k_warn("WMI peer assoc prepare failed for %pM vdev %i: %i\n",
 +              ath10k_warn("failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
  
 +      peer_arg.peer_reassoc = reassoc;
        ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
 -              ath10k_warn("Peer assoc failed for STA %pM vdev %i: %d\n",
 +              ath10k_warn("failed to run peer assoc for STA %pM vdev %i: %d\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
  
        ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
        if (ret) {
 -              ath10k_warn("failed to setup peer SMPS for vdev: %d\n", ret);
 +              ath10k_warn("failed to setup peer SMPS for vdev %d: %d\n",
 +                          arvif->vdev_id, ret);
                return ret;
        }
  
 +      if (!sta->wme) {
 +              arvif->num_legacy_stations++;
 +              ret  = ath10k_recalc_rtscts_prot(arvif);
 +              if (ret) {
 +                      ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
 +                                  arvif->vdev_id, ret);
 +                      return ret;
 +              }
 +      }
 +
        ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
        if (ret) {
 -              ath10k_warn("could not install peer wep keys for vdev %i: %d\n",
 +              ath10k_warn("failed to install peer wep keys for vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
  
        ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
        if (ret) {
 -              ath10k_warn("could not set qos params for STA %pM for vdev %i: %d\n",
 +              ath10k_warn("failed to set qos params for STA %pM for vdev %i: %d\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
@@@ -1635,19 -1575,9 +1635,19 @@@ static int ath10k_station_disassoc(stru
  
        lockdep_assert_held(&ar->conf_mutex);
  
 +      if (!sta->wme) {
 +              arvif->num_legacy_stations--;
 +              ret = ath10k_recalc_rtscts_prot(arvif);
 +              if (ret) {
 +                      ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
 +                                  arvif->vdev_id, ret);
 +                      return ret;
 +              }
 +      }
 +
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
 -              ath10k_warn("could not clear all peer wep keys for vdev %i: %d\n",
 +              ath10k_warn("failed to clear all peer wep keys for vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@@ -1755,44 -1685,19 +1755,44 @@@ static int ath10k_update_channel_list(s
        return ret;
  }
  
 +static enum wmi_dfs_region
 +ath10k_mac_get_dfs_region(enum nl80211_dfs_regions dfs_region)
 +{
 +      switch (dfs_region) {
 +      case NL80211_DFS_UNSET:
 +              return WMI_UNINIT_DFS_DOMAIN;
 +      case NL80211_DFS_FCC:
 +              return WMI_FCC_DFS_DOMAIN;
 +      case NL80211_DFS_ETSI:
 +              return WMI_ETSI_DFS_DOMAIN;
 +      case NL80211_DFS_JP:
 +              return WMI_MKK4_DFS_DOMAIN;
 +      }
 +      return WMI_UNINIT_DFS_DOMAIN;
 +}
 +
  static void ath10k_regd_update(struct ath10k *ar)
  {
        struct reg_dmn_pair_mapping *regpair;
        int ret;
 +      enum wmi_dfs_region wmi_dfs_reg;
 +      enum nl80211_dfs_regions nl_dfs_reg;
  
        lockdep_assert_held(&ar->conf_mutex);
  
        ret = ath10k_update_channel_list(ar);
        if (ret)
 -              ath10k_warn("could not update channel list (%d)\n", ret);
 +              ath10k_warn("failed to update channel list: %d\n", ret);
  
        regpair = ar->ath_common.regulatory.regpair;
  
 +      if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
 +              nl_dfs_reg = ar->dfs_detector->region;
 +              wmi_dfs_reg = ath10k_mac_get_dfs_region(nl_dfs_reg);
 +      } else {
 +              wmi_dfs_reg = WMI_UNINIT_DFS_DOMAIN;
 +      }
 +
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
                                            regpair->reg_domain, /* 2ghz */
                                            regpair->reg_domain, /* 5ghz */
                                            regpair->reg_2ghz_ctl,
 -                                          regpair->reg_5ghz_ctl);
 +                                          regpair->reg_5ghz_ctl,
 +                                          wmi_dfs_reg);
        if (ret)
 -              ath10k_warn("could not set pdev regdomain (%d)\n", ret);
 +              ath10k_warn("failed to set pdev regdomain: %d\n", ret);
  }
  
  static void ath10k_reg_notifier(struct wiphy *wiphy,
                result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
                                                          request->dfs_region);
                if (!result)
 -                      ath10k_warn("dfs region 0x%X not supported, will trigger radar for every pulse\n",
 +                      ath10k_warn("DFS region 0x%X not supported, will trigger radar for every pulse\n",
                                    request->dfs_region);
        }
  
@@@ -1855,10 -1759,10 +1855,10 @@@ static u8 ath10k_tx_h_get_vdev_id(struc
        if (info->control.vif)
                return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
  
 -      if (ar->monitor_enabled)
 +      if (ar->monitor_started)
                return ar->monitor_vdev_id;
  
 -      ath10k_warn("could not resolve vdev id\n");
 +      ath10k_warn("failed to resolve vdev id\n");
        return 0;
  }
  
@@@ -1899,9 -1803,7 +1899,9 @@@ static void ath10k_tx_wep_key_work(stru
                                        arvif->ar->wmi.vdev_param->def_keyid,
                                        keyidx);
        if (ret) {
 -              ath10k_warn("could not update wep keyidx (%d)\n", ret);
 +              ath10k_warn("failed to update wep key index for vdev %d: %d\n",
 +                          arvif->vdev_id,
 +                          ret);
                return;
        }
  
@@@ -1977,7 -1879,7 +1977,7 @@@ static void ath10k_tx_htt(struct ath10
                             ar->fw_features)) {
                        if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
                            ATH10K_MAX_NUM_MGMT_PENDING) {
 -                              ath10k_warn("wmi mgmt_tx queue limit reached\n");
 +                              ath10k_warn("reached WMI management tranmist queue limit\n");
                                ret = -EBUSY;
                                goto exit;
                        }
  
  exit:
        if (ret) {
 -              ath10k_warn("tx failed (%d). dropping packet.\n", ret);
 +              ath10k_warn("failed to transmit packet, dropping: %d\n", ret);
                ieee80211_free_txskb(ar->hw, skb);
        }
  }
@@@ -2062,7 -1964,7 +2062,7 @@@ void ath10k_offchan_tx_work(struct work
                if (!peer) {
                        ret = ath10k_peer_create(ar, vdev_id, peer_addr);
                        if (ret)
 -                              ath10k_warn("peer %pM on vdev %d not created (%d)\n",
 +                              ath10k_warn("failed to create peer %pM on vdev %d: %d\n",
                                            peer_addr, vdev_id, ret);
                }
  
                if (!peer) {
                        ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
                        if (ret)
 -                              ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
 +                              ath10k_warn("failed to delete peer %pM on vdev %d: %d\n",
                                            peer_addr, vdev_id, ret);
                }
  
@@@ -2116,8 -2018,7 +2116,8 @@@ void ath10k_mgmt_over_wmi_tx_work(struc
  
                ret = ath10k_wmi_mgmt_tx(ar, skb);
                if (ret) {
 -                      ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
 +                      ath10k_warn("failed to transmit management frame via WMI: %d\n",
 +                                  ret);
                        ieee80211_free_txskb(ar->hw, skb);
                }
        }
@@@ -2142,7 -2043,7 +2142,7 @@@ void ath10k_reset_scan(unsigned long pt
                return;
        }
  
 -      ath10k_warn("scan timeout. resetting. fw issue?\n");
 +      ath10k_warn("scan timed out, firmware problem?\n");
  
        if (ar->scan.is_roc)
                ieee80211_remain_on_channel_expired(ar->hw);
@@@ -2178,7 -2079,7 +2178,7 @@@ static int ath10k_abort_scan(struct ath
  
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
 -              ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
 +              ath10k_warn("failed to stop wmi scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                ath10k_offchan_tx_purge(ar);
  
        spin_lock_bh(&ar->data_lock);
        if (ar->scan.in_progress) {
 -              ath10k_warn("could not stop scan. its still in progress\n");
 +              ath10k_warn("failed to stop scan, it's still in progress\n");
                ar->scan.in_progress = false;
                ath10k_offchan_tx_purge(ar);
                ret = -ETIMEDOUT;
@@@ -2293,13 -2194,7 +2293,13 @@@ void ath10k_halt(struct ath10k *ar
  {
        lockdep_assert_held(&ar->conf_mutex);
  
 -      ath10k_stop_cac(ar);
 +      if (ath10k_monitor_is_enabled(ar)) {
 +              clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 +              ar->promisc = false;
 +              ar->monitor = false;
 +              ath10k_monitor_stop(ar);
 +      }
 +
        del_timer_sync(&ar->scan.timeout);
        ath10k_offchan_tx_purge(ar);
        ath10k_mgmt_over_wmi_tx_purge(ar);
@@@ -2331,14 -2226,14 +2331,14 @@@ static int ath10k_start(struct ieee8021
  
        ret = ath10k_hif_power_up(ar);
        if (ret) {
 -              ath10k_err("could not init hif (%d)\n", ret);
 +              ath10k_err("Could not init hif: %d\n", ret);
                ar->state = ATH10K_STATE_OFF;
                goto exit;
        }
  
        ret = ath10k_core_start(ar);
        if (ret) {
 -              ath10k_err("could not init core (%d)\n", ret);
 +              ath10k_err("Could not init core: %d\n", ret);
                ath10k_hif_power_down(ar);
                ar->state = ATH10K_STATE_OFF;
                goto exit;
  
        ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1);
        if (ret)
 -              ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
 -                          ret);
 +              ath10k_warn("failed to enable PMF QOS: %d\n", ret);
  
        ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1);
        if (ret)
 -              ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
 -                          ret);
 +              ath10k_warn("failed to enable dynamic BW: %d\n", ret);
  
        /*
         * By default FW set ARP frames ac to voice (6). In that case ARP
        ret = ath10k_wmi_pdev_set_param(ar,
                                        ar->wmi.pdev_param->arp_ac_override, 0);
        if (ret) {
 -              ath10k_warn("could not set arp ac override parameter: %d\n",
 +              ath10k_warn("failed to set arp ac override parameter: %d\n",
                            ret);
                goto exit;
        }
  
 +      ar->num_started_vdevs = 0;
        ath10k_regd_update(ar);
        ret = 0;
  
@@@ -2413,7 -2309,7 +2413,7 @@@ static int ath10k_config_ps(struct ath1
        list_for_each_entry(arvif, &ar->arvifs, list) {
                ret = ath10k_mac_vif_setup_ps(arvif);
                if (ret) {
 -                      ath10k_warn("could not setup powersave (%d)\n", ret);
 +                      ath10k_warn("failed to setup powersave: %d\n", ret);
                        break;
                }
        }
@@@ -2447,6 -2343,7 +2447,6 @@@ static const char *chandef_get_width(en
  static void ath10k_config_chan(struct ath10k *ar)
  {
        struct ath10k_vif *arvif;
 -      bool monitor_was_enabled;
        int ret;
  
        lockdep_assert_held(&ar->conf_mutex);
  
        /* First stop monitor interface. Some FW versions crash if there's a
         * lone monitor interface. */
 -      monitor_was_enabled = ar->monitor_enabled;
 -
 -      if (ar->monitor_enabled)
 -              ath10k_monitor_stop(ar);
 +      if (ar->monitor_started)
 +              ath10k_monitor_vdev_stop(ar);
  
        list_for_each_entry(arvif, &ar->arvifs, list) {
                if (!arvif->is_started)
  
                ret = ath10k_vdev_stop(arvif);
                if (ret) {
 -                      ath10k_warn("could not stop vdev %d (%d)\n",
 +                      ath10k_warn("failed to stop vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
  
                ret = ath10k_vdev_start(arvif);
                if (ret) {
 -                      ath10k_warn("could not start vdev %d (%d)\n",
 +                      ath10k_warn("failed to start vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
                ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
                                         arvif->bssid);
                if (ret) {
 -                      ath10k_warn("could not bring vdev up %d (%d)\n",
 +                      ath10k_warn("failed to bring vdev up %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
        }
  
 -      if (monitor_was_enabled)
 -              ath10k_monitor_start(ar, ar->monitor_vdev_id);
 +      if (ath10k_monitor_is_enabled(ar))
 +              ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
  }
  
  static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
  
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                ath10k_dbg(ATH10K_DBG_MAC,
 -                         "mac config channel %d mhz flags 0x%x\n",
 +                         "mac config channel %dMHz flags 0x%x radar %d\n",
                           conf->chandef.chan->center_freq,
 -                         conf->chandef.chan->flags);
 +                         conf->chandef.chan->flags,
 +                         conf->radar_enabled);
  
                spin_lock_bh(&ar->data_lock);
                ar->rx_channel = conf->chandef.chan;
                spin_unlock_bh(&ar->data_lock);
  
 -              ath10k_config_radar_detection(ar);
 +              ar->radar_enabled = conf->radar_enabled;
 +              ath10k_recalc_radar_detection(ar);
  
                if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) {
                        ar->chandef = conf->chandef;
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                hw->conf.power_level * 2);
                if (ret)
 -                      ath10k_warn("mac failed to set 2g txpower %d (%d)\n",
 +                      ath10k_warn("failed to set 2g txpower %d: %d\n",
                                    hw->conf.power_level, ret);
  
                param = ar->wmi.pdev_param->txpower_limit5g;
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                hw->conf.power_level * 2);
                if (ret)
 -                      ath10k_warn("mac failed to set 5g txpower %d (%d)\n",
 +                      ath10k_warn("failed to set 5g txpower %d: %d\n",
                                    hw->conf.power_level, ret);
        }
  
                ath10k_config_ps(ar);
  
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
 -              if (conf->flags & IEEE80211_CONF_MONITOR)
 -                      ret = ath10k_monitor_create(ar);
 -              else
 -                      ret = ath10k_monitor_destroy(ar);
 +              if (conf->flags & IEEE80211_CONF_MONITOR && !ar->monitor) {
 +                      ar->monitor = true;
 +                      ret = ath10k_monitor_start(ar);
 +                      if (ret) {
 +                              ath10k_warn("failed to start monitor (config): %d\n",
 +                                          ret);
 +                              ar->monitor = false;
 +                      }
 +              } else if (!(conf->flags & IEEE80211_CONF_MONITOR) &&
 +                         ar->monitor) {
 +                      ar->monitor = false;
 +                      ath10k_monitor_stop(ar);
 +              }
        }
  
        mutex_unlock(&ar->conf_mutex);
@@@ -2609,6 -2497,12 +2609,6 @@@ static int ath10k_add_interface(struct 
        INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
        INIT_LIST_HEAD(&arvif->list);
  
 -      if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
 -              ath10k_warn("Only one monitor interface allowed\n");
 -              ret = -EBUSY;
 -              goto err;
 -      }
 -
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
                ret = -EBUSY;
        ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
                                     arvif->vdev_subtype, vif->addr);
        if (ret) {
 -              ath10k_warn("WMI vdev %i create failed: ret %d\n",
 +              ath10k_warn("failed to create WMI vdev %i: %d\n",
                            arvif->vdev_id, ret);
                goto err;
        }
        ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
                                        arvif->def_wep_key_idx);
        if (ret) {
 -              ath10k_warn("Failed to set vdev %i default keyid: %d\n",
 +              ath10k_warn("failed to set vdev %i default key id: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
                                        ATH10K_HW_TXRX_NATIVE_WIFI);
        /* 10.X firmware does not support this VDEV parameter. Do not warn */
        if (ret && ret != -EOPNOTSUPP) {
 -              ath10k_warn("Failed to set vdev %i TX encap: %d\n",
 +              ath10k_warn("failed to set vdev %i TX encapsulation: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
                if (ret) {
 -                      ath10k_warn("Failed to create vdev %i peer for AP: %d\n",
 +                      ath10k_warn("failed to create vdev %i peer for AP: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_vdev_delete;
                }
  
                ret = ath10k_mac_set_kickout(arvif);
                if (ret) {
 -                      ath10k_warn("Failed to set vdev %i kickout parameters: %d\n",
 +                      ath10k_warn("failed to set vdev %i kickout parameters: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
 -                      ath10k_warn("Failed to set vdev %i RX wake policy: %d\n",
 +                      ath10k_warn("failed to set vdev %i RX wake policy: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
 -                      ath10k_warn("Failed to set vdev %i TX wake thresh: %d\n",
 +                      ath10k_warn("failed to set vdev %i TX wake thresh: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
 -                      ath10k_warn("Failed to set vdev %i PSPOLL count: %d\n",
 +                      ath10k_warn("failed to set vdev %i PSPOLL count: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
  
        ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
        if (ret) {
 -              ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
 +              ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
                            arvif->vdev_id, ret);
                goto err_peer_delete;
        }
  
        ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
        if (ret) {
 -              ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
 +              ath10k_warn("failed to set frag threshold for vdev %d: %d\n",
                            arvif->vdev_id, ret);
                goto err_peer_delete;
        }
  
 -      if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
 -              ar->monitor_present = true;
 -
        mutex_unlock(&ar->conf_mutex);
        return 0;
  
@@@ -2782,7 -2679,7 +2782,7 @@@ static void ath10k_remove_interface(str
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
                if (ret)
 -                      ath10k_warn("Failed to remove peer for AP vdev %i: %d\n",
 +                      ath10k_warn("failed to remove peer for AP vdev %i: %d\n",
                                    arvif->vdev_id, ret);
  
                kfree(arvif->u.ap.noa_data);
  
        ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
        if (ret)
 -              ath10k_warn("WMI vdev %i delete failed: %d\n",
 +              ath10k_warn("failed to delete WMI vdev %i: %d\n",
                            arvif->vdev_id, ret);
  
 -      if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
 -              ar->monitor_present = false;
 -
        ath10k_peer_cleanup(ar, arvif->vdev_id);
  
        mutex_unlock(&ar->conf_mutex);
@@@ -2828,17 -2728,28 +2828,17 @@@ static void ath10k_configure_filter(str
        *total_flags &= SUPPORTED_FILTERS;
        ar->filter_flags = *total_flags;
  
 -      /* Monitor must not be started if it wasn't created first.
 -       * Promiscuous mode may be started on a non-monitor interface - in
 -       * such case the monitor vdev is not created so starting the
 -       * monitor makes no sense. Since ath10k uses no special RX filters
 -       * (only BSS filter in STA mode) there's no need for any special
 -       * action here. */
 -      if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
 -          !ar->monitor_enabled && ar->monitor_present) {
 -              ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n",
 -                         ar->monitor_vdev_id);
 -
 -              ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
 -              if (ret)
 -                      ath10k_warn("Unable to start monitor mode\n");
 -      } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
 -                 ar->monitor_enabled && ar->monitor_present) {
 -              ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n",
 -                         ar->monitor_vdev_id);
 -
 -              ret = ath10k_monitor_stop(ar);
 -              if (ret)
 -                      ath10k_warn("Unable to stop monitor mode\n");
 +      if (ar->filter_flags & FIF_PROMISC_IN_BSS && !ar->promisc) {
 +              ar->promisc = true;
 +              ret = ath10k_monitor_start(ar);
 +              if (ret) {
 +                      ath10k_warn("failed to start monitor (promisc): %d\n",
 +                                  ret);
 +                      ar->promisc = false;
 +              }
 +      } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && ar->promisc) {
 +              ar->promisc = false;
 +              ath10k_monitor_stop(ar);
        }
  
        mutex_unlock(&ar->conf_mutex);
@@@ -2869,7 -2780,7 +2869,7 @@@ static void ath10k_bss_info_changed(str
                           arvif->vdev_id, arvif->beacon_interval);
  
                if (ret)
 -                      ath10k_warn("Failed to set beacon interval for vdev %d: %i\n",
 +                      ath10k_warn("failed to set beacon interval for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
  
                ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
                                                WMI_BEACON_STAGGERED_MODE);
                if (ret)
 -                      ath10k_warn("Failed to set beacon mode for vdev %d: %i\n",
 +                      ath10k_warn("failed to set beacon mode for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
  
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                arvif->dtim_period);
                if (ret)
 -                      ath10k_warn("Failed to set dtim period for vdev %d: %i\n",
 +                      ath10k_warn("failed to set dtim period for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
  
                        ret = ath10k_peer_create(ar, arvif->vdev_id,
                                                 info->bssid);
                        if (ret)
 -                              ath10k_warn("Failed to add peer %pM for vdev %d when changing bssid: %i\n",
 +                              ath10k_warn("failed to add peer %pM for vdev %d when changing bssid: %i\n",
                                            info->bssid, arvif->vdev_id, ret);
  
                        if (vif->type == NL80211_IFTYPE_STATION) {
                ath10k_control_beaconing(arvif, info);
  
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
 -              u32 cts_prot;
 -              if (info->use_cts_prot)
 -                      cts_prot = 1;
 -              else
 -                      cts_prot = 0;
 -
 +              arvif->use_cts_prot = info->use_cts_prot;
                ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
 -                         arvif->vdev_id, cts_prot);
 +                         arvif->vdev_id, info->use_cts_prot);
  
 -              vdev_param = ar->wmi.vdev_param->enable_rtscts;
 -              ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
 -                                              cts_prot);
 +              ret = ath10k_recalc_rtscts_prot(arvif);
                if (ret)
 -                      ath10k_warn("Failed to set CTS prot for vdev %d: %d\n",
 +                      ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
        }
  
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                slottime);
                if (ret)
 -                      ath10k_warn("Failed to set erp slot for vdev %d: %i\n",
 +                      ath10k_warn("failed to set erp slot for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
  
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                preamble);
                if (ret)
 -                      ath10k_warn("Failed to set preamble for vdev %d: %i\n",
 +                      ath10k_warn("failed to set preamble for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
  
@@@ -3072,7 -2990,7 +3072,7 @@@ static int ath10k_hw_scan(struct ieee80
  
        ret = ath10k_start_scan(ar, &arg);
        if (ret) {
 -              ath10k_warn("could not start hw scan (%d)\n", ret);
 +              ath10k_warn("failed to start hw scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                spin_unlock_bh(&ar->data_lock);
@@@ -3092,7 -3010,8 +3092,7 @@@ static void ath10k_cancel_hw_scan(struc
        mutex_lock(&ar->conf_mutex);
        ret = ath10k_abort_scan(ar);
        if (ret) {
 -              ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n",
 -                          ret);
 +              ath10k_warn("failed to abort scan: %d\n", ret);
                ieee80211_scan_completed(hw, 1 /* aborted */);
        }
        mutex_unlock(&ar->conf_mutex);
@@@ -3170,7 -3089,7 +3170,7 @@@ static int ath10k_set_key(struct ieee80
  
        if (!peer) {
                if (cmd == SET_KEY) {
 -                      ath10k_warn("cannot install key for non-existent peer %pM\n",
 +                      ath10k_warn("failed to install key for non-existent peer %pM\n",
                                    peer_addr);
                        ret = -EOPNOTSUPP;
                        goto exit;
  
        ret = ath10k_install_key(arvif, key, cmd, peer_addr);
        if (ret) {
 -              ath10k_warn("key installation failed for vdev %i peer %pM: %d\n",
 +              ath10k_warn("failed to install key for vdev %i peer %pM: %d\n",
                            arvif->vdev_id, peer_addr, ret);
                goto exit;
        }
                peer->keys[key->keyidx] = NULL;
        else if (peer == NULL)
                /* impossible unless FW goes crazy */
 -              ath10k_warn("peer %pM disappeared!\n", peer_addr);
 +              ath10k_warn("Peer %pM disappeared!\n", peer_addr);
        spin_unlock_bh(&ar->data_lock);
  
  exit:
@@@ -3276,16 -3195,6 +3276,16 @@@ static void ath10k_sta_rc_update_wk(str
                                    sta->addr, smps, err);
        }
  
 +      if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
 +              ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
 +                         sta->addr);
 +
 +              err = ath10k_station_assoc(ar, arvif, sta, true);
 +              if (err)
 +                      ath10k_warn("failed to reassociate station: %pM\n",
 +                                  sta->addr);
 +      }
 +
        mutex_unlock(&ar->conf_mutex);
  }
  
@@@ -3327,7 -3236,7 +3327,7 @@@ static int ath10k_sta_state(struct ieee
                        max_num_peers = TARGET_NUM_PEERS;
  
                if (ar->num_peers >= max_num_peers) {
 -                      ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n",
 +                      ath10k_warn("number of peers exceeded: peers number %d (max peers %d)\n",
                                    ar->num_peers, max_num_peers);
                        ret = -ENOBUFS;
                        goto exit;
  
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
 -                      ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n",
 +                      ath10k_warn("failed to add peer %pM for vdev %d when adding a new sta: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        } else if ((old_state == IEEE80211_STA_NONE &&
                    new_state == IEEE80211_STA_NOTEXIST)) {
                           arvif->vdev_id, sta->addr);
                ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
                if (ret)
 -                      ath10k_warn("Failed to delete peer %pM for vdev %d: %i\n",
 +                      ath10k_warn("failed to delete peer %pM for vdev %d: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
  
                if (vif->type == NL80211_IFTYPE_STATION)
                ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
                           sta->addr);
  
 -              ret = ath10k_station_assoc(ar, arvif, sta);
 +              ret = ath10k_station_assoc(ar, arvif, sta, false);
                if (ret)
 -                      ath10k_warn("Failed to associate station %pM for vdev %i: %i\n",
 +                      ath10k_warn("failed to associate station %pM for vdev %i: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH &&
  
                ret = ath10k_station_disassoc(ar, arvif, sta);
                if (ret)
 -                      ath10k_warn("Failed to disassociate station: %pM vdev %i ret %i\n",
 +                      ath10k_warn("failed to disassociate station: %pM vdev %i: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        }
  exit:
@@@ -3430,7 -3339,7 +3430,7 @@@ static int ath10k_conf_tx_uapsd(struct 
                                          WMI_STA_PS_PARAM_UAPSD,
                                          arvif->u.sta.uapsd);
        if (ret) {
 -              ath10k_warn("could not set uapsd params %d\n", ret);
 +              ath10k_warn("failed to set uapsd params: %d\n", ret);
                goto exit;
        }
  
                                          WMI_STA_PS_PARAM_RX_WAKE_POLICY,
                                          value);
        if (ret)
 -              ath10k_warn("could not set rx wake param %d\n", ret);
 +              ath10k_warn("failed to set rx wake param: %d\n", ret);
  
  exit:
        return ret;
@@@ -3493,13 -3402,13 +3493,13 @@@ static int ath10k_conf_tx(struct ieee80
        /* FIXME: FW accepts wmm params per hw, not per vif */
        ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
        if (ret) {
 -              ath10k_warn("could not set wmm params %d\n", ret);
 +              ath10k_warn("failed to set wmm params: %d\n", ret);
                goto exit;
        }
  
        ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
        if (ret)
 -              ath10k_warn("could not set sta uapsd %d\n", ret);
 +              ath10k_warn("failed to set sta uapsd: %d\n", ret);
  
  exit:
        mutex_unlock(&ar->conf_mutex);
@@@ -3552,7 -3461,7 +3552,7 @@@ static int ath10k_remain_on_channel(str
  
        ret = ath10k_start_scan(ar, &arg);
        if (ret) {
 -              ath10k_warn("could not start roc scan (%d)\n", ret);
 +              ath10k_warn("failed to start roc scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                spin_unlock_bh(&ar->data_lock);
  
        ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
        if (ret == 0) {
 -              ath10k_warn("could not switch to channel for roc scan\n");
 +              ath10k_warn("failed to switch to channel for roc scan\n");
                ath10k_abort_scan(ar);
                ret = -ETIMEDOUT;
                goto exit;
@@@ -3602,7 -3511,7 +3602,7 @@@ static int ath10k_set_rts_threshold(str
  
                ret = ath10k_mac_set_rts(arvif, value);
                if (ret) {
 -                      ath10k_warn("could not set rts threshold for vdev %d (%d)\n",
 +                      ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        break;
                }
@@@ -3625,7 -3534,7 +3625,7 @@@ static int ath10k_set_frag_threshold(st
  
                ret = ath10k_mac_set_rts(arvif, value);
                if (ret) {
 -                      ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n",
 +                      ath10k_warn("failed to set fragmentation threshold for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        break;
                }
        return ret;
  }
  
- static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
  {
        struct ath10k *ar = hw->priv;
        bool skip;
                }), ATH10K_FLUSH_TIMEOUT_HZ);
  
        if (ret <= 0 || skip)
 -              ath10k_warn("tx not flushed (skip %i ar-state %i): %i\n",
 +              ath10k_warn("failed to flush transmit queue (skip %i ar-state %i): %i\n",
                            skip, ar->state, ret);
  
  skip:
@@@ -3699,7 -3609,7 +3700,7 @@@ static int ath10k_suspend(struct ieee80
  
        ret = ath10k_hif_suspend(ar);
        if (ret) {
 -              ath10k_warn("could not suspend hif (%d)\n", ret);
 +              ath10k_warn("failed to suspend hif: %d\n", ret);
                goto resume;
        }
  
  resume:
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret)
 -              ath10k_warn("could not resume target (%d)\n", ret);
 +              ath10k_warn("failed to resume target: %d\n", ret);
  
        ret = 1;
  exit:
@@@ -3725,14 -3635,14 +3726,14 @@@ static int ath10k_resume(struct ieee802
  
        ret = ath10k_hif_resume(ar);
        if (ret) {
 -              ath10k_warn("could not resume hif (%d)\n", ret);
 +              ath10k_warn("failed to resume hif: %d\n", ret);
                ret = 1;
                goto exit;
        }
  
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret) {
 -              ath10k_warn("could not resume target (%d)\n", ret);
 +              ath10k_warn("failed to resume target: %d\n", ret);
                ret = 1;
                goto exit;
        }
@@@ -4055,7 -3965,7 +4056,7 @@@ static int ath10k_set_fixed_rate_param(
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                        vdev_param, fixed_rate);
        if (ret) {
 -              ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n",
 +              ath10k_warn("failed to set fixed rate param 0x%02x: %d\n",
                            fixed_rate, ret);
                ret = -EINVAL;
                goto exit;
                                        vdev_param, fixed_nss);
  
        if (ret) {
 -              ath10k_warn("Could not set fixed_nss param %d: %d\n",
 +              ath10k_warn("failed to set fixed nss param %d: %d\n",
                            fixed_nss, ret);
                ret = -EINVAL;
                goto exit;
                                        force_sgi);
  
        if (ret) {
 -              ath10k_warn("Could not set sgi param %d: %d\n",
 +              ath10k_warn("failed to set sgi param %d: %d\n",
                            force_sgi, ret);
                ret = -EINVAL;
                goto exit;
@@@ -4117,7 -4027,7 +4118,7 @@@ static int ath10k_set_bitrate_mask(stru
        }
  
        if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) {
 -              ath10k_warn("Could not force SGI usage for default rate settings\n");
 +              ath10k_warn("failed to force SGI usage for default rate settings\n");
                return -EINVAL;
        }
  
@@@ -4163,8 -4073,8 +4164,8 @@@ static void ath10k_sta_rc_update(struc
                        bw = WMI_PEER_CHWIDTH_80MHZ;
                        break;
                case IEEE80211_STA_RX_BW_160:
 -                      ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
 -                                  sta->addr, sta->bandwidth);
 +                      ath10k_warn("Invalid bandwith %d in rc update for %pM\n",
 +                                  sta->bandwidth, sta->addr);
                        bw = WMI_PEER_CHWIDTH_20MHZ;
                        break;
                }
                        smps = WMI_PEER_SMPS_DYNAMIC;
                        break;
                case IEEE80211_SMPS_NUM_MODES:
 -                      ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
 -                                  sta->addr, sta->smps_mode);
 +                      ath10k_warn("Invalid smps %d in sta rc update for %pM\n",
 +                                  sta->smps_mode, sta->addr);
                        smps = WMI_PEER_SMPS_PS_NONE;
                        break;
                }
                arsta->smps = smps;
        }
  
 -      if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
 -              /* FIXME: Not implemented. Probably the only way to do it would
 -               * be to re-assoc the peer. */
 -              changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
 -              ath10k_dbg(ATH10K_DBG_MAC,
 -                         "mac sta rc update for %pM: changing supported rates not implemented\n",
 -                         sta->addr);
 -      }
 -
        arsta->changed |= changed;
  
        spin_unlock_bh(&ar->data_lock);
@@@ -4598,6 -4517,7 +4599,6 @@@ int ath10k_mac_register(struct ath10k *
                        IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                        IEEE80211_HW_HAS_RATE_CONTROL |
                        IEEE80211_HW_SUPPORTS_STATIC_SMPS |
 -                      IEEE80211_HW_WANT_MONITOR_VIF |
                        IEEE80211_HW_AP_LINK_PS |
                        IEEE80211_HW_SPECTRUM_MGMT;
  
                                                             NL80211_DFS_UNSET);
  
                if (!ar->dfs_detector)
 -                      ath10k_warn("dfs pattern detector init failed\n");
 +                      ath10k_warn("failed to initialise DFS pattern detector\n");
        }
  
        ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
                            ath10k_reg_notifier);
        if (ret) {
 -              ath10k_err("Regulatory initialization failed: %i\n", ret);
 +              ath10k_err("failed to initialise regulatory: %i\n", ret);
                goto err_free;
        }
  
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
 -              ath10k_err("ieee80211 registration failed: %d\n", ret);
 +              ath10k_err("failed to register ieee80211: %d\n", ret);
                goto err_free;
        }
  
index 22c9e5471f9ca8821a568846faef6c173b187c1a,49265c6a1a7e46838c9cd99e7d48233b709758f0..8d7b9b66fefa592e5cee77b55e84c49b14ea2de0
@@@ -261,8 -261,6 +261,8 @@@ static bool ath_complete_reset(struct a
        sc->gtt_cnt = 0;
        ieee80211_wake_queues(sc->hw);
  
 +      ath9k_p2p_ps_timer(sc);
 +
        return true;
  }
  
@@@ -1121,8 -1119,6 +1121,8 @@@ static int ath9k_add_interface(struct i
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
  
 +      avp->vif = vif;
 +
        an->sc = sc;
        an->sta = NULL;
        an->vif = vif;
@@@ -1167,29 -1163,6 +1167,29 @@@ static int ath9k_change_interface(struc
        return 0;
  }
  
 +static void
 +ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
 +{
 +      struct ath_hw *ah = sc->sc_ah;
 +      s32 tsf, target_tsf;
 +
 +      if (!avp || !avp->noa.has_next_tsf)
 +              return;
 +
 +      ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer);
 +
 +      tsf = ath9k_hw_gettsf32(sc->sc_ah);
 +
 +      target_tsf = avp->noa.next_tsf;
 +      if (!avp->noa.absent)
 +              target_tsf -= ATH_P2P_PS_STOP_TIME;
 +
 +      if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME)
 +              target_tsf = tsf + ATH_P2P_PS_STOP_TIME;
 +
 +      ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000);
 +}
 +
  static void ath9k_remove_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
  {
  
        mutex_lock(&sc->mutex);
  
 +      spin_lock_bh(&sc->sc_pcu_lock);
 +      if (avp == sc->p2p_ps_vif) {
 +              sc->p2p_ps_vif = NULL;
 +              ath9k_update_p2p_ps_timer(sc, NULL);
 +      }
 +      spin_unlock_bh(&sc->sc_pcu_lock);
 +
        sc->nvifs--;
        sc->tx99_vif = NULL;
  
@@@ -1670,72 -1636,6 +1670,72 @@@ static void ath9k_bss_assoc_iter(void *
                ath9k_set_assoc_state(sc, vif);
  }
  
 +void ath9k_p2p_ps_timer(void *priv)
 +{
 +      struct ath_softc *sc = priv;
 +      struct ath_vif *avp = sc->p2p_ps_vif;
 +      struct ieee80211_vif *vif;
 +      struct ieee80211_sta *sta;
 +      struct ath_node *an;
 +      u32 tsf;
 +
 +      if (!avp)
 +              return;
 +
 +      tsf = ath9k_hw_gettsf32(sc->sc_ah);
 +      if (!avp->noa.absent)
 +              tsf += ATH_P2P_PS_STOP_TIME;
 +
 +      if (!avp->noa.has_next_tsf ||
 +          avp->noa.next_tsf - tsf > BIT(31))
 +              ieee80211_update_p2p_noa(&avp->noa, tsf);
 +
 +      ath9k_update_p2p_ps_timer(sc, avp);
 +
 +      rcu_read_lock();
 +
 +      vif = avp->vif;
 +      sta = ieee80211_find_sta(vif, vif->bss_conf.bssid);
 +      if (!sta)
 +              goto out;
 +
 +      an = (void *) sta->drv_priv;
 +      if (an->sleeping == !!avp->noa.absent)
 +              goto out;
 +
 +      an->sleeping = avp->noa.absent;
 +      if (an->sleeping)
 +              ath_tx_aggr_sleep(sta, sc, an);
 +      else
 +              ath_tx_aggr_wakeup(sc, an);
 +
 +out:
 +      rcu_read_unlock();
 +}
 +
 +void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
 +{
 +      struct ath_vif *avp = (void *)vif->drv_priv;
 +      unsigned long flags;
 +      u32 tsf;
 +
 +      if (!sc->p2p_ps_timer)
 +              return;
 +
 +      if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p)
 +              return;
 +
 +      sc->p2p_ps_vif = avp;
 +
 +      spin_lock_irqsave(&sc->sc_pm_lock, flags);
 +      if (!(sc->ps_flags & PS_BEACON_SYNC)) {
 +              tsf = ath9k_hw_gettsf32(sc->sc_ah);
 +              ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
 +              ath9k_update_p2p_ps_timer(sc, avp);
 +      }
 +      spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 +}
 +
  static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   struct ieee80211_bss_conf *bss_conf,
                }
        }
  
 +      if (changed & BSS_CHANGED_P2P_PS) {
 +              spin_lock_bh(&sc->sc_pcu_lock);
 +              ath9k_update_p2p_ps(sc, vif);
 +              spin_unlock_bh(&sc->sc_pcu_lock);
 +      }
 +
        if (changed & CHECK_ANI)
                ath_check_ani(sc);
  
@@@ -1989,7 -1883,8 +1989,8 @@@ static bool ath9k_has_tx_pending(struc
        return !!npend;
  }
  
- static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                       u32 queues, bool drop)
  {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
index d3abc15125d6556f4294b4d48541299d320a8aad,d4fae9fb2ddbed30d777942de451d42de2677997..29af7b51e3708788d02f4a1651205a348a5102dd
@@@ -1091,7 -1091,8 +1091,8 @@@ static void iwlagn_configure_filter(str
                        FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
  }
  
- static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+ static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                            u32 queues, bool drop)
  {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
  
                }
        }
        IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
 -      iwl_trans_wait_tx_queue_empty(priv->trans);
 +      iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
  done:
        mutex_unlock(&priv->mutex);
        IWL_DEBUG_MAC80211(priv, "leave\n");
diff --combined net/mac80211/chan.c
index 75b5dd2c9267f10e8cb0e5680c3aa11c94a5dbe4,d8b1b861484259a426e91ad12d5dbe0ac17f784b..48e6d6f010cd0f26acd379fea0c601bf8d67e33f
  #include "ieee80211_i.h"
  #include "driver-ops.h"
  
+ static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
+                                         struct ieee80211_chanctx *ctx)
+ {
+       struct ieee80211_sub_if_data *sdata;
+       int num = 0;
+       lockdep_assert_held(&local->chanctx_mtx);
+       list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
+               num++;
+       return num;
+ }
+ static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
+                                         struct ieee80211_chanctx *ctx)
+ {
+       struct ieee80211_sub_if_data *sdata;
+       int num = 0;
+       lockdep_assert_held(&local->chanctx_mtx);
+       list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
+               num++;
+       return num;
+ }
+ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
+                              struct ieee80211_chanctx *ctx)
+ {
+       return ieee80211_chanctx_num_assigned(local, ctx) +
+              ieee80211_chanctx_num_reserved(local, ctx);
+ }
+ static int ieee80211_num_chanctx(struct ieee80211_local *local)
+ {
+       struct ieee80211_chanctx *ctx;
+       int num = 0;
+       lockdep_assert_held(&local->chanctx_mtx);
+       list_for_each_entry(ctx, &local->chanctx_list, list)
+               num++;
+       return num;
+ }
+ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
+ {
+       lockdep_assert_held(&local->chanctx_mtx);
+       return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
+ }
+ static const struct cfg80211_chan_def *
+ ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx,
+                                  const struct cfg80211_chan_def *compat)
+ {
+       struct ieee80211_sub_if_data *sdata;
+       lockdep_assert_held(&local->chanctx_mtx);
+       list_for_each_entry(sdata, &ctx->reserved_vifs,
+                           reserved_chanctx_list) {
+               if (!compat)
+                       compat = &sdata->reserved_chandef;
+               compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
+                                                    compat);
+               if (!compat)
+                       break;
+       }
+       return compat;
+ }
+ static const struct cfg80211_chan_def *
+ ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
+                                      struct ieee80211_chanctx *ctx,
+                                      const struct cfg80211_chan_def *compat)
+ {
+       struct ieee80211_sub_if_data *sdata;
+       lockdep_assert_held(&local->chanctx_mtx);
+       list_for_each_entry(sdata, &ctx->assigned_vifs,
+                           assigned_chanctx_list) {
+               if (sdata->reserved_chanctx != NULL)
+                       continue;
+               if (!compat)
+                       compat = &sdata->vif.bss_conf.chandef;
+               compat = cfg80211_chandef_compatible(
+                               &sdata->vif.bss_conf.chandef, compat);
+               if (!compat)
+                       break;
+       }
+       return compat;
+ }
+ static const struct cfg80211_chan_def *
+ ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx,
+                                  const struct cfg80211_chan_def *compat)
+ {
+       lockdep_assert_held(&local->chanctx_mtx);
+       compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
+       if (!compat)
+               return NULL;
+       compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
+       if (!compat)
+               return NULL;
+       return compat;
+ }
+ static bool
+ ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
+                                     struct ieee80211_chanctx *ctx,
+                                     const struct cfg80211_chan_def *def)
+ {
+       lockdep_assert_held(&local->chanctx_mtx);
+       if (ieee80211_chanctx_combined_chandef(local, ctx, def))
+               return true;
+       if (!list_empty(&ctx->reserved_vifs) &&
+           ieee80211_chanctx_reserved_chandef(local, ctx, def))
+               return true;
+       return false;
+ }
+ static struct ieee80211_chanctx *
+ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
+                                  const struct cfg80211_chan_def *chandef,
+                                  enum ieee80211_chanctx_mode mode)
+ {
+       struct ieee80211_chanctx *ctx;
+       lockdep_assert_held(&local->chanctx_mtx);
+       if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
+               return NULL;
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
+                       continue;
+               if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
+                                                          chandef))
+                       continue;
+               return ctx;
+       }
+       return NULL;
+ }
  static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
  {
        switch (sta->bandwidth) {
@@@ -190,6 -354,11 +354,11 @@@ ieee80211_find_chanctx(struct ieee80211
                if (!compat)
                        continue;
  
+               compat = ieee80211_chanctx_reserved_chandef(local, ctx,
+                                                           compat);
+               if (!compat)
+                       continue;
                ieee80211_change_chanctx(local, ctx, compat);
  
                return ctx;
@@@ -217,62 -386,91 +386,91 @@@ static bool ieee80211_is_radar_required
  }
  
  static struct ieee80211_chanctx *
- ieee80211_new_chanctx(struct ieee80211_local *local,
-                     const struct cfg80211_chan_def *chandef,
-                     enum ieee80211_chanctx_mode mode)
+ ieee80211_alloc_chanctx(struct ieee80211_local *local,
+                       const struct cfg80211_chan_def *chandef,
+                       enum ieee80211_chanctx_mode mode)
  {
        struct ieee80211_chanctx *ctx;
-       u32 changed;
-       int err;
  
        lockdep_assert_held(&local->chanctx_mtx);
  
        ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
        if (!ctx)
-               return ERR_PTR(-ENOMEM);
+               return NULL;
  
+       INIT_LIST_HEAD(&ctx->assigned_vifs);
+       INIT_LIST_HEAD(&ctx->reserved_vifs);
        ctx->conf.def = *chandef;
        ctx->conf.rx_chains_static = 1;
        ctx->conf.rx_chains_dynamic = 1;
        ctx->mode = mode;
        ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
        ieee80211_recalc_chanctx_min_def(local, ctx);
+       return ctx;
+ }
+ static int ieee80211_add_chanctx(struct ieee80211_local *local,
+                                struct ieee80211_chanctx *ctx)
+ {
+       u32 changed;
+       int err;
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
        if (!local->use_chanctx)
                local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
  
-       /* we hold the mutex to prevent idle from changing */
-       lockdep_assert_held(&local->mtx);
        /* turn idle off *before* setting channel -- some drivers need that */
        changed = ieee80211_idle_off(local);
        if (changed)
                ieee80211_hw_config(local, changed);
  
        if (!local->use_chanctx) {
-               local->_oper_chandef = *chandef;
+               local->_oper_chandef = ctx->conf.def;
 -              ieee80211_hw_config(local, 0);
 +              ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
        } else {
                err = drv_add_chanctx(local, ctx);
                if (err) {
-                       kfree(ctx);
                        ieee80211_recalc_idle(local);
-                       return ERR_PTR(err);
+                       return err;
                }
        }
  
-       /* and keep the mutex held until the new chanctx is on the list */
-       list_add_rcu(&ctx->list, &local->chanctx_list);
+       return 0;
+ }
+ static struct ieee80211_chanctx *
+ ieee80211_new_chanctx(struct ieee80211_local *local,
+                     const struct cfg80211_chan_def *chandef,
+                     enum ieee80211_chanctx_mode mode)
+ {
+       struct ieee80211_chanctx *ctx;
+       int err;
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
  
+       ctx = ieee80211_alloc_chanctx(local, chandef, mode);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+       err = ieee80211_add_chanctx(local, ctx);
+       if (err) {
+               kfree(ctx);
+               return ERR_PTR(err);
+       }
+       list_add_rcu(&ctx->list, &local->chanctx_list);
        return ctx;
  }
  
- static void ieee80211_free_chanctx(struct ieee80211_local *local,
-                                  struct ieee80211_chanctx *ctx)
+ static void ieee80211_del_chanctx(struct ieee80211_local *local,
+                                 struct ieee80211_chanctx *ctx)
  {
-       bool check_single_channel = false;
        lockdep_assert_held(&local->chanctx_mtx);
  
-       WARN_ON_ONCE(ctx->refcount != 0);
        if (!local->use_chanctx) {
                struct cfg80211_chan_def *chandef = &local->_oper_chandef;
                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
                /* NOTE: Disabling radar is only valid here for
                 * single channel context. To be sure, check it ...
                 */
-               if (local->hw.conf.radar_enabled)
-                       check_single_channel = true;
+               WARN_ON(local->hw.conf.radar_enabled &&
+                       !list_empty(&local->chanctx_list));
                local->hw.conf.radar_enabled = false;
  
 -              ieee80211_hw_config(local, 0);
 +              ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
        } else {
                drv_remove_chanctx(local, ctx);
        }
  
-       list_del_rcu(&ctx->list);
-       kfree_rcu(ctx, rcu_head);
-       /* throw a warning if this wasn't the only channel context. */
-       WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
        ieee80211_recalc_idle(local);
  }
  
- static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
-                                       struct ieee80211_chanctx *ctx)
+ static void ieee80211_free_chanctx(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx)
  {
-       struct ieee80211_local *local = sdata->local;
-       int ret;
        lockdep_assert_held(&local->chanctx_mtx);
  
-       ret = drv_assign_vif_chanctx(local, sdata, ctx);
-       if (ret)
-               return ret;
-       rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
-       ctx->refcount++;
-       ieee80211_recalc_txpower(sdata);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
-       sdata->vif.bss_conf.idle = false;
+       WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
  
-       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-           sdata->vif.type != NL80211_IFTYPE_MONITOR)
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
-       return 0;
+       list_del_rcu(&ctx->list);
+       ieee80211_del_chanctx(local, ctx);
+       kfree_rcu(ctx, rcu_head);
  }
  
  static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
@@@ -384,30 -563,58 +563,58 @@@ static void ieee80211_recalc_radar_chan
        drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
  }
  
- static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
-                                          struct ieee80211_chanctx *ctx)
+ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+                                       struct ieee80211_chanctx *new_ctx)
  {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *curr_ctx = NULL;
+       int ret = 0;
  
-       lockdep_assert_held(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
  
-       ctx->refcount--;
-       rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+       if (conf) {
+               curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
  
-       sdata->vif.bss_conf.idle = true;
+               drv_unassign_vif_chanctx(local, sdata, curr_ctx);
+               conf = NULL;
+               list_del(&sdata->assigned_chanctx_list);
+       }
  
-       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-           sdata->vif.type != NL80211_IFTYPE_MONITOR)
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
+       if (new_ctx) {
+               ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
+               if (ret)
+                       goto out;
  
-       drv_unassign_vif_chanctx(local, sdata, ctx);
+               conf = &new_ctx->conf;
+               list_add(&sdata->assigned_chanctx_list,
+                        &new_ctx->assigned_vifs);
+       }
  
-       if (ctx->refcount > 0) {
-               ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
-               ieee80211_recalc_smps_chanctx(local, ctx);
-               ieee80211_recalc_radar_chanctx(local, ctx);
-               ieee80211_recalc_chanctx_min_def(local, ctx);
+ out:
+       rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
+       sdata->vif.bss_conf.idle = !conf;
+       if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
+               ieee80211_recalc_chanctx_chantype(local, curr_ctx);
+               ieee80211_recalc_smps_chanctx(local, curr_ctx);
+               ieee80211_recalc_radar_chanctx(local, curr_ctx);
+               ieee80211_recalc_chanctx_min_def(local, curr_ctx);
        }
+       if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
+               ieee80211_recalc_txpower(sdata);
+               ieee80211_recalc_chanctx_min_def(local, new_ctx);
+       }
+       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+           sdata->vif.type != NL80211_IFTYPE_MONITOR)
+               ieee80211_bss_info_change_notify(sdata,
+                                                BSS_CHANGED_IDLE);
+       return ret;
  }
  
  static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
  
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
  
-       ieee80211_unassign_vif_chanctx(sdata, ctx);
-       if (ctx->refcount == 0)
+       if (sdata->reserved_chanctx)
+               ieee80211_vif_unreserve_chanctx(sdata);
+       ieee80211_assign_vif_chanctx(sdata, NULL);
+       if (ieee80211_chanctx_refcount(local, ctx) == 0)
                ieee80211_free_chanctx(local, ctx);
  }
  
@@@ -492,13 -702,6 +702,13 @@@ void ieee80211_recalc_smps_chanctx(stru
                rx_chains_static = max(rx_chains_static, needed_static);
                rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
        }
 +
 +      /* Disable SMPS for the monitor interface */
 +      sdata = rcu_dereference(local->monitor_sdata);
 +      if (sdata &&
 +          rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf)
 +              rx_chains_dynamic = rx_chains_static = local->rx_chains;
 +
        rcu_read_unlock();
  
        if (!local->use_chanctx) {
@@@ -526,6 -729,7 +736,7 @@@ int ieee80211_vif_use_channel(struct ie
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx *ctx;
+       u8 radar_detect_width = 0;
        int ret;
  
        lockdep_assert_held(&local->mtx);
        WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
  
        mutex_lock(&local->chanctx_mtx);
+       ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
+                                           chandef,
+                                           sdata->wdev.iftype);
+       if (ret < 0)
+               goto out;
+       if (ret > 0)
+               radar_detect_width = BIT(chandef->width);
+       sdata->radar_required = ret;
+       ret = ieee80211_check_combinations(sdata, chandef, mode,
+                                          radar_detect_width);
+       if (ret < 0)
+               goto out;
        __ieee80211_vif_release_channel(sdata);
  
        ctx = ieee80211_find_chanctx(local, chandef, mode);
        ret = ieee80211_assign_vif_chanctx(sdata, ctx);
        if (ret) {
                /* if assign fails refcount stays the same */
-               if (ctx->refcount == 0)
+               if (ieee80211_chanctx_refcount(local, ctx) == 0)
                        ieee80211_free_chanctx(local, ctx);
                goto out;
        }
        return ret;
  }
  
+ static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+                                         struct ieee80211_chanctx *ctx,
+                                         u32 *changed)
+ {
+       struct ieee80211_local *local = sdata->local;
+       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
+       u32 chanctx_changed = 0;
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+                                    IEEE80211_CHAN_DISABLED))
+               return -EINVAL;
+       if (ieee80211_chanctx_refcount(local, ctx) != 1)
+               return -EINVAL;
+       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
+               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+               *changed |= BSS_CHANGED_BANDWIDTH;
+       }
+       sdata->vif.bss_conf.chandef = *chandef;
+       ctx->conf.def = *chandef;
+       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+       drv_change_chanctx(local, ctx, chanctx_changed);
+       ieee80211_recalc_chanctx_chantype(local, ctx);
+       ieee80211_recalc_smps_chanctx(local, ctx);
+       ieee80211_recalc_radar_chanctx(local, ctx);
+       ieee80211_recalc_chanctx_min_def(local, ctx);
+       return 0;
+ }
  int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
                                 u32 *changed)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
-       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
        int ret;
-       u32 chanctx_changed = 0;
  
        lockdep_assert_held(&local->mtx);
  
        if (WARN_ON(!sdata->vif.csa_active))
                return -EINVAL;
  
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
-                                    IEEE80211_CHAN_DISABLED))
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
+       }
+       ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
+  out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+ }
+ static void
+ __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+                                     bool clear)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sub_if_data *vlan;
+       struct ieee80211_chanctx_conf *conf;
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+               return;
+       lockdep_assert_held(&local->mtx);
+       /* Check that conf exists, even when clearing this function
+        * must be called with the AP's channel context still there
+        * as it would otherwise cause VLANs to have an invalid
+        * channel context pointer for a while, possibly pointing
+        * to a channel context that has already been freed.
+        */
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                               lockdep_is_held(&local->chanctx_mtx));
+       WARN_ON(!conf);
+       if (clear)
+               conf = NULL;
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
+ }
+ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+                                        bool clear)
+ {
+       struct ieee80211_local *local = sdata->local;
+       mutex_lock(&local->chanctx_mtx);
+       __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
+       mutex_unlock(&local->chanctx_mtx);
+ }
+ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
+ {
+       struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
+       lockdep_assert_held(&sdata->local->chanctx_mtx);
+       if (WARN_ON(!ctx))
                return -EINVAL;
  
+       list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
+       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
+               ieee80211_free_chanctx(sdata->local, ctx);
+       return 0;
+ }
+ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum ieee80211_chanctx_mode mode,
+                                 bool radar_required)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *new_ctx, *curr_ctx;
+       int ret = 0;
        mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (!conf) {
                goto out;
        }
  
-       ctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (ctx->refcount != 1) {
+       curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
+       if (!new_ctx) {
+               if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 &&
+                   (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
+                       /* if we're the only users of the chanctx and
+                        * the driver supports changing a running
+                        * context, reserve our current context
+                        */
+                       new_ctx = curr_ctx;
+               } else if (ieee80211_can_create_new_chanctx(local)) {
+                       /* create a new context and reserve it */
+                       new_ctx = ieee80211_new_chanctx(local, chandef, mode);
+                       if (IS_ERR(new_ctx)) {
+                               ret = PTR_ERR(new_ctx);
+                               goto out;
+                       }
+               } else {
+                       ret = -EBUSY;
+                       goto out;
+               }
+       }
+       list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
+       sdata->reserved_chanctx = new_ctx;
+       sdata->reserved_chandef = *chandef;
+       sdata->reserved_radar_required = radar_required;
+ out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+ }
+ int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
+                                      u32 *changed)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *ctx;
+       struct ieee80211_chanctx *old_ctx;
+       struct ieee80211_chanctx_conf *conf;
+       int ret;
+       u32 tmp_changed = *changed;
+       /* TODO: need to recheck if the chandef is usable etc.? */
+       lockdep_assert_held(&local->mtx);
+       mutex_lock(&local->chanctx_mtx);
+       ctx = sdata->reserved_chanctx;
+       if (WARN_ON(!ctx)) {
                ret = -EINVAL;
                goto out;
        }
  
-       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
-               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
-               *changed |= BSS_CHANGED_BANDWIDTH;
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
        }
  
-       sdata->vif.bss_conf.chandef = *chandef;
-       ctx->conf.def = *chandef;
+       old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
  
-       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
-       drv_change_chanctx(local, ctx, chanctx_changed);
+       if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
+               tmp_changed |= BSS_CHANGED_BANDWIDTH;
+       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+       /* unref our reservation */
+       sdata->reserved_chanctx = NULL;
+       sdata->radar_required = sdata->reserved_radar_required;
+       list_del(&sdata->reserved_chanctx_list);
+       if (old_ctx == ctx) {
+               /* This is our own context, just change it */
+               ret = __ieee80211_vif_change_channel(sdata, old_ctx,
+                                                    &tmp_changed);
+               if (ret)
+                       goto out;
+       } else {
+               ret = ieee80211_assign_vif_chanctx(sdata, ctx);
+               if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+                       ieee80211_free_chanctx(local, old_ctx);
+               if (ret) {
+                       /* if assign fails refcount stays the same */
+                       if (ieee80211_chanctx_refcount(local, ctx) == 0)
+                               ieee80211_free_chanctx(local, ctx);
+                       goto out;
+               }
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+       }
+       *changed = tmp_changed;
  
        ieee80211_recalc_chanctx_chantype(local, ctx);
        ieee80211_recalc_smps_chanctx(local, ctx);
        ieee80211_recalc_radar_chanctx(local, ctx);
        ieee80211_recalc_chanctx_min_def(local, ctx);
-       ret = 0;
-  out:
+ out:
        mutex_unlock(&local->chanctx_mtx);
        return ret;
  }
@@@ -695,40 -1108,6 +1115,6 @@@ void ieee80211_vif_vlan_copy_chanctx(st
        mutex_unlock(&local->chanctx_mtx);
  }
  
- void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
-                                        bool clear)
- {
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sub_if_data *vlan;
-       struct ieee80211_chanctx_conf *conf;
-       ASSERT_RTNL();
-       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
-               return;
-       mutex_lock(&local->chanctx_mtx);
-       /*
-        * Check that conf exists, even when clearing this function
-        * must be called with the AP's channel context still there
-        * as it would otherwise cause VLANs to have an invalid
-        * channel context pointer for a while, possibly pointing
-        * to a channel context that has already been freed.
-        */
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                               lockdep_is_held(&local->chanctx_mtx));
-       WARN_ON(!conf);
-       if (clear)
-               conf = NULL;
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
-       mutex_unlock(&local->chanctx_mtx);
- }
  void ieee80211_iter_chan_contexts_atomic(
        struct ieee80211_hw *hw,
        void (*iter)(struct ieee80211_hw *hw,
diff --combined net/mac80211/main.c
index 4c1bf61bc778683dc352ebb32a585d45649168a6,69175f1539b7669f1f267f4857a4585a6958b835..27b9364cdf177d8f28e410bf50eff708bc40e424
@@@ -148,8 -148,6 +148,8 @@@ static u32 ieee80211_hw_conf_chan(struc
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                if (!rcu_access_pointer(sdata->vif.chanctx_conf))
                        continue;
 +              if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 +                      continue;
                power = min(power, sdata->vif.bss_conf.txpower);
        }
        rcu_read_unlock();
@@@ -201,7 -199,7 +201,7 @@@ void ieee80211_bss_info_change_notify(s
  {
        struct ieee80211_local *local = sdata->local;
  
 -      if (!changed)
 +      if (!changed || sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                return;
  
        drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);
@@@ -340,7 -338,7 +340,7 @@@ static int ieee80211_ifa_changed(struc
  
        sdata_unlock(sdata);
  
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
  }
  #endif
  
@@@ -371,7 -369,7 +371,7 @@@ static int ieee80211_ifa6_changed(struc
  
        drv_ipv6_addr_change(local, sdata, idev);
  
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
  }
  #endif
  
@@@ -446,7 -444,9 +446,9 @@@ static const struct ieee80211_ht_cap ma
        .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                                IEEE80211_HT_CAP_MAX_AMSDU |
                                IEEE80211_HT_CAP_SGI_20 |
-                               IEEE80211_HT_CAP_SGI_40),
+                               IEEE80211_HT_CAP_SGI_40 |
+                               IEEE80211_HT_CAP_LDPC_CODING |
+                               IEEE80211_HT_CAP_40MHZ_INTOLERANT),
        .mcs = {
                .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
                             0xff, 0xff, 0xff, 0xff, 0xff, },