]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
authorJohn W. Linville <linville@tuxdriver.com>
Thu, 13 Feb 2014 19:43:02 +0000 (14:43 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 13 Feb 2014 19:43:02 +0000 (14:43 -0500)
22 files changed:
1  2 
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/rtl818x/rtl8180/dev.c
net/mac80211/cfg.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/iface.c
net/mac80211/tx.c
net/wireless/core.c
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/nl80211.h

index c75493f4236a652bf9f0109c35cb8fdca411715b,99a203174f45a04b50248a370ca4113e256e75ca..ba83f582bf4a71c74b676a38f82e209125d1cec2
@@@ -262,6 -262,8 +262,8 @@@ enum tid_aggr_state 
  struct ath9k_htc_sta {
        u8 index;
        enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID];
+       struct work_struct rc_update_work;
+       struct ath9k_htc_priv *htc_priv;
  };
  
  #define ATH9K_HTC_RXBUF 256
@@@ -275,6 -277,7 +277,6 @@@ struct ath9k_htc_rxbuf 
  };
  
  struct ath9k_htc_rx {
 -      int last_rssi; /* FIXME: per-STA */
        struct list_head rxbuf;
        spinlock_t rxbuflock;
  };
index 9db8aefb86006d38560236ee4c1a79cdb6a29105,c57d6b859c043207a11883b7a2584241c16aa7dd..8d0b9bcb47b465059399bfaea0589406751e906b
@@@ -34,6 -34,10 +34,10 @@@ static int ath9k_htc_btcoex_enable
  module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444);
  MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
  
+ static int ath9k_ps_enable;
+ module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
+ MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
  #define CHAN2G(_freq, _idx)  { \
        .center_freq = (_freq), \
        .hw_value = (_idx), \
@@@ -607,7 -611,6 +611,7 @@@ static void ath9k_init_misc(struct ath9
  
        memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
  
 +      common->last_rssi = ATH_RSSI_DUMMY_MARKER;
        priv->ah->opmode = NL80211_IFTYPE_STATION;
  }
  
@@@ -726,12 -729,14 +730,14 @@@ static void ath9k_set_hw_capab(struct a
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_HAS_RATE_CONTROL |
                IEEE80211_HW_RX_INCLUDES_FCS |
-               IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_PS_NULLFUNC_STACK |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_MFP_CAPABLE |
                IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
  
+       if (ath9k_ps_enable)
+               hw->flags |= IEEE80211_HW_SUPPORTS_PS;
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC) |
index 228549a65ab9d17de7fd83f40777abc0975d86db,c9254a61ca52d0984c23efdf4cb01aeb6b40ae23..90dad4172b0a04a1c61ce7805f1c07e7ed146bd7
@@@ -1270,18 -1270,50 +1270,50 @@@ static void ath9k_htc_configure_filter(
        mutex_unlock(&priv->mutex);
  }
  
+ static void ath9k_htc_sta_rc_update_work(struct work_struct *work)
+ {
+       struct ath9k_htc_sta *ista =
+           container_of(work, struct ath9k_htc_sta, rc_update_work);
+       struct ieee80211_sta *sta =
+           container_of((void *)ista, struct ieee80211_sta, drv_priv);
+       struct ath9k_htc_priv *priv = ista->htc_priv;
+       struct ath_common *common = ath9k_hw_common(priv->ah);
+       struct ath9k_htc_target_rate trate;
+       mutex_lock(&priv->mutex);
+       ath9k_htc_ps_wakeup(priv);
+       memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
+       ath9k_htc_setup_rate(priv, sta, &trate);
+       if (!ath9k_htc_send_rate_cmd(priv, &trate))
+               ath_dbg(common, CONFIG,
+                       "Supported rates for sta: %pM updated, rate caps: 0x%X\n",
+                       sta->addr, be32_to_cpu(trate.capflags));
+       else
+               ath_dbg(common, CONFIG,
+                       "Unable to update supported rates for sta: %pM\n",
+                       sta->addr);
+       ath9k_htc_ps_restore(priv);
+       mutex_unlock(&priv->mutex);
+ }
  static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta)
  {
        struct ath9k_htc_priv *priv = hw->priv;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
        int ret;
  
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
        ret = ath9k_htc_add_station(priv, vif, sta);
-       if (!ret)
+       if (!ret) {
+               INIT_WORK(&ista->rc_update_work, ath9k_htc_sta_rc_update_work);
+               ista->htc_priv = priv;
                ath9k_htc_init_rate(priv, sta);
+       }
        ath9k_htc_ps_restore(priv);
        mutex_unlock(&priv->mutex);
  
@@@ -1293,12 -1325,13 +1325,13 @@@ static int ath9k_htc_sta_remove(struct 
                                struct ieee80211_sta *sta)
  {
        struct ath9k_htc_priv *priv = hw->priv;
-       struct ath9k_htc_sta *ista;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
        int ret;
  
+       cancel_work_sync(&ista->rc_update_work);
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
-       ista = (struct ath9k_htc_sta *) sta->drv_priv;
        htc_sta_drain(priv->htc, ista->index);
        ret = ath9k_htc_remove_station(priv, vif, sta);
        ath9k_htc_ps_restore(priv);
@@@ -1311,28 -1344,12 +1344,12 @@@ static void ath9k_htc_sta_rc_update(str
                                    struct ieee80211_vif *vif,
                                    struct ieee80211_sta *sta, u32 changed)
  {
-       struct ath9k_htc_priv *priv = hw->priv;
-       struct ath_common *common = ath9k_hw_common(priv->ah);
-       struct ath9k_htc_target_rate trate;
-       mutex_lock(&priv->mutex);
-       ath9k_htc_ps_wakeup(priv);
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
  
-       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
-               memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
-               ath9k_htc_setup_rate(priv, sta, &trate);
-               if (!ath9k_htc_send_rate_cmd(priv, &trate))
-                       ath_dbg(common, CONFIG,
-                               "Supported rates for sta: %pM updated, rate caps: 0x%X\n",
-                               sta->addr, be32_to_cpu(trate.capflags));
-               else
-                       ath_dbg(common, CONFIG,
-                               "Unable to update supported rates for sta: %pM\n",
-                               sta->addr);
-       }
+       if (!(changed & IEEE80211_RC_SUPP_RATES_CHANGED))
+               return;
  
-       ath9k_htc_ps_restore(priv);
-       mutex_unlock(&priv->mutex);
+       schedule_work(&ista->rc_update_work);
  }
  
  static int ath9k_htc_conf_tx(struct ieee80211_hw *hw,
@@@ -1457,7 -1474,6 +1474,7 @@@ static void ath9k_htc_bss_iter(void *da
  
        if ((vif->type == NL80211_IFTYPE_STATION) && bss_conf->assoc) {
                common->curaid = bss_conf->aid;
 +              common->last_rssi = ATH_RSSI_DUMMY_MARKER;
                memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
        }
  }
index 15b8e783d1a7dd472bea73e3874212ce99043b01,11eab9f01fd89ac9bb8f9b665646051a6386ac0a..5db01b4212c8201383473bfdf886137f02b41491
@@@ -23,6 -23,7 +23,6 @@@
  
  #include "hw.h"
  #include "hw-ops.h"
 -#include "rc.h"
  #include "ar9003_mac.h"
  #include "ar9003_mci.h"
  #include "ar9003_phy.h"
@@@ -1315,7 -1316,7 +1315,7 @@@ static bool ath9k_hw_set_reset(struct a
        if (AR_SREV_9300_20_OR_LATER(ah))
                udelay(50);
        else if (AR_SREV_9100(ah))
-               udelay(10000);
+               mdelay(10);
        else
                udelay(100);
  
@@@ -2050,9 -2051,8 +2050,8 @@@ static bool ath9k_hw_set_power_awake(st
  
        REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
                    AR_RTC_FORCE_WAKE_EN);
        if (AR_SREV_9100(ah))
-               udelay(10000);
+               mdelay(10);
        else
                udelay(50);
  
index 67411d21c9a5a1c965f68582bccca058de0aa383,1fc2e5a26b525b5695be27cd3d29d71b6732118d..07a0315dd2f64baff254e18da191c273c32e4173
@@@ -57,6 -57,10 +57,10 @@@ static int ath9k_bt_ant_diversity
  module_param_named(bt_ant_diversity, ath9k_bt_ant_diversity, int, 0444);
  MODULE_PARM_DESC(bt_ant_diversity, "Enable WLAN/BT RX antenna diversity");
  
+ static int ath9k_ps_enable;
+ module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
+ MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
  bool is_ath9k_unloaded;
  /* We use the hw_value as an index into our private channel structure */
  
@@@ -534,7 -538,7 +538,7 @@@ static void ath9k_init_misc(struct ath_
  
        setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
  
 -      sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
 +      common->last_rssi = ATH_RSSI_DUMMY_MARKER;
        sc->config.txpowlimit = ATH_TXPOWER_MAX;
        memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
        sc->beacon.slottime = ATH9K_SLOT_TIME_9;
@@@ -903,13 -907,15 +907,15 @@@ static void ath9k_set_hw_capab(struct a
        hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
                IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                IEEE80211_HW_SIGNAL_DBM |
-               IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_PS_NULLFUNC_STACK |
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE |
                IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
  
+       if (ath9k_ps_enable)
+               hw->flags |= IEEE80211_HW_SUPPORTS_PS;
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
                hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
  
@@@ -1100,11 -1106,19 +1106,11 @@@ static int __init ath9k_init(void
  {
        int error;
  
 -      /* Register rate control algorithm */
 -      error = ath_rate_control_register();
 -      if (error != 0) {
 -              pr_err("Unable to register rate control algorithm: %d\n",
 -                     error);
 -              goto err_out;
 -      }
 -
        error = ath_pci_init();
        if (error < 0) {
                pr_err("No PCI devices found, driver not installed\n");
                error = -ENODEV;
 -              goto err_rate_unregister;
 +              goto err_out;
        }
  
        error = ath_ahb_init();
  
   err_pci_exit:
        ath_pci_exit();
 -
 - err_rate_unregister:
 -      ath_rate_control_unregister();
   err_out:
        return error;
  }
@@@ -1127,6 -1144,7 +1133,6 @@@ static void __exit ath9k_exit(void
        is_ath9k_unloaded = true;
        ath_ahb_exit();
        ath_pci_exit();
 -      ath_rate_control_unregister();
        pr_info("%s: Driver unloaded\n", dev_info);
  }
  module_exit(ath9k_exit);
index 42780971aa04392cdc6e53e0db077abdb4ced69f,725e954d8475284a0f2b6332d3627c638dd6d01a..53b9cad504779fb4712e8b3e4da893d3d61b13fe
@@@ -182,6 -182,11 +182,11 @@@ static int iwl_init_channel_map(struct 
  
        for (ch_idx = 0; ch_idx < IWL_NUM_CHANNELS; ch_idx++) {
                ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
+               if (ch_idx >= NUM_2GHZ_CHANNELS &&
+                   !data->sku_cap_band_52GHz_enable)
+                       ch_flags &= ~NVM_CHANNEL_VALID;
                if (!(ch_flags & NVM_CHANNEL_VALID)) {
                        IWL_DEBUG_EEPROM(dev,
                                         "Ch. %d Flags %x [%sGHz] - No traffic\n",
@@@ -397,7 -402,11 +402,7 @@@ iwl_parse_nvm_data(struct device *dev, 
        iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE,
                        tx_chains, rx_chains);
  
 -      data->calib_version = 255;   /* TODO:
 -                                      this value will prevent some checks from
 -                                      failing, we need to check if this
 -                                      field is still needed, and if it does,
 -                                      where is it in the NVM*/
 +      data->calib_version = 255;
  
        return data;
  }
index ba4dcabf7c4a508924a239c5db4aaa35bfb62b9b,6bf9766e59821a1b45e0c666e2a59172fc84a9bc..beaf8140abbfa3d848743700d03a92314c7b2fbe
@@@ -66,7 -66,6 +66,7 @@@
  #include <linux/netdevice.h>
  #include <linux/etherdevice.h>
  #include <linux/ip.h>
 +#include <linux/if_arp.h>
  #include <net/mac80211.h>
  #include <net/tcp.h>
  
@@@ -129,117 -128,6 +129,117 @@@ static const struct wiphy_wowlan_tcp_su
  };
  #endif
  
 +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
 +/*
 + * Use the reserved field to indicate magic values.
 + * these values will only be used internally by the driver,
 + * and won't make it to the fw (reserved will be 0).
 + * BC_FILTER_MAGIC_IP - configure the val of this attribute to
 + *    be the vif's ip address. in case there is not a single
 + *    ip address (0, or more than 1), this attribute will
 + *    be skipped.
 + * BC_FILTER_MAGIC_MAC - set the val of this attribute to
 + *    the LSB bytes of the vif's mac address
 + */
 +enum {
 +      BC_FILTER_MAGIC_NONE = 0,
 +      BC_FILTER_MAGIC_IP,
 +      BC_FILTER_MAGIC_MAC,
 +};
 +
 +static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
 +      {
 +              /* arp */
 +              .discard = 0,
 +              .frame_type = BCAST_FILTER_FRAME_TYPE_ALL,
 +              .attrs = {
 +                      {
 +                              /* frame type - arp, hw type - ethernet */
 +                              .offset_type =
 +                                      BCAST_FILTER_OFFSET_PAYLOAD_START,
 +                              .offset = sizeof(rfc1042_header),
 +                              .val = cpu_to_be32(0x08060001),
 +                              .mask = cpu_to_be32(0xffffffff),
 +                      },
 +                      {
 +                              /* arp dest ip */
 +                              .offset_type =
 +                                      BCAST_FILTER_OFFSET_PAYLOAD_START,
 +                              .offset = sizeof(rfc1042_header) + 2 +
 +                                        sizeof(struct arphdr) +
 +                                        ETH_ALEN + sizeof(__be32) +
 +                                        ETH_ALEN,
 +                              .mask = cpu_to_be32(0xffffffff),
 +                              /* mark it as special field */
 +                              .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP),
 +                      },
 +              },
 +      },
 +      {
 +              /* dhcp offer bcast */
 +              .discard = 0,
 +              .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4,
 +              .attrs = {
 +                      {
 +                              /* udp dest port - 68 (bootp client)*/
 +                              .offset_type = BCAST_FILTER_OFFSET_IP_END,
 +                              .offset = offsetof(struct udphdr, dest),
 +                              .val = cpu_to_be32(0x00440000),
 +                              .mask = cpu_to_be32(0xffff0000),
 +                      },
 +                      {
 +                              /* dhcp - lsb bytes of client hw address */
 +                              .offset_type = BCAST_FILTER_OFFSET_IP_END,
 +                              .offset = 38,
 +                              .mask = cpu_to_be32(0xffffffff),
 +                              /* mark it as special field */
 +                              .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC),
 +                      },
 +              },
 +      },
 +      /* last filter must be empty */
 +      {},
 +};
 +#endif
 +
 +void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 +{
 +      if (!mvm->trans->cfg->d0i3)
 +              return;
 +
 +      IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
 +      WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
 +      iwl_trans_ref(mvm->trans);
 +}
 +
 +void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
 +{
 +      if (!mvm->trans->cfg->d0i3)
 +              return;
 +
 +      IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
 +      WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
 +      iwl_trans_unref(mvm->trans);
 +}
 +
 +static void
 +iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
 +{
 +      int i;
 +
 +      if (!mvm->trans->cfg->d0i3)
 +              return;
 +
 +      for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
 +              if (ref == i)
 +                      continue;
 +
 +              IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
 +              clear_bit(i, mvm->ref_bitmap);
 +              iwl_trans_unref(mvm->trans);
 +      }
 +}
 +
  static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
  {
        int i;
@@@ -315,9 -203,6 +315,9 @@@ int iwl_mvm_mac_setup_register(struct i
        hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
                                       REGULATORY_DISABLE_BEACON_HINTS;
  
 +      if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
 +              hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 +
        hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
        hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(iwl_mvm_iface_combinations);
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
  
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
+       if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
                hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
                hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
        }
  #endif
  
 +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
 +      /* assign default bcast filtering configuration */
 +      mvm->bcast_filters = iwl_mvm_default_bcast_filters;
 +#endif
 +
        ret = iwl_mvm_leds_init(mvm);
        if (ret)
                return ret;
@@@ -425,9 -305,6 +425,9 @@@ static void iwl_mvm_mac_tx(struct ieee8
                           struct sk_buff *skb)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 +      struct ieee80211_sta *sta = control->sta;
 +      struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 +      struct ieee80211_hdr *hdr = (void *)skb->data;
  
        if (iwl_mvm_is_radio_killed(mvm)) {
                IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
            !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
                goto drop;
  
 -      if (control->sta) {
 -              if (iwl_mvm_tx_skb(mvm, skb, control->sta))
 +      /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
 +      if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER &&
 +                   ieee80211_is_mgmt(hdr->frame_control) &&
 +                   !ieee80211_is_deauth(hdr->frame_control) &&
 +                   !ieee80211_is_disassoc(hdr->frame_control) &&
 +                   !ieee80211_is_action(hdr->frame_control)))
 +              sta = NULL;
 +
 +      if (sta) {
 +              if (iwl_mvm_tx_skb(mvm, skb, sta))
                        goto drop;
                return;
        }
@@@ -547,7 -416,6 +547,7 @@@ static void iwl_mvm_restart_cleanup(str
                iwl_mvm_cleanup_iterator, mvm);
  
        mvm->p2p_device_vif = NULL;
 +      mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
  
        iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
  
        ieee80211_wake_queues(mvm->hw);
  
 +      /* cleanup all stale references (scan, roc), but keep the
 +       * ucode_down ref until reconfig is complete */
 +      iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
 +
        mvm->vif_count = 0;
        mvm->rx_ba_sessions = 0;
  }
@@@ -593,9 -457,6 +593,9 @@@ static void iwl_mvm_mac_restart_complet
                IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
                        ret);
  
 +      /* allow transport/FW low power modes */
 +      iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
 +
        mutex_unlock(&mvm->mutex);
  }
  
@@@ -603,14 -464,9 +603,14 @@@ static void iwl_mvm_mac_stop(struct iee
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
  
 +      flush_work(&mvm->d0i3_exit_work);
        flush_work(&mvm->async_handlers_wk);
  
        mutex_lock(&mvm->mutex);
 +
 +      /* disallow low power states when the FW is down */
 +      iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 +
        /* async_handlers_wk is now blocked */
  
        /*
        cancel_work_sync(&mvm->async_handlers_wk);
  }
  
 -static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
 -                                        struct ieee80211_vif *vif)
 -{
 -      struct iwl_mvm *mvm = data;
 -
 -      iwl_mvm_power_update_mode(mvm, vif);
 -}
 -
  static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
  {
        u16 i;
@@@ -703,8 -567,7 +703,8 @@@ static int iwl_mvm_mac_add_interface(st
            vif->type == NL80211_IFTYPE_ADHOC) {
                u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
                ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
 -                                             qmask);
 +                                             qmask,
 +                                             ieee80211_vif_type_p2p(vif));
                if (ret) {
                        IWL_ERR(mvm, "Failed to allocate bcast sta\n");
                        goto out_release;
        if (ret)
                goto out_release;
  
 -      iwl_mvm_power_disable(mvm, vif);
 +      ret = iwl_mvm_power_update_mac(mvm, vif);
 +      if (ret)
 +              goto out_release;
  
        /* beacon filtering */
 -      ret = iwl_mvm_disable_beacon_filter(mvm, vif);
 +      ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
        if (ret)
                goto out_remove_mac;
  
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
  
 -      /* TODO: remove this when legacy PM will be discarded */
 -      ieee80211_iterate_active_interfaces(
 -              mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 -              iwl_mvm_power_update_iterator, mvm);
 -
        iwl_mvm_mac_ctxt_release(mvm, vif);
   out_unlock:
        mutex_unlock(&mvm->mutex);
@@@ -870,7 -736,11 +870,7 @@@ static void iwl_mvm_mac_remove_interfac
        if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
  
 -      /* TODO: remove this when legacy PM will be discarded */
 -      ieee80211_iterate_active_interfaces(
 -              mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 -              iwl_mvm_power_update_iterator, mvm);
 -
 +      iwl_mvm_power_update_mac(mvm, vif);
        iwl_mvm_mac_ctxt_remove(mvm, vif);
  
  out_release:
@@@ -988,156 -858,6 +988,156 @@@ out
        *total_flags = 0;
  }
  
 +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
 +struct iwl_bcast_iter_data {
 +      struct iwl_mvm *mvm;
 +      struct iwl_bcast_filter_cmd *cmd;
 +      u8 current_filter;
 +};
 +
 +static void
 +iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif,
 +                       const struct iwl_fw_bcast_filter *in_filter,
 +                       struct iwl_fw_bcast_filter *out_filter)
 +{
 +      struct iwl_fw_bcast_filter_attr *attr;
 +      int i;
 +
 +      memcpy(out_filter, in_filter, sizeof(*out_filter));
 +
 +      for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) {
 +              attr = &out_filter->attrs[i];
 +
 +              if (!attr->mask)
 +                      break;
 +
 +              switch (attr->reserved1) {
 +              case cpu_to_le16(BC_FILTER_MAGIC_IP):
 +                      if (vif->bss_conf.arp_addr_cnt != 1) {
 +                              attr->mask = 0;
 +                              continue;
 +                      }
 +
 +                      attr->val = vif->bss_conf.arp_addr_list[0];
 +                      break;
 +              case cpu_to_le16(BC_FILTER_MAGIC_MAC):
 +                      attr->val = *(__be32 *)&vif->addr[2];
 +                      break;
 +              default:
 +                      break;
 +              }
 +              attr->reserved1 = 0;
 +              out_filter->num_attrs++;
 +      }
 +}
 +
 +static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
 +                                        struct ieee80211_vif *vif)
 +{
 +      struct iwl_bcast_iter_data *data = _data;
 +      struct iwl_mvm *mvm = data->mvm;
 +      struct iwl_bcast_filter_cmd *cmd = data->cmd;
 +      struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 +      struct iwl_fw_bcast_mac *bcast_mac;
 +      int i;
 +
 +      if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs)))
 +              return;
 +
 +      bcast_mac = &cmd->macs[mvmvif->id];
 +
 +      /* enable filtering only for associated stations */
 +      if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
 +              return;
 +
 +      bcast_mac->default_discard = 1;
 +
 +      /* copy all configured filters */
 +      for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) {
 +              /*
 +               * Make sure we don't exceed our filters limit.
 +               * if there is still a valid filter to be configured,
 +               * be on the safe side and just allow bcast for this mac.
 +               */
 +              if (WARN_ON_ONCE(data->current_filter >=
 +                               ARRAY_SIZE(cmd->filters))) {
 +                      bcast_mac->default_discard = 0;
 +                      bcast_mac->attached_filters = 0;
 +                      break;
 +              }
 +
 +              iwl_mvm_set_bcast_filter(vif,
 +                                       &mvm->bcast_filters[i],
 +                                       &cmd->filters[data->current_filter]);
 +
 +              /* skip current filter if it contains no attributes */
 +              if (!cmd->filters[data->current_filter].num_attrs)
 +                      continue;
 +
 +              /* attach the filter to current mac */
 +              bcast_mac->attached_filters |=
 +                              cpu_to_le16(BIT(data->current_filter));
 +
 +              data->current_filter++;
 +      }
 +}
 +
 +bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
 +                                  struct iwl_bcast_filter_cmd *cmd)
 +{
 +      struct iwl_bcast_iter_data iter_data = {
 +              .mvm = mvm,
 +              .cmd = cmd,
 +      };
 +
 +      memset(cmd, 0, sizeof(*cmd));
 +      cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
 +      cmd->max_macs = ARRAY_SIZE(cmd->macs);
 +
 +#ifdef CONFIG_IWLWIFI_DEBUGFS
 +      /* use debugfs filters/macs if override is configured */
 +      if (mvm->dbgfs_bcast_filtering.override) {
 +              memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters,
 +                     sizeof(cmd->filters));
 +              memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs,
 +                     sizeof(cmd->macs));
 +              return true;
 +      }
 +#endif
 +
 +      /* if no filters are configured, do nothing */
 +      if (!mvm->bcast_filters)
 +              return false;
 +
 +      /* configure and attach these filters for each associated sta vif */
 +      ieee80211_iterate_active_interfaces(
 +              mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
 +              iwl_mvm_bcast_filter_iterator, &iter_data);
 +
 +      return true;
 +}
 +static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
 +                                        struct ieee80211_vif *vif)
 +{
 +      struct iwl_bcast_filter_cmd cmd;
 +
 +      if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
 +              return 0;
 +
 +      if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
 +              return 0;
 +
 +      return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
 +                                  sizeof(cmd), &cmd);
 +}
 +#else
 +static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
 +                                               struct ieee80211_vif *vif)
 +{
 +      return 0;
 +}
 +#endif
 +
  static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                             struct ieee80211_vif *vif,
                                             struct ieee80211_bss_conf *bss_conf,
  
                        iwl_mvm_sf_update(mvm, vif, false);
                        iwl_mvm_power_vif_assoc(mvm, vif);
 +                      if (vif->p2p)
 +                              iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /*
                         * If update fails - SF might be running in associated
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
                        if (ret)
                                IWL_ERR(mvm, "failed to remove AP station\n");
 +
 +                      if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
 +                              mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
                        mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
                        /* remove quota for this interface */
                        ret = iwl_mvm_update_quotas(mvm, NULL);
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
 +
 +                      if (vif->p2p)
 +                              iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT);
                }
  
                iwl_mvm_recalc_multicast(mvm);
 +              iwl_mvm_configure_bcast_filter(mvm, vif);
  
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
  
 -              if (!(mvm->fw->ucode_capa.flags &
 -                                      IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
 -                      /* Workaround for FW bug, otherwise FW disables device
 -                       * power save upon disassociation
 -                       */
 -                      ret = iwl_mvm_power_update_mode(mvm, vif);
 -                      if (ret)
 -                              IWL_ERR(mvm, "failed to update power mode\n");
 -              }
                iwl_mvm_bt_coex_vif_change(mvm);
                iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
                                    IEEE80211_SMPS_AUTOMATIC);
                                          &mvmvif->time_event_data);
        } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
                              BSS_CHANGED_QOS)) {
 -              ret = iwl_mvm_power_update_mode(mvm, vif);
 +              ret = iwl_mvm_power_update_mac(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
                IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
                /* reset cqm events tracking */
                mvmvif->bf_data.last_cqm_event = 0;
 -              ret = iwl_mvm_update_beacon_filter(mvm, vif);
 +              ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC);
                if (ret)
                        IWL_ERR(mvm, "failed to update CQM thresholds\n");
        }
 +
 +      if (changes & BSS_CHANGED_ARP_FILTER) {
 +              IWL_DEBUG_MAC80211(mvm, "arp filter changed");
 +              iwl_mvm_configure_bcast_filter(mvm, vif);
 +      }
  }
  
  static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        if (ret)
                goto out_remove;
  
 -      mvmvif->ap_ibss_active = true;
 -
        /* Send the bcast station. At this stage the TBTT and DTIM time events
         * are added and applied to the scheduler */
        ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
  
        /* power updated needs to be done before quotas */
        mvm->bound_vif_cnt++;
 -      iwl_mvm_power_update_binding(mvm, vif, true);
 +      iwl_mvm_power_update_mac(mvm, vif);
  
        ret = iwl_mvm_update_quotas(mvm, vif);
        if (ret)
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
  
 +      iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
 +
        iwl_mvm_bt_coex_vif_change(mvm);
  
        mutex_unlock(&mvm->mutex);
  
  out_quota_failed:
        mvm->bound_vif_cnt--;
 -      iwl_mvm_power_update_binding(mvm, vif, false);
 +      iwl_mvm_power_update_mac(mvm, vif);
        mvmvif->ap_ibss_active = false;
        iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
  out_unbind:
@@@ -1347,8 -1062,6 +1347,8 @@@ static void iwl_mvm_stop_ap_ibss(struc
  
        iwl_mvm_bt_coex_vif_change(mvm);
  
 +      iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS);
 +
        /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
        iwl_mvm_binding_remove_vif(mvm, vif);
  
        mvm->bound_vif_cnt--;
 -      iwl_mvm_power_update_binding(mvm, vif, false);
 +      iwl_mvm_power_update_mac(mvm, vif);
  
        iwl_mvm_mac_ctxt_remove(mvm, vif);
  
@@@ -1372,20 -1085,26 +1372,20 @@@ iwl_mvm_bss_info_changed_ap_ibss(struc
                                 u32 changes)
  {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 -      enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT |
 -                                            BSS_CHANGED_HT |
 -                                            BSS_CHANGED_BANDWIDTH;
 -      int ret;
  
        /* Changes will be applied when the AP/IBSS is started */
        if (!mvmvif->ap_ibss_active)
                return;
  
 -      if (changes & ht_change) {
 -              ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
 -              if (ret)
 -                      IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 -      }
 +      if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
 +                     BSS_CHANGED_BANDWIDTH) &&
 +          iwl_mvm_mac_ctxt_changed(mvm, vif))
 +              IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
  
        /* Need to send a new beacon template to the FW */
 -      if (changes & BSS_CHANGED_BEACON) {
 -              if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
 -                      IWL_WARN(mvm, "Failed updating beacon data\n");
 -      }
 +      if (changes & BSS_CHANGED_BEACON &&
 +          iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
 +              IWL_WARN(mvm, "Failed updating beacon data\n");
  }
  
  static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@@ -1418,8 -1137,6 +1418,8 @@@ static int iwl_mvm_mac_hw_scan(struct i
                               struct cfg80211_scan_request *req)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 +      struct iwl_notification_wait wait_scan_done;
 +      static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
        int ret;
  
        if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
  
        mutex_lock(&mvm->mutex);
  
 -      if (mvm->scan_status == IWL_MVM_SCAN_NONE)
 -              ret = iwl_mvm_scan_request(mvm, vif, req);
 -      else
 +      switch (mvm->scan_status) {
 +      case IWL_MVM_SCAN_SCHED:
 +              iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
 +                                         scan_done_notif,
 +                                         ARRAY_SIZE(scan_done_notif),
 +                                         NULL, NULL);
 +              iwl_mvm_sched_scan_stop(mvm);
 +              ret = iwl_wait_notification(&mvm->notif_wait,
 +                                          &wait_scan_done, HZ);
 +              if (ret) {
 +                      ret = -EBUSY;
 +                      goto out;
 +              }
 +              /* iwl_mvm_rx_scan_offload_complete_notif() will be called
 +               * soon but will not reset the scan status as it won't be
 +               * IWL_MVM_SCAN_SCHED any more since we queue the next scan
 +               * immediately (below)
 +               */
 +              break;
 +      case IWL_MVM_SCAN_NONE:
 +              break;
 +      default:
                ret = -EBUSY;
 +              goto out;
 +      }
 +
 +      iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
  
 +      ret = iwl_mvm_scan_request(mvm, vif, req);
 +      if (ret)
 +              iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
 +out:
        mutex_unlock(&mvm->mutex);
  
        return ret;
@@@ -1478,32 -1168,20 +1478,32 @@@ static void iwl_mvm_mac_cancel_hw_scan(
  
  static void
  iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
 -                                struct ieee80211_sta *sta, u16 tid,
 +                                struct ieee80211_sta *sta, u16 tids,
                                  int num_frames,
                                  enum ieee80211_frame_release_type reason,
                                  bool more_data)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
  
 -      /* TODO: how do we tell the fw to send frames for a specific TID */
 +      /* Called when we need to transmit (a) frame(s) from mac80211 */
  
 -      /*
 -       * The fw will send EOSP notification when the last frame will be
 -       * transmitted.
 -       */
 -      iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames);
 +      iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
 +                                        tids, more_data, false);
 +}
 +
 +static void
 +iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw,
 +                                  struct ieee80211_sta *sta, u16 tids,
 +                                  int num_frames,
 +                                  enum ieee80211_frame_release_type reason,
 +                                  bool more_data)
 +{
 +      struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 +
 +      /* Called when we need to transmit (a) frame(s) from agg queue */
 +
 +      iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames,
 +                                        tids, more_data, true);
  }
  
  static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 +      int tid;
  
        switch (cmd) {
        case STA_NOTIFY_SLEEP:
                if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
                        ieee80211_sta_block_awake(hw, sta, true);
 +              spin_lock_bh(&mvmsta->lock);
 +              for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
 +                      struct iwl_mvm_tid_data *tid_data;
 +
 +                      tid_data = &mvmsta->tid_data[tid];
 +                      if (tid_data->state != IWL_AGG_ON &&
 +                          tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
 +                              continue;
 +                      if (iwl_mvm_tid_queued(tid_data) == 0)
 +                              continue;
 +                      ieee80211_sta_set_buffered(sta, tid, true);
 +              }
 +              spin_unlock_bh(&mvmsta->lock);
                /*
                 * The fw updates the STA to be asleep. Tx packets on the Tx
                 * queues to this station will not be transmitted. The fw will
@@@ -1622,12 -1286,12 +1622,12 @@@ static int iwl_mvm_mac_sta_state(struc
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
                /* enable beacon filtering */
 -              WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
 +              WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTHORIZED &&
                   new_state == IEEE80211_STA_ASSOC) {
                /* disable beacon filtering */
 -              WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif));
 +              WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC));
                ret = 0;
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH) {
@@@ -2092,7 -1756,7 +2092,7 @@@ static int iwl_mvm_assign_vif_chanctx(s
         * otherwise fw will complain.
         */
        mvm->bound_vif_cnt++;
 -      iwl_mvm_power_update_binding(mvm, vif, true);
 +      iwl_mvm_power_update_mac(mvm, vif);
  
        /* Setting the quota at this stage is only required for monitor
         * interfaces. For the other types, the bss_info changed flow
   out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
        mvm->bound_vif_cnt--;
 -      iwl_mvm_power_update_binding(mvm, vif, false);
 +      iwl_mvm_power_update_mac(mvm, vif);
   out_unlock:
        mutex_unlock(&mvm->mutex);
        if (ret)
@@@ -2143,7 -1807,7 +2143,7 @@@ static void iwl_mvm_unassign_vif_chanct
  
        iwl_mvm_binding_remove_vif(mvm, vif);
        mvm->bound_vif_cnt--;
 -      iwl_mvm_power_update_binding(mvm, vif, false);
 +      iwl_mvm_power_update_mac(mvm, vif);
  
  out_unlock:
        mvmvif->phy_ctxt = NULL;
@@@ -2210,9 -1874,8 +2210,9 @@@ static int __iwl_mvm_mac_testmode_cmd(s
                        return -EINVAL;
  
                if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
 -                      return iwl_mvm_enable_beacon_filter(mvm, vif);
 -              return iwl_mvm_disable_beacon_filter(mvm, vif);
 +                      return iwl_mvm_enable_beacon_filter(mvm, vif,
 +                                                          CMD_SYNC);
 +              return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
        }
  
        return -EOPNOTSUPP;
@@@ -2251,7 -1914,6 +2251,7 @@@ struct ieee80211_ops iwl_mvm_hw_ops = 
        .sta_state = iwl_mvm_mac_sta_state,
        .sta_notify = iwl_mvm_mac_sta_notify,
        .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
 +      .release_buffered_frames = iwl_mvm_mac_release_buffered_frames,
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
        .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
index bf4e773c6f468d6d51ab1f332295d4a265287d8a,742afc429c946c0c5defa31de5f7d720071d2485..eba55cc3352db3cb2997f39e8586b48ed63762ec
@@@ -344,7 -344,8 +344,8 @@@ int iwl_mvm_scan_request(struct iwl_mv
  
        iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
  
-       cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
+       cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                          TX_CMD_FLG_BT_DIS);
        cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
        cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
        cmd->tx_cmd.rate_n_flags =
@@@ -407,8 -408,6 +408,8 @@@ int iwl_mvm_rx_scan_complete(struct iwl
        mvm->scan_status = IWL_MVM_SCAN_NONE;
        ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
  
 +      iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
 +
        return 0;
  }
  
@@@ -477,7 -476,6 +478,7 @@@ void iwl_mvm_cancel_scan(struct iwl_mv
  
        if (iwl_mvm_is_radio_killed(mvm)) {
                ieee80211_scan_completed(mvm->hw, true);
 +              iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
                mvm->scan_status = IWL_MVM_SCAN_NONE;
                return;
        }
        ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
 -              /* mac80211's state will be cleaned in the fw_restart flow */
 +              /* mac80211's state will be cleaned in the nic_restart flow */
                goto out_remove_notif;
        }
  
@@@ -511,16 -509,11 +512,16 @@@ int iwl_mvm_rx_scan_offload_complete_no
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
  
 +      /* scan status must be locked for proper checking */
 +      lockdep_assert_held(&mvm->mutex);
 +
        IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
                       scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
                       "completed" : "aborted");
  
 -      mvm->scan_status = IWL_MVM_SCAN_NONE;
 +      /* might already be something else again, don't reset if so */
 +      if (mvm->scan_status == IWL_MVM_SCAN_SCHED)
 +              mvm->scan_status = IWL_MVM_SCAN_NONE;
        ieee80211_sched_scan_stopped(mvm->hw);
  
        return 0;
@@@ -603,9 -596,6 +604,9 @@@ static void iwl_scan_offload_build_ssid
         * config match list.
         */
        for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) {
 +              /* skip empty SSID matchsets */
 +              if (!req->match_sets[i].ssid.ssid_len)
 +                      continue;
                scan->direct_scan[i].id = WLAN_EID_SSID;
                scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
                memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
@@@ -818,6 -808,8 +819,8 @@@ int iwl_mvm_config_sched_scan_profiles(
        profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN;
        profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN;
        profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN;
+       if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len)
+               profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN;
  
        for (i = 0; i < req->n_match_sets; i++) {
                profile = &profile_cfg->profiles[i];
index d1da93b79cc6fb3e575278884f2241f2965a294c,3397f59cd4e4deb532be48e31c2906b45101f6a5..2677d1c0e1a1b69a46b533cb722d7f386974b8a2
  #include "sta.h"
  #include "rs.h"
  
 -static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6,
 +static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7,
                                         struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
  {
        memset(cmd_v5, 0, sizeof(*cmd_v5));
  
 -      cmd_v5->add_modify = cmd_v6->add_modify;
 -      cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx;
 -      cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color;
 -      memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN);
 -      cmd_v5->sta_id = cmd_v6->sta_id;
 -      cmd_v5->modify_mask = cmd_v6->modify_mask;
 -      cmd_v5->station_flags = cmd_v6->station_flags;
 -      cmd_v5->station_flags_msk = cmd_v6->station_flags_msk;
 -      cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid;
 -      cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid;
 -      cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn;
 -      cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count;
 -      cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags;
 -      cmd_v5->assoc_id = cmd_v6->assoc_id;
 -      cmd_v5->beamform_flags = cmd_v6->beamform_flags;
 -      cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk;
 +      cmd_v5->add_modify = cmd_v7->add_modify;
 +      cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx;
 +      cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color;
 +      memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN);
 +      cmd_v5->sta_id = cmd_v7->sta_id;
 +      cmd_v5->modify_mask = cmd_v7->modify_mask;
 +      cmd_v5->station_flags = cmd_v7->station_flags;
 +      cmd_v5->station_flags_msk = cmd_v7->station_flags_msk;
 +      cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid;
 +      cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid;
 +      cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn;
 +      cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count;
 +      cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags;
 +      cmd_v5->assoc_id = cmd_v7->assoc_id;
 +      cmd_v5->beamform_flags = cmd_v7->beamform_flags;
 +      cmd_v5->tfd_queue_msk = cmd_v7->tfd_queue_msk;
  }
  
  static void
@@@ -110,7 -110,7 +110,7 @@@ iwl_mvm_add_sta_key_to_add_sta_cmd_v5(s
  }
  
  static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm,
 -                                         struct iwl_mvm_add_sta_cmd_v6 *cmd,
 +                                         struct iwl_mvm_add_sta_cmd_v7 *cmd,
                                           int *status)
  {
        struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
                return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd),
                                                   cmd, status);
  
 -      iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
 +      iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
  
        return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5),
                                           &cmd_v5, status);
  }
  
  static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags,
 -                                  struct iwl_mvm_add_sta_cmd_v6 *cmd)
 +                                  struct iwl_mvm_add_sta_cmd_v7 *cmd)
  {
        struct iwl_mvm_add_sta_cmd_v5 cmd_v5;
  
                return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags,
                                            sizeof(*cmd), cmd);
  
 -      iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5);
 +      iwl_mvm_add_sta_cmd_v7_to_v5(cmd, &cmd_v5);
  
        return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5),
                                    &cmd_v5);
@@@ -175,30 -175,19 +175,30 @@@ static int iwl_mvm_send_add_sta_key_cmd
                                    &sta_cmd);
  }
  
 -static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
 +static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm,
 +                                  enum nl80211_iftype iftype)
  {
        int sta_id;
 +      u32 reserved_ids = 0;
  
 +      BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);
        WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
  
        lockdep_assert_held(&mvm->mutex);
  
 +      /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */
 +      if (iftype != NL80211_IFTYPE_STATION)
 +              reserved_ids = BIT(0);
 +
        /* Don't take rcu_read_lock() since we are protected by mvm->mutex */
 -      for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++)
 +      for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) {
 +              if (BIT(sta_id) & reserved_ids)
 +                      continue;
 +
                if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                               lockdep_is_held(&mvm->mutex)))
                        return sta_id;
 +      }
        return IWL_MVM_STATION_COUNT;
  }
  
@@@ -207,7 -196,7 +207,7 @@@ int iwl_mvm_sta_send_to_fw(struct iwl_m
                           bool update)
  {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 -      struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd;
 +      struct iwl_mvm_add_sta_cmd_v7 add_sta_cmd;
        int ret;
        u32 status;
        u32 agg_size = 0, mpdu_dens = 0;
@@@ -323,8 -312,7 +323,8 @@@ int iwl_mvm_add_sta(struct iwl_mvm *mvm
        lockdep_assert_held(&mvm->mutex);
  
        if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
 -              sta_id = iwl_mvm_find_free_sta_id(mvm);
 +              sta_id = iwl_mvm_find_free_sta_id(mvm,
 +                                                ieee80211_vif_type_p2p(vif));
        else
                sta_id = mvm_sta->sta_id;
  
@@@ -380,7 -368,7 +380,7 @@@ int iwl_mvm_update_sta(struct iwl_mvm *
  int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain)
  {
 -      struct iwl_mvm_add_sta_cmd_v6 cmd = {};
 +      struct iwl_mvm_add_sta_cmd_v7 cmd = {};
        int ret;
        u32 status;
  
@@@ -534,10 -522,6 +534,10 @@@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm
  
                /* unassoc - go ahead - remove the AP STA now */
                mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
 +
 +              /* clear d0i3_ap_sta_id if no longer relevant */
 +              if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id)
 +                      mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
        }
  
        /*
@@@ -576,10 -560,10 +576,10 @@@ int iwl_mvm_rm_sta_id(struct iwl_mvm *m
  }
  
  int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
 -                           u32 qmask)
 +                           u32 qmask, enum nl80211_iftype iftype)
  {
        if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
 -              sta->sta_id = iwl_mvm_find_free_sta_id(mvm);
 +              sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
                if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
                        return -ENOSPC;
        }
@@@ -603,13 -587,13 +603,13 @@@ static int iwl_mvm_add_int_sta_common(s
                                      const u8 *addr,
                                      u16 mac_id, u16 color)
  {
 -      struct iwl_mvm_add_sta_cmd_v6 cmd;
 +      struct iwl_mvm_add_sta_cmd_v7 cmd;
        int ret;
        u32 status;
  
        lockdep_assert_held(&mvm->mutex);
  
 -      memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6));
 +      memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7));
        cmd.sta_id = sta->sta_id;
        cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
                                                             color));
@@@ -643,8 -627,7 +643,8 @@@ int iwl_mvm_add_aux_sta(struct iwl_mvm 
        lockdep_assert_held(&mvm->mutex);
  
        /* Add the aux station, but without any queues */
 -      ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0);
 +      ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0,
 +                                     NL80211_IFTYPE_UNSPECIFIED);
        if (ret)
                return ret;
  
@@@ -669,7 -652,7 +669,7 @@@ int iwl_mvm_send_bcast_sta(struct iwl_m
  {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
-       static const u8 *baddr = _baddr;
+       const u8 *baddr = _baddr;
  
        lockdep_assert_held(&mvm->mutex);
  
@@@ -716,8 -699,7 +716,8 @@@ int iwl_mvm_add_bcast_sta(struct iwl_mv
        lockdep_assert_held(&mvm->mutex);
  
        qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
 -      ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask);
 +      ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask,
 +                                     ieee80211_vif_type_p2p(vif));
        if (ret)
                return ret;
  
@@@ -753,7 -735,7 +753,7 @@@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *
                       int tid, u16 ssn, bool start)
  {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 -      struct iwl_mvm_add_sta_cmd_v6 cmd = {};
 +      struct iwl_mvm_add_sta_cmd_v7 cmd = {};
        int ret;
        u32 status;
  
@@@ -812,7 -794,7 +812,7 @@@ static int iwl_mvm_sta_tx_agg(struct iw
                              int tid, u8 queue, bool start)
  {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 -      struct iwl_mvm_add_sta_cmd_v6 cmd = {};
 +      struct iwl_mvm_add_sta_cmd_v7 cmd = {};
        int ret;
        u32 status;
  
        return ret;
  }
  
 -static const u8 tid_to_ac[] = {
 +static const u8 tid_to_mac80211_ac[] = {
        IEEE80211_AC_BE,
        IEEE80211_AC_BK,
        IEEE80211_AC_BK,
        IEEE80211_AC_VO,
  };
  
 +static const u8 tid_to_ucode_ac[] = {
 +      AC_BE,
 +      AC_BK,
 +      AC_BK,
 +      AC_BE,
 +      AC_VI,
 +      AC_VI,
 +      AC_VO,
 +      AC_VO,
 +};
 +
  int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta, u16 tid, u16 *ssn)
  {
        }
  
        /* the new tx queue is still connected to the same mac80211 queue */
 -      mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]];
 +      mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]];
  
        spin_lock_bh(&mvmsta->lock);
        tid_data = &mvmsta->tid_data[tid];
@@@ -945,7 -916,7 +945,7 @@@ int iwl_mvm_sta_tx_agg_oper(struct iwl_
        tid_data->ssn = 0xffff;
        spin_unlock_bh(&mvmsta->lock);
  
 -      fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]];
 +      fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];
  
        ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
        if (ret)
@@@ -1440,7 -1411,7 +1440,7 @@@ void iwl_mvm_sta_modify_ps_wake(struct 
                                struct ieee80211_sta *sta)
  {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 -      struct iwl_mvm_add_sta_cmd_v6 cmd = {
 +      struct iwl_mvm_add_sta_cmd_v7 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .station_flags_msk = cpu_to_le32(STA_FLG_PS),
  void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                                       struct ieee80211_sta *sta,
                                       enum ieee80211_frame_release_type reason,
 -                                     u16 cnt)
 +                                     u16 cnt, u16 tids, bool more_data,
 +                                     bool agg)
  {
 -      u16 sleep_state_flags =
 -              (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
 -                      STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 -      struct iwl_mvm_add_sta_cmd_v6 cmd = {
 +      struct iwl_mvm_add_sta_cmd_v7 cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
                .sleep_tx_count = cpu_to_le16(cnt),
                .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
 -              /*
 -               * Same modify mask for sleep_tx_count and sleep_state_flags so
 -               * we must set the sleep_state_flags too.
 -               */
 -              .sleep_state_flags = cpu_to_le16(sleep_state_flags),
        };
 -      int ret;
 +      int tid, ret;
 +      unsigned long _tids = tids;
 +
 +      /* convert TIDs to ACs - we don't support TSPEC so that's OK
 +       * Note that this field is reserved and unused by firmware not
 +       * supporting GO uAPSD, so it's safe to always do this.
 +       */
 +      for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT)
 +              cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]);
 +
 +      /* If we're releasing frames from aggregation queues then check if the
 +       * all queues combined that we're releasing frames from have
 +       *  - more frames than the service period, in which case more_data
 +       *    needs to be set
 +       *  - fewer than 'cnt' frames, in which case we need to adjust the
 +       *    firmware command (but do that unconditionally)
 +       */
 +      if (agg) {
 +              int remaining = cnt;
 +
 +              spin_lock_bh(&mvmsta->lock);
 +              for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
 +                      struct iwl_mvm_tid_data *tid_data;
 +                      u16 n_queued;
 +
 +                      tid_data = &mvmsta->tid_data[tid];
 +                      if (WARN(tid_data->state != IWL_AGG_ON &&
 +                               tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA,
 +                               "TID %d state is %d\n",
 +                               tid, tid_data->state)) {
 +                              spin_unlock_bh(&mvmsta->lock);
 +                              ieee80211_sta_eosp(sta);
 +                              return;
 +                      }
 +
 +                      n_queued = iwl_mvm_tid_queued(tid_data);
 +                      if (n_queued > remaining) {
 +                              more_data = true;
 +                              remaining = 0;
 +                              break;
 +                      }
 +                      remaining -= n_queued;
 +              }
 +              spin_unlock_bh(&mvmsta->lock);
 +
 +              cmd.sleep_tx_count = cpu_to_le16(cnt - remaining);
 +              if (WARN_ON(cnt - remaining == 0)) {
 +                      ieee80211_sta_eosp(sta);
 +                      return;
 +              }
 +      }
 +
 +      /* Note: this is ignored by firmware not supporting GO uAPSD */
 +      if (more_data)
 +              cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA);
 +
 +      if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) {
 +              mvmsta->next_status_eosp = true;
 +              cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL);
 +      } else {
 +              cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
 +      }
  
 -      /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
        ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
  }
 +
 +int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
 +                        struct iwl_rx_cmd_buffer *rxb,
 +                        struct iwl_device_cmd *cmd)
 +{
 +      struct iwl_rx_packet *pkt = rxb_addr(rxb);
 +      struct iwl_mvm_eosp_notification *notif = (void *)pkt->data;
 +      struct ieee80211_sta *sta;
 +      u32 sta_id = le32_to_cpu(notif->sta_id);
 +
 +      if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT))
 +              return 0;
 +
 +      rcu_read_lock();
 +      sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
 +      if (!IS_ERR_OR_NULL(sta))
 +              ieee80211_sta_eosp(sta);
 +      rcu_read_unlock();
 +
 +      return 0;
 +}
index 8d18bf23e4bf74454be4d3757ef577d6a352c26c,4df12fa9d33685b285b489a67897479f8f8dc54a..74d60bf27750ae7d88dda39fbf473f971ddd8f79
@@@ -377,13 -377,6 +377,13 @@@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm
        tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
        /* From now on, we cannot access info->control */
  
 +      /*
 +       * we handle that entirely ourselves -- for uAPSD the firmware
 +       * will always send a notification, and for PS-Poll responses
 +       * we'll notify mac80211 when getting frame status
 +       */
 +      info->flags &= ~IEEE80211_TX_STATUS_EOSP;
 +
        spin_lock(&mvmsta->lock);
  
        if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
@@@ -444,17 -437,6 +444,17 @@@ static void iwl_mvm_check_ratid_empty(s
  
        lockdep_assert_held(&mvmsta->lock);
  
 +      if ((tid_data->state == IWL_AGG_ON ||
 +           tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) &&
 +          iwl_mvm_tid_queued(tid_data) == 0) {
 +              /*
 +               * Now that this aggregation queue is empty tell mac80211 so it
 +               * knows we no longer have frames buffered for the station on
 +               * this TID (for the TIM bitmap calculation.)
 +               */
 +              ieee80211_sta_set_buffered(sta, tid, false);
 +      }
 +
        if (tid_data->ssn != tid_data->next_reclaimed)
                return;
  
@@@ -677,8 -659,14 +677,14 @@@ static void iwl_mvm_rx_tx_cmd_single(st
        rcu_read_lock();
  
        sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+       /*
+        * sta can't be NULL otherwise it'd mean that the sta has been freed in
+        * the firmware while we still have packets for it in the Tx queues.
+        */
+       if (WARN_ON_ONCE(!sta))
+               goto out;
  
-       if (!IS_ERR_OR_NULL(sta)) {
+       if (!IS_ERR(sta)) {
                mvmsta = iwl_mvm_sta_from_mac80211(sta);
  
                if (tid != IWL_TID_NON_QOS) {
                        iwl_mvm_check_ratid_empty(mvm, sta, tid);
                        spin_unlock_bh(&mvmsta->lock);
                }
 +
 +              if (mvmsta->next_status_eosp) {
 +                      mvmsta->next_status_eosp = false;
 +                      ieee80211_sta_eosp(sta);
 +              }
        } else {
-               sta = NULL;
                mvmsta = NULL;
        }
  
         * If the txq is not an AMPDU queue, there is no chance we freed
         * several skbs. Check that out...
         */
-       if (txq_id < mvm->first_agg_queue && !WARN_ON(skb_freed > 1) &&
-           atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) {
-               if (mvmsta) {
-                       /*
-                        * If there are no pending frames for this STA, notify
-                        * mac80211 that this station can go to sleep in its
-                        * STA table.
-                        */
-                       if (mvmsta->vif->type == NL80211_IFTYPE_AP)
-                               ieee80211_sta_block_awake(mvm->hw, sta, false);
-                       /*
-                        * We might very well have taken mvmsta pointer while
-                        * the station was being removed. The remove flow might
-                        * have seen a pending_frame (because we didn't take
-                        * the lock) even if now the queues are drained. So make
-                        * really sure now that this the station is not being
-                        * removed. If it is, run the drain worker to remove it.
-                        */
-                       spin_lock_bh(&mvmsta->lock);
-                       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
-                       if (!sta || PTR_ERR(sta) == -EBUSY) {
-                               /*
-                                * Station disappeared in the meantime:
-                                * so we are draining.
-                                */
-                               set_bit(sta_id, mvm->sta_drained);
-                               schedule_work(&mvm->sta_drained_wk);
-                       }
-                       spin_unlock_bh(&mvmsta->lock);
-               } else if (!mvmsta && PTR_ERR(sta) == -EBUSY) {
-                       /* Tx response without STA, so we are draining */
-                       set_bit(sta_id, mvm->sta_drained);
-                       schedule_work(&mvm->sta_drained_wk);
-               }
+       if (txq_id >= mvm->first_agg_queue)
+               goto out;
+       /* We can't free more than one frame at once on a shared queue */
+       WARN_ON(skb_freed > 1);
+       /* If we have still frames from this STA nothing to do here */
+       if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
+               goto out;
+       if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
+               /*
+                * If there are no pending frames for this STA, notify
+                * mac80211 that this station can go to sleep in its
+                * STA table.
+                * If mvmsta is not NULL, sta is valid.
+                */
+               ieee80211_sta_block_awake(mvm->hw, sta, false);
+       }
+       if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
+               /*
+                * We are draining and this was the last packet - pre_rcu_remove
+                * has been called already. We might be after the
+                * synchronize_net already.
+                * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues.
+                */
+               set_bit(sta_id, mvm->sta_drained);
+               schedule_work(&mvm->sta_drained_wk);
        }
  
+ out:
        rcu_read_unlock();
  }
  
index f4598cb2dd2e8922f9c598b360d89c64351bf29e,86989df693566aa5b604be092a7910244cf04aee..1493f79e67e0fb0b00b490a800094c5859cdbffa
@@@ -376,67 -376,9 +376,67 @@@ struct iwl_error_event_table 
        u32 flow_handler;       /* FH read/write pointers, RX credit */
  } __packed;
  
 +/*
 + * UMAC error struct - relevant starting from family 8000 chip.
 + * Note: This structure is read from the device with IO accesses,
 + * and the reading already does the endian conversion. As it is
 + * read with u32-sized accesses, any members with a different size
 + * need to be ordered correctly though!
 + */
 +struct iwl_umac_error_event_table {
 +      u32 valid;              /* (nonzero) valid, (0) log is empty */
 +      u32 error_id;           /* type of error */
 +      u32 pc;                 /* program counter */
 +      u32 blink1;             /* branch link */
 +      u32 blink2;             /* branch link */
 +      u32 ilink1;             /* interrupt link */
 +      u32 ilink2;             /* interrupt link */
 +      u32 data1;              /* error-specific data */
 +      u32 data2;              /* error-specific data */
 +      u32 line;               /* source code line of error */
 +      u32 umac_ver;           /* umac version */
 +} __packed;
 +
  #define ERROR_START_OFFSET  (1 * sizeof(u32))
  #define ERROR_ELEM_SIZE     (7 * sizeof(u32))
  
 +static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
 +{
 +      struct iwl_trans *trans = mvm->trans;
 +      struct iwl_umac_error_event_table table;
 +      u32 base;
 +
 +      base = mvm->umac_error_event_table;
 +
 +      if (base < 0x800000 || base >= 0x80C000) {
 +              IWL_ERR(mvm,
 +                      "Not valid error log pointer 0x%08X for %s uCode\n",
 +                      base,
 +                      (mvm->cur_ucode == IWL_UCODE_INIT)
 +                                      ? "Init" : "RT");
 +              return;
 +      }
 +
 +      iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
 +
 +      if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
 +              IWL_ERR(trans, "Start IWL Error Log Dump:\n");
 +              IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
 +                      mvm->status, table.valid);
 +      }
 +
 +      IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
 +              desc_lookup(table.error_id));
 +      IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc);
 +      IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1);
 +      IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2);
 +      IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1);
 +      IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2);
 +      IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
 +      IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
 +      IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver);
 +}
 +
  void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
  {
        struct iwl_trans *trans = mvm->trans;
                        base = mvm->fw->inst_errlog_ptr;
        }
  
 -      if (base < 0x800000 || base >= 0x80C000) {
 +      if (base < 0x800000) {
                IWL_ERR(mvm,
                        "Not valid error log pointer 0x%08X for %s uCode\n",
                        base,
                        mvm->status, table.valid);
        }
  
+       IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
        trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
                                      table.data1, table.data2, table.data3,
                                      table.blink1, table.blink2, table.ilink1,
        IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
        IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
        IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
 +
 +      if (mvm->support_umac_log)
 +              iwl_mvm_dump_umac_error_log(mvm);
  }
  
  void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
  {
        const struct fw_img *img;
        int ofs, len = 0;
 -      u8 *buf;
 +      int i;
 +      __le32 *buf;
  
        if (!mvm->ucode_loaded)
                return;
                return;
  
        iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
 -      iwl_print_hex_error(mvm->trans, buf, len);
 +      len = len >> 2;
 +      for (i = 0; i < len; i++) {
 +              IWL_ERR(mvm, "0x%08X\n", le32_to_cpu(buf[i]));
 +              /* Add a small delay to let syslog catch up */
 +              udelay(10);
 +      }
  
        kfree(buf);
  }
@@@ -581,7 -516,7 +583,7 @@@ void iwl_mvm_update_smps(struct iwl_mv
                         enum ieee80211_smps_mode smps_request)
  {
        struct iwl_mvm_vif *mvmvif;
 -      enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
 +      enum ieee80211_smps_mode smps_mode;
        int i;
  
        lockdep_assert_held(&mvm->mutex);
        if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
                return;
  
 +      if (vif->type == NL80211_IFTYPE_AP)
 +              smps_mode = IEEE80211_SMPS_OFF;
 +      else
 +              smps_mode = IEEE80211_SMPS_AUTOMATIC;
 +
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
        mvmvif->smps_requests[req_type] = smps_request;
        for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
  
        ieee80211_request_smps(vif, smps_mode);
  }
 +
 +int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 +                             bool value)
 +{
 +      struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 +      int res;
 +
 +      lockdep_assert_held(&mvm->mutex);
 +
 +      mvmvif->low_latency = value;
 +
 +      res = iwl_mvm_update_quotas(mvm, NULL);
 +      if (res)
 +              return res;
 +
 +      iwl_mvm_bt_coex_vif_change(mvm);
 +
 +      return iwl_mvm_power_update_mac(mvm, vif);
 +}
index 85779390c4448aeebdb89189bddee9ce2d508d06,f47bcbe2945aabf35702cd0319ba322bf073aef7..0f52e961a5a514f069e80b139a3e5df484b5e76e
@@@ -66,7 -66,6 +66,7 @@@
  #include <linux/module.h>
  #include <linux/pci.h>
  #include <linux/pci-aspm.h>
 +#include <linux/acpi.h>
  
  #include "iwl-trans.h"
  #include "iwl-drv.h"
@@@ -360,20 -359,25 +360,25 @@@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_c
  /* 7265 Series */
        {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x5112, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x510A, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)},
 +
 +/* 8000 Series */
 +      {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
 +      {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
  #endif /* CONFIG_IWLMVM */
  
        {0}
  };
  MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
  
 +#ifdef CONFIG_ACPI
 +#define SPL_METHOD            "SPLC"
 +#define SPL_DOMAINTYPE_MODULE BIT(0)
 +#define SPL_DOMAINTYPE_WIFI   BIT(1)
 +#define SPL_DOMAINTYPE_WIGIG  BIT(2)
 +#define SPL_DOMAINTYPE_RFEM   BIT(3)
 +
 +static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
 +{
 +      union acpi_object *limits, *domain_type, *power_limit;
 +
 +      if (splx->type != ACPI_TYPE_PACKAGE ||
 +          splx->package.count != 2 ||
 +          splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
 +          splx->package.elements[0].integer.value != 0) {
 +              IWL_ERR(trans, "Unsupported splx structure");
 +              return 0;
 +      }
 +
 +      limits = &splx->package.elements[1];
 +      if (limits->type != ACPI_TYPE_PACKAGE ||
 +          limits->package.count < 2 ||
 +          limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
 +          limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
 +              IWL_ERR(trans, "Invalid limits element");
 +              return 0;
 +      }
 +
 +      domain_type = &limits->package.elements[0];
 +      power_limit = &limits->package.elements[1];
 +      if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
 +              IWL_DEBUG_INFO(trans, "WiFi power is not limited");
 +              return 0;
 +      }
 +
 +      return power_limit->integer.value;
 +}
 +
 +static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
 +{
 +      acpi_handle pxsx_handle;
 +      acpi_handle handle;
 +      struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
 +      acpi_status status;
 +
 +      pxsx_handle = ACPI_HANDLE(&pdev->dev);
 +      if (!pxsx_handle) {
 +              IWL_ERR(trans, "Could not retrieve root port ACPI handle");
 +              return;
 +      }
 +
 +      /* Get the method's handle */
 +      status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
 +      if (ACPI_FAILURE(status)) {
 +              IWL_DEBUG_INFO(trans, "SPL method not found");
 +              return;
 +      }
 +
 +      /* Call SPLC with no arguments */
 +      status = acpi_evaluate_object(handle, NULL, NULL, &splx);
 +      if (ACPI_FAILURE(status)) {
 +              IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
 +              return;
 +      }
 +
 +      trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
 +      IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
 +                     trans->dflt_pwr_limit);
 +      kfree(splx.pointer);
 +}
 +
 +#else /* CONFIG_ACPI */
 +static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {}
 +#endif
 +
  /* PCI registers */
  #define PCI_CFG_RETRY_TIMEOUT 0x041
  
@@@ -495,8 -420,6 +500,8 @@@ static int iwl_pci_probe(struct pci_de
                goto out_free_trans;
        }
  
 +      set_dflt_pwr_limit(iwl_trans, pdev);
 +
        /* register transport layer debugfs here */
        ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
        if (ret)
index 9bc843e9f4b49156acc5cec17e943918e0e4627c,3867d1470b36aef5664bcab74e85f38702c8315d..7980ab1f9eca3032a7af8b6c4993798895640c66
@@@ -107,6 -107,7 +107,7 @@@ static void rtl8180_handle_rx(struct ie
        struct rtl8180_priv *priv = dev->priv;
        unsigned int count = 32;
        u8 signal, agc, sq;
+       dma_addr_t mapping;
  
        while (count--) {
                struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
                        if (unlikely(!new_skb))
                                goto done;
  
+                       mapping = pci_map_single(priv->pdev,
+                                              skb_tail_pointer(new_skb),
+                                              MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+                       if (pci_dma_mapping_error(priv->pdev, mapping)) {
+                               kfree_skb(new_skb);
+                               dev_err(&priv->pdev->dev, "RX DMA map error\n");
+                               goto done;
+                       }
                        pci_unmap_single(priv->pdev,
                                         *((dma_addr_t *)skb->cb),
                                         MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
  
                        skb = new_skb;
                        priv->rx_buf[priv->rx_idx] = skb;
-                       *((dma_addr_t *) skb->cb) =
-                               pci_map_single(priv->pdev, skb_tail_pointer(skb),
-                                              MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+                       *((dma_addr_t *) skb->cb) = mapping;
                }
  
        done:
@@@ -266,6 -276,13 +276,13 @@@ static void rtl8180_tx(struct ieee80211
        mapping = pci_map_single(priv->pdev, skb->data,
                                 skb->len, PCI_DMA_TODEVICE);
  
+       if (pci_dma_mapping_error(priv->pdev, mapping)) {
+               kfree_skb(skb);
+               dev_err(&priv->pdev->dev, "TX DMA mapping error\n");
+               return;
+       }
        tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS |
                   RTL818X_TX_DESC_FLAG_LS |
                   (ieee80211_get_tx_rate(dev, info)->hw_value << 24) |
@@@ -602,13 -619,13 +619,13 @@@ static int rtl8180_start(struct ieee802
  
        if (priv->r8185) {
                reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
 -              reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
 -              reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
 +              reg &= ~RTL818X_CW_CONF_PERPACKET_CW;
 +              reg |= RTL818X_CW_CONF_PERPACKET_RETRY;
                rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
  
                reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
 -              reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
 -              reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
 +              reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN;
 +              reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL;
                reg |=  RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
                rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
  
diff --combined net/mac80211/cfg.c
index 6973ccdd230b88942ba71ceae911fb4d58424279,453e974287d19b52971116bca5e0e401746f3ffb..363d19b5d5c8d4bb88b75d537b69b7bef68f768e
@@@ -451,11 -451,11 +451,11 @@@ void sta_set_rate_info_rx(struct sta_in
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
        if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
 -      if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
 +      if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
 -      if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
 +      if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
 -      if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
 +      if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
  }
  
@@@ -970,9 -970,9 +970,9 @@@ static int ieee80211_start_ap(struct wi
        /* TODO: make hostapd tell us what it wants */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = sdata->local->rx_chains;
 -      sdata->radar_required = params->radar_required;
  
        mutex_lock(&local->mtx);
 +      sdata->radar_required = params->radar_required;
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
        mutex_unlock(&local->mtx);
                                        IEEE80211_P2P_OPPPS_ENABLE_BIT;
  
        err = ieee80211_assign_beacon(sdata, &params->beacon);
-       if (err < 0)
+       if (err < 0) {
+               ieee80211_vif_release_channel(sdata);
                return err;
+       }
        changed |= err;
  
        err = drv_start_ap(sdata->local, sdata);
                if (old)
                        kfree_rcu(old, rcu_head);
                RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+               ieee80211_vif_release_channel(sdata);
                return err;
        }
  
@@@ -1053,7 -1056,6 +1056,7 @@@ static int ieee80211_change_beacon(stru
        int err;
  
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 +      sdata_assert_lock(sdata);
  
        /* don't allow changing the beacon while CSA is in place - offset
         * of channel switch counter may change
@@@ -1081,8 -1083,6 +1084,8 @@@ static int ieee80211_stop_ap(struct wip
        struct probe_resp *old_probe_resp;
        struct cfg80211_chan_def chandef;
  
 +      sdata_assert_lock(sdata);
 +
        old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (!old_beacon)
                return -ENOENT;
        kfree(sdata->u.ap.next_beacon);
        sdata->u.ap.next_beacon = NULL;
  
-       cancel_work_sync(&sdata->u.ap.request_smps_work);
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                netif_carrier_off(vlan->dev);
        kfree_rcu(old_beacon, rcu_head);
        if (old_probe_resp)
                kfree_rcu(old_probe_resp, rcu_head);
+       sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF;
  
        __sta_info_flush(sdata, true);
        ieee80211_free_keys(sdata, true);
@@@ -1344,18 -1343,6 +1346,18 @@@ static int sta_apply_parameters(struct 
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    params->vht_capa, sta);
  
 +      if (params->opmode_notif_used) {
 +              enum ieee80211_band band =
 +                      ieee80211_get_sdata_band(sdata);
 +
 +              /* returned value is only needed for rc update, but the
 +               * rc isn't initialized here yet, so ignore it
 +               */
 +              __ieee80211_vht_handle_opmode(sdata, sta,
 +                                            params->opmode_notif,
 +                                            band, false);
 +      }
 +
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
  #ifdef CONFIG_MAC80211_MESH
                u32 changed = 0;
@@@ -2643,18 -2630,6 +2645,18 @@@ static int ieee80211_start_roc_work(str
        if (!roc)
                return -ENOMEM;
  
 +      /*
 +       * If the duration is zero, then the driver
 +       * wouldn't actually do anything. Set it to
 +       * 10 for now.
 +       *
 +       * TODO: cancel the off-channel operation
 +       *       when we get the SKB's TX status and
 +       *       the wait time was zero before.
 +       */
 +      if (!duration)
 +              duration = 10;
 +
        roc->chan = channel;
        roc->duration = duration;
        roc->req_duration = duration;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
  
+       /*
+        * cookie is either the roc cookie (for normal roc)
+        * or the SKB (for mgmt TX)
+        */
+       if (!txskb) {
+               /* local->mtx protects this */
+               local->roc_cookie_counter++;
+               roc->cookie = local->roc_cookie_counter;
+               /* wow, you wrapped 64 bits ... more likely a bug */
+               if (WARN_ON(roc->cookie == 0)) {
+                       roc->cookie = 1;
+                       local->roc_cookie_counter++;
+               }
+               *cookie = roc->cookie;
+       } else {
+               *cookie = (unsigned long)txskb;
+       }
        /* if there's one pending or we're scanning, queue this one */
        if (!list_empty(&local->roc_list) ||
            local->scanning || local->radar_detect_enabled)
  
        /* otherwise actually kick it off here (for error handling) */
  
 -      /*
 -       * If the duration is zero, then the driver
 -       * wouldn't actually do anything. Set it to
 -       * 10 for now.
 -       *
 -       * TODO: cancel the off-channel operation
 -       *       when we get the SKB's TX status and
 -       *       the wait time was zero before.
 -       */
 -      if (!duration)
 -              duration = 10;
 -
        ret = drv_remain_on_channel(local, sdata, channel, duration, type);
        if (ret) {
                kfree(roc);
        if (!queued)
                list_add_tail(&roc->list, &local->roc_list);
  
-       /*
-        * cookie is either the roc cookie (for normal roc)
-        * or the SKB (for mgmt TX)
-        */
-       if (!txskb) {
-               /* local->mtx protects this */
-               local->roc_cookie_counter++;
-               roc->cookie = local->roc_cookie_counter;
-               /* wow, you wrapped 64 bits ... more likely a bug */
-               if (WARN_ON(roc->cookie == 0)) {
-                       roc->cookie = 1;
-                       local->roc_cookie_counter++;
-               }
-               *cookie = roc->cookie;
-       } else {
-               *cookie = (unsigned long)txskb;
-       }
        return 0;
  }
  
@@@ -3003,88 -2990,69 +3005,88 @@@ cfg80211_beacon_dup(struct cfg80211_bea
        return new_beacon;
  }
  
 -void ieee80211_csa_finalize_work(struct work_struct *work)
 +void ieee80211_csa_finish(struct ieee80211_vif *vif)
 +{
 +      struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 +
 +      ieee80211_queue_work(&sdata->local->hw,
 +                           &sdata->csa_finalize_work);
 +}
 +EXPORT_SYMBOL(ieee80211_csa_finish);
 +
 +static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
  {
 -      struct ieee80211_sub_if_data *sdata =
 -              container_of(work, struct ieee80211_sub_if_data,
 -                           csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
        int err, changed = 0;
  
 -      sdata_lock(sdata);
 -      /* AP might have been stopped while waiting for the lock. */
 -      if (!sdata->vif.csa_active)
 -              goto unlock;
 -
 -      if (!ieee80211_sdata_running(sdata))
 -              goto unlock;
 +      sdata_assert_lock(sdata);
  
 -      sdata->radar_required = sdata->csa_radar_required;
        mutex_lock(&local->mtx);
 +      sdata->radar_required = sdata->csa_radar_required;
        err = ieee80211_vif_change_channel(sdata, &changed);
        mutex_unlock(&local->mtx);
        if (WARN_ON(err < 0))
 -              goto unlock;
 +              return;
  
        if (!local->use_chanctx) {
                local->_oper_chandef = sdata->csa_chandef;
                ieee80211_hw_config(local, 0);
        }
  
 -      ieee80211_bss_info_change_notify(sdata, changed);
 -
        sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
 -              if (err < 0)
 -                      goto unlock;
 -
 -              changed |= err;
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
  
 -              ieee80211_bss_info_change_notify(sdata, err);
 +              if (err < 0)
 +                      return;
 +              changed |= err;
                break;
        case NL80211_IFTYPE_ADHOC:
 -              ieee80211_ibss_finish_csa(sdata);
 +              err = ieee80211_ibss_finish_csa(sdata);
 +              if (err < 0)
 +                      return;
 +              changed |= err;
                break;
  #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
 -                      goto unlock;
 +                      return;
 +              changed |= err;
                break;
  #endif
        default:
                WARN_ON(1);
 -              goto unlock;
 +              return;
        }
  
 +      ieee80211_bss_info_change_notify(sdata, changed);
 +
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
  
        cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
 +}
 +
 +void ieee80211_csa_finalize_work(struct work_struct *work)
 +{
 +      struct ieee80211_sub_if_data *sdata =
 +              container_of(work, struct ieee80211_sub_if_data,
 +                           csa_finalize_work);
 +
 +      sdata_lock(sdata);
 +      /* AP might have been stopped while waiting for the lock. */
 +      if (!sdata->vif.csa_active)
 +              goto unlock;
 +
 +      if (!ieee80211_sdata_running(sdata))
 +              goto unlock;
 +
 +      ieee80211_csa_finalize(sdata);
  
  unlock:
        sdata_unlock(sdata);
@@@ -3098,9 -3066,9 +3100,9 @@@ int ieee80211_channel_switch(struct wip
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
        struct ieee80211_if_mesh __maybe_unused *ifmsh;
 -      int err, num_chanctx;
 +      int err, num_chanctx, changed = 0;
  
 -      lockdep_assert_held(&sdata->wdev.mtx);
 +      sdata_assert_lock(sdata);
  
        if (!list_empty(&local->roc_list) || local->scanning)
                return -EBUSY;
  
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
 -              sdata->csa_counter_offset_beacon =
 -                      params->counter_offset_beacon;
 -              sdata->csa_counter_offset_presp = params->counter_offset_presp;
                sdata->u.ap.next_beacon =
                        cfg80211_beacon_dup(&params->beacon_after);
                if (!sdata->u.ap.next_beacon)
                        return -ENOMEM;
  
 +              /*
 +               * With a count of 0, we don't have to wait for any
 +               * TBTT before switching, so complete the CSA
 +               * immediately.  In theory, with a count == 1 we
 +               * should delay the switch until just before the next
 +               * TBTT, but that would complicate things so we switch
 +               * immediately too.  If we would delay the switch
 +               * until the next TBTT, we would have to set the probe
 +               * response here.
 +               *
 +               * TODO: A channel switch with count <= 1 without
 +               * sending a CSA action frame is kind of useless,
 +               * because the clients won't know we're changing
 +               * channels.  The action frame must be implemented
 +               * either here or in the userspace.
 +               */
 +              if (params->count <= 1)
 +                      break;
 +
 +              sdata->csa_counter_offset_beacon =
 +                      params->counter_offset_beacon;
 +              sdata->csa_counter_offset_presp = params->counter_offset_presp;
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
                }
 +              changed |= err;
 +
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!sdata->vif.bss_conf.ibss_joined)
                    params->chandef.chan->band)
                        return -EINVAL;
  
 -              err = ieee80211_ibss_csa_beacon(sdata, params);
 -              if (err < 0)
 -                      return err;
 +              /* see comments in the NL80211_IFTYPE_AP block */
 +              if (params->count > 1) {
 +                      err = ieee80211_ibss_csa_beacon(sdata, params);
 +                      if (err < 0)
 +                              return err;
 +                      changed |= err;
 +              }
 +
 +              ieee80211_send_action_csa(sdata, params);
 +
                break;
  #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                ifmsh = &sdata->u.mesh;
  
 -              if (!ifmsh->mesh_id)
 -                      return -EINVAL;
 -
                if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
                        return -EINVAL;
  
                    params->chandef.chan->band)
                        return -EINVAL;
  
 -              ifmsh->chsw_init = true;
 -              if (!ifmsh->pre_value)
 -                      ifmsh->pre_value = 1;
 -              else
 -                      ifmsh->pre_value++;
 +              if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
 +                      ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
 +                      if (!ifmsh->pre_value)
 +                              ifmsh->pre_value = 1;
 +                      else
 +                              ifmsh->pre_value++;
 +              }
  
 -              err = ieee80211_mesh_csa_beacon(sdata, params, true);
 -              if (err < 0) {
 -                      ifmsh->chsw_init = false;
 -                      return err;
 +              /* see comments in the NL80211_IFTYPE_AP block */
 +              if (params->count > 1) {
 +                      err = ieee80211_mesh_csa_beacon(sdata, params);
 +                      if (err < 0) {
 +                              ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
 +                              return err;
 +                      }
 +                      changed |= err;
                }
 +
 +              if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
 +                      ieee80211_send_action_csa(sdata, params);
 +
                break;
  #endif
        default:
        sdata->csa_chandef = params->chandef;
        sdata->vif.csa_active = true;
  
 -      ieee80211_bss_info_change_notify(sdata, err);
 -      drv_channel_switch_beacon(sdata, &params->chandef);
 +      if (changed) {
 +              ieee80211_bss_info_change_notify(sdata, changed);
 +              drv_channel_switch_beacon(sdata, &params->chandef);
 +      } else {
 +              /* if the beacon didn't change, we can finalize immediately */
 +              ieee80211_csa_finalize(sdata);
 +      }
  
        return 0;
  }
@@@ -3937,7 -3865,7 +3939,7 @@@ static int ieee80211_set_qos_map(struc
        return 0;
  }
  
 -struct cfg80211_ops mac80211_config_ops = {
 +const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
        .change_virtual_intf = ieee80211_change_iface,
diff --combined net/mac80211/ht.c
index dc3c28002e3e2d99ee20f6d3809fe594d3796430,70dd013de8361e39c95264eeae967e193fe240e5..afbe2b203c3ea2bb3bb8694107f34892c7f34adc
@@@ -375,7 -375,7 +375,7 @@@ void ieee80211_send_delba(struct ieee80
        mgmt->u.action.u.delba.params = cpu_to_le16(params);
        mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
  
 -      ieee80211_tx_skb_tid(sdata, skb, tid);
 +      ieee80211_tx_skb(sdata, skb);
  }
  
  void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@@@ -466,7 -466,9 +466,9 @@@ void ieee80211_request_smps_ap_work(str
                             u.ap.request_smps_work);
  
        sdata_lock(sdata);
-       __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
+       if (sdata_dereference(sdata->u.ap.beacon, sdata))
+               __ieee80211_request_smps_ap(sdata,
+                                           sdata->u.ap.driver_smps_mode);
        sdata_unlock(sdata);
  }
  
diff --combined net/mac80211/ibss.c
index 9c84b75f3de8a58dc4f4e3cbf8a0c0c72be63ab3,2796a198728fd12bab4625ae1b112123988794f0..4453e2725e407b2650376f1682d183376341e99d
@@@ -220,6 -220,7 +220,6 @@@ static void __ieee80211_sta_join_ibss(s
  {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
 -      struct ieee80211_supported_band *sband;
        struct ieee80211_mgmt *mgmt;
        struct cfg80211_bss *bss;
        u32 bss_change;
        }
  
        mutex_lock(&local->mtx);
 -      ieee80211_vif_release_channel(sdata);
        if (ieee80211_vif_use_channel(sdata, &chandef,
                                      ifibss->fixed_channel ?
                                        IEEE80211_CHANCTX_SHARED :
                mutex_unlock(&local->mtx);
                return;
        }
 +      sdata->radar_required = radar_required;
        mutex_unlock(&local->mtx);
  
        memcpy(ifibss->bssid, bssid, ETH_ALEN);
  
 -      sband = local->hw.wiphy->bands[chan->band];
 -
        presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
                                           capability, tsf, &chandef,
                                           &have_higher_than_11mbit, NULL);
        rcu_assign_pointer(ifibss->presp, presp);
        mgmt = (void *)presp->head;
  
 -      sdata->radar_required = radar_required;
        sdata->vif.bss_conf.enable_beacon = true;
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
                                              presp->head_len, 0, GFP_KERNEL);
        cfg80211_put_bss(local->hw.wiphy, bss);
        netif_carrier_on(sdata->dev);
 -      cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
 +      cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL);
  }
  
  static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
@@@ -517,6 -521,12 +517,6 @@@ int ieee80211_ibss_csa_beacon(struct ie
        if (old_presp)
                kfree_rcu(old_presp, rcu_head);
  
 -      /* it might not send the beacon for a while. send an action frame
 -       * immediately to announce the channel switch.
 -       */
 -      if (csa_settings)
 -              ieee80211_send_action_csa(sdata, csa_settings);
 -
        return BSS_CHANGED_BEACON;
   out:
        return ret;
@@@ -526,7 -536,7 +526,7 @@@ int ieee80211_ibss_finish_csa(struct ie
  {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct cfg80211_bss *cbss;
 -      int err;
 +      int err, changed = 0;
        u16 capability;
  
        sdata_assert_lock(sdata);
        if (err < 0)
                return err;
  
 -      if (err)
 -              ieee80211_bss_info_change_notify(sdata, err);
 +      changed |= err;
  
 -      return 0;
 +      return changed;
  }
  
  void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
@@@ -684,12 -695,9 +684,9 @@@ static void ieee80211_ibss_disconnect(s
        struct cfg80211_bss *cbss;
        struct beacon_data *presp;
        struct sta_info *sta;
-       int active_ibss;
        u16 capability;
  
-       active_ibss = ieee80211_sta_active_ibss(sdata);
-       if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
+       if (!is_zero_ether_addr(ifibss->bssid)) {
                capability = WLAN_CAPABILITY_IBSS;
  
                if (ifibss->privacy)
@@@ -791,8 -799,6 +788,8 @@@ ieee80211_ibss_process_chanswitch(struc
        int err;
        u32 sta_flags;
  
 +      sdata_assert_lock(sdata);
 +
        sta_flags = IEEE80211_STA_DISABLE_VHT;
        switch (ifibss->chandef.width) {
        case NL80211_CHAN_WIDTH_5:
@@@ -1462,11 -1468,6 +1459,11 @@@ static void ieee80211_rx_mgmt_probe_req
        memcpy(((struct ieee80211_mgmt *) skb->data)->da, mgmt->sa, ETH_ALEN);
        ibss_dbg(sdata, "Sending ProbeResp to %pM\n", mgmt->sa);
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
 +
 +      /* avoid excessive retries for probe request to wildcard SSIDs */
 +      if (pos[1] == 0)
 +              IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NO_ACK;
 +
        ieee80211_tx_skb(sdata, skb);
  }
  
diff --combined net/mac80211/iface.c
index 8880bc8fce0dc9d20a118efd9667218ea62f4b1e,d6d1f1df9119acf15d15f0f2571be04ae8e74cae..96518ad200c4dc47a74ecd8284981c67872351e4
@@@ -418,20 -418,24 +418,24 @@@ int ieee80211_add_virtual_monitor(struc
                return ret;
        }
  
+       mutex_lock(&local->iflist_mtx);
+       rcu_assign_pointer(local->monitor_sdata, sdata);
+       mutex_unlock(&local->iflist_mtx);
        mutex_lock(&local->mtx);
        ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
                                        IEEE80211_CHANCTX_EXCLUSIVE);
        mutex_unlock(&local->mtx);
        if (ret) {
+               mutex_lock(&local->iflist_mtx);
+               rcu_assign_pointer(local->monitor_sdata, NULL);
+               mutex_unlock(&local->iflist_mtx);
+               synchronize_net();
                drv_remove_interface(local, sdata);
                kfree(sdata);
                return ret;
        }
  
-       mutex_lock(&local->iflist_mtx);
-       rcu_assign_pointer(local->monitor_sdata, sdata);
-       mutex_unlock(&local->iflist_mtx);
        return 0;
  }
  
@@@ -770,12 -774,19 +774,19 @@@ static void ieee80211_do_stop(struct ie
  
        ieee80211_roc_purge(local, sdata);
  
-       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
                ieee80211_mgd_stop(sdata);
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               break;
+       case NL80211_IFTYPE_ADHOC:
                ieee80211_ibss_stop(sdata);
+               break;
+       case NL80211_IFTYPE_AP:
+               cancel_work_sync(&sdata->u.ap.request_smps_work);
+               break;
+       default:
+               break;
+       }
  
        /*
         * Remove all stations associated with this interface.
        cancel_work_sync(&local->dynamic_ps_enable_work);
  
        cancel_work_sync(&sdata->recalc_smps);
 +      sdata_lock(sdata);
        sdata->vif.csa_active = false;
 +      sdata_unlock(sdata);
        cancel_work_sync(&sdata->csa_finalize_work);
  
        cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
diff --combined net/mac80211/tx.c
index 5476a69b45c9244123dd21dbd60882b3f98b6e70,97a02d3f7d87720795e1518d27fce0bbaed9bc4f..722151fa5dced6aaccee5fe8dab0986d2a78bde2
@@@ -452,7 -452,8 +452,7 @@@ static int ieee80211_use_mfp(__le16 fc
        if (sta == NULL || !test_sta_flag(sta, WLAN_STA_MFP))
                return 0;
  
 -      if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *)
 -                                          skb->data))
 +      if (!ieee80211_is_robust_mgmt_frame(skb))
                return 0;
  
        return 1;
@@@ -522,8 -523,11 +522,8 @@@ ieee80211_tx_h_ps_buf(struct ieee80211_
        if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
                return TX_CONTINUE;
  
 -      /* only deauth, disassoc and action are bufferable MMPDUs */
        if (ieee80211_is_mgmt(hdr->frame_control) &&
 -          !ieee80211_is_deauth(hdr->frame_control) &&
 -          !ieee80211_is_disassoc(hdr->frame_control) &&
 -          !ieee80211_is_action(hdr->frame_control)) {
 +          !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
                if (tx->flags & IEEE80211_TX_UNICAST)
                        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
                return TX_CONTINUE;
@@@ -563,7 -567,7 +563,7 @@@ ieee80211_tx_h_select_key(struct ieee80
                tx->key = key;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
                 is_multicast_ether_addr(hdr->addr1) &&
 -               ieee80211_is_robust_mgmt_frame(hdr) &&
 +               ieee80211_is_robust_mgmt_frame(tx->skb) &&
                 (key = rcu_dereference(tx->sdata->default_mgmt_key)))
                tx->key = key;
        else if (is_multicast_ether_addr(hdr->addr1) &&
                tx->key = NULL;
        else if (tx->skb->protocol == tx->sdata->control_port_protocol)
                tx->key = NULL;
 -      else if (ieee80211_is_robust_mgmt_frame(hdr) &&
 +      else if (ieee80211_is_robust_mgmt_frame(tx->skb) &&
                 !(ieee80211_is_action(hdr->frame_control) &&
                   tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP)))
                tx->key = NULL;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
 -               !ieee80211_is_robust_mgmt_frame(hdr))
 +               !ieee80211_is_robust_mgmt_frame(tx->skb))
                tx->key = NULL;
        else {
                I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
@@@ -874,7 -878,7 +874,7 @@@ static int ieee80211_fragment(struct ie
        }
  
        /* adjust first fragment's length */
-       skb->len = hdrlen + per_fragm;
+       skb_trim(skb, hdrlen + per_fragm);
        return 0;
  }
  
@@@ -2398,6 -2402,15 +2398,6 @@@ static int ieee80211_beacon_add_tim(str
        return 0;
  }
  
 -void ieee80211_csa_finish(struct ieee80211_vif *vif)
 -{
 -      struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 -
 -      ieee80211_queue_work(&sdata->local->hw,
 -                           &sdata->csa_finalize_work);
 -}
 -EXPORT_SYMBOL(ieee80211_csa_finish);
 -
  static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
                                 struct beacon_data *beacon)
  {
        if (WARN_ON(counter_offset_beacon >= beacon_data_len))
                return;
  
 -      /* warn if the driver did not check for/react to csa completeness */
 -      if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
 +      /* Warn if the driver did not check for/react to csa
 +       * completeness.  A beacon with CSA counter set to 0 should
 +       * never occur, because a counter of 1 means switch just
 +       * before the next beacon.
 +       */
 +      if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
                return;
  
        beacon_data[counter_offset_beacon]--;
@@@ -2497,7 -2506,7 +2497,7 @@@ bool ieee80211_csa_is_complete(struct i
        if (WARN_ON(counter_beacon > beacon_data_len))
                goto out;
  
 -      if (beacon_data[counter_beacon] == 0)
 +      if (beacon_data[counter_beacon] == 1)
                ret = true;
   out:
        rcu_read_unlock();
diff --combined net/wireless/core.c
index b5ff39a6f6ed9949a828b2391ad5bdf284ab54b9,010892b81a06642a3886c7df9b74f0af9ecb1d3c..76ae6a605abb3366ab757bda121606d1b4747f10
@@@ -203,8 -203,11 +203,11 @@@ void cfg80211_stop_p2p_device(struct cf
  
        rdev->opencount--;
  
-       WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
-               !rdev->scan_req->notified);
+       if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+               if (WARN_ON(!rdev->scan_req->notified))
+                       rdev->scan_req->aborted = true;
+               ___cfg80211_scan_done(rdev, false);
+       }
  }
  
  static int cfg80211_rfkill_set_block(void *data, bool blocked)
@@@ -440,9 -443,6 +443,6 @@@ int wiphy_register(struct wiphy *wiphy
        int i;
        u16 ifmodes = wiphy->interface_modes;
  
-       /* support for 5/10 MHz is broken due to nl80211 API mess - disable */
-       wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_5_10_MHZ;
        /*
         * There are major locking problems in nl80211/mac80211 for CSA,
         * disable for all drivers until this has been reworked.
@@@ -737,7 -737,7 +737,7 @@@ void cfg80211_unregister_wdev(struct wi
  }
  EXPORT_SYMBOL(cfg80211_unregister_wdev);
  
 -static struct device_type wiphy_type = {
 +static const struct device_type wiphy_type = {
        .name   = "wlan",
  };
  
@@@ -859,8 -859,11 +859,11 @@@ static int cfg80211_netdev_notifier_cal
                break;
        case NETDEV_DOWN:
                cfg80211_update_iface_num(rdev, wdev->iftype, -1);
-               WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
-                       !rdev->scan_req->notified);
+               if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+                       if (WARN_ON(!rdev->scan_req->notified))
+                               rdev->scan_req->aborted = true;
+                       ___cfg80211_scan_done(rdev, false);
+               }
  
                if (WARN_ON(rdev->sched_scan_req &&
                            rdev->sched_scan_req->dev == wdev->netdev)) {
diff --combined net/wireless/core.h
index 9895ab16c0510d2fcbf631d8120ec535beed7db8,f1d193b557b69a5c021b76130f04bf58655288dd..40683004d52338fa8830a11cd38fdd42c74c156a
@@@ -62,6 -62,7 +62,7 @@@ struct cfg80211_registered_device 
        struct rb_root bss_tree;
        u32 bss_generation;
        struct cfg80211_scan_request *scan_req; /* protected by RTNL */
+       struct sk_buff *scan_msg;
        struct cfg80211_sched_scan_request *sched_scan_req;
        unsigned long suspend_at;
        struct work_struct scan_done_wk;
@@@ -210,7 -211,6 +211,7 @@@ struct cfg80211_event 
                } dc;
                struct {
                        u8 bssid[ETH_ALEN];
 +                      struct ieee80211_channel *channel;
                } ij;
        };
  };
@@@ -258,8 -258,7 +259,8 @@@ int __cfg80211_leave_ibss(struct cfg802
                          struct net_device *dev, bool nowext);
  int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, bool nowext);
 -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid);
 +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 +                          struct ieee80211_channel *channel);
  int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
                            struct wireless_dev *wdev);
  
@@@ -363,7 -362,8 +364,8 @@@ int cfg80211_validate_key_settings(stru
                                   struct key_params *params, int key_idx,
                                   bool pairwise, const u8 *mac_addr);
  void __cfg80211_scan_done(struct work_struct *wk);
- void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev);
+ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
+                          bool send_message);
  void __cfg80211_sched_scan_results(struct work_struct *wk);
  int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
                               bool driver_initiated);
@@@ -443,8 -443,7 +445,8 @@@ static inline unsigned int elapsed_jiff
  void
  cfg80211_get_chan_state(struct wireless_dev *wdev,
                        struct ieee80211_channel **chan,
 -                      enum cfg80211_chan_mode *chanmode);
 +                      enum cfg80211_chan_mode *chanmode,
 +                      u8 *radar_detect);
  
  int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
                                 struct cfg80211_chan_def *chandef);
diff --combined net/wireless/nl80211.c
index ebea1a197afb368faadfc90d1f97f2ffa6e1bcff,4fe2e6e2bc7635daef9aee5752ed97eafb29012a..8e6b6a2d35cb27984aaddb73cdbc462ef3e350f6
@@@ -382,8 -382,6 +382,8 @@@ static const struct nla_policy nl80211_
        [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
        [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
                                   .len = IEEE80211_QOS_MAP_LEN_MAX },
 +      [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
 +      [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
  };
  
  /* policy for the key attributes */
@@@ -857,19 -855,6 +857,19 @@@ static int nl80211_key_allowed(struct w
        return 0;
  }
  
 +static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy,
 +                                                      struct nlattr *tb)
 +{
 +      struct ieee80211_channel *chan;
 +
 +      if (tb == NULL)
 +              return NULL;
 +      chan = ieee80211_get_channel(wiphy, nla_get_u32(tb));
 +      if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
 +              return NULL;
 +      return chan;
 +}
 +
  static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
  {
        struct nlattr *nl_modes = nla_nest_start(msg, attr);
@@@ -1601,12 -1586,6 +1601,12 @@@ static int nl80211_send_wiphy(struct cf
                    (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
                     nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
                        goto nla_put_failure;
 +
 +              if (dev->wiphy.max_ap_assoc_sta &&
 +                  nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
 +                              dev->wiphy.max_ap_assoc_sta))
 +                      goto nla_put_failure;
 +
                state->split_start++;
                break;
        case 11:
@@@ -1740,9 -1719,10 +1740,10 @@@ static int nl80211_dump_wiphy(struct sk
                                 * We can then retry with the larger buffer.
                                 */
                                if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
-                                   !skb->len &&
+                                   !skb->len && !state->split &&
                                    cb->min_dump_alloc < 4096) {
                                        cb->min_dump_alloc = 4096;
+                                       state->split_start = 0;
                                        rtnl_unlock();
                                        return 1;
                                }
@@@ -2055,12 -2035,10 +2056,12 @@@ static int nl80211_set_wiphy(struct sk_
                nla_for_each_nested(nl_txq_params,
                                    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
                                    rem_txq_params) {
 -                      nla_parse(tb, NL80211_TXQ_ATTR_MAX,
 -                                nla_data(nl_txq_params),
 -                                nla_len(nl_txq_params),
 -                                txq_params_policy);
 +                      result = nla_parse(tb, NL80211_TXQ_ATTR_MAX,
 +                                         nla_data(nl_txq_params),
 +                                         nla_len(nl_txq_params),
 +                                         txq_params_policy);
 +                      if (result)
 +                              return result;
                        result = parse_txq_params(tb, &txq_params);
                        if (result)
                                return result;
@@@ -3281,7 -3259,7 +3282,7 @@@ static int nl80211_start_ap(struct sk_b
        if (!err) {
                wdev->preset_chandef = params.chandef;
                wdev->beacon_interval = params.beacon_interval;
 -              wdev->channel = params.chandef.chan;
 +              wdev->chandef = params.chandef;
                wdev->ssid_len = params.ssid_len;
                memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
        }
@@@ -3924,8 -3902,8 +3925,8 @@@ static struct net_device *get_vlan(stru
        return ERR_PTR(ret);
  }
  
 -static struct nla_policy
 -nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
 +static const struct nla_policy
 +nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
        [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
        [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
  };
@@@ -4626,6 -4604,8 +4627,6 @@@ static int parse_reg_rule(struct nlatt
                return -EINVAL;
        if (!tb[NL80211_ATTR_FREQ_RANGE_END])
                return -EINVAL;
 -      if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
 -              return -EINVAL;
        if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
                return -EINVAL;
  
                nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
        freq_range->end_freq_khz =
                nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
 -      freq_range->max_bandwidth_khz =
 -              nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
 +      if (tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
 +              freq_range->max_bandwidth_khz =
 +                      nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
  
        power_rule->max_eirp =
                nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
@@@ -5107,7 -5086,6 +5108,7 @@@ static int nl80211_get_reg(struct sk_bu
                const struct ieee80211_reg_rule *reg_rule;
                const struct ieee80211_freq_range *freq_range;
                const struct ieee80211_power_rule *power_rule;
 +              unsigned int max_bandwidth_khz;
  
                reg_rule = &regdom->reg_rules[i];
                freq_range = &reg_rule->freq_range;
                if (!nl_reg_rule)
                        goto nla_put_failure_rcu;
  
 +              max_bandwidth_khz = freq_range->max_bandwidth_khz;
 +              if (!max_bandwidth_khz)
 +                      max_bandwidth_khz = reg_get_max_bandwidth(regdom,
 +                                                                reg_rule);
 +
                if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
                                reg_rule->flags) ||
                    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
                    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
                                freq_range->end_freq_khz) ||
                    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
 -                              freq_range->max_bandwidth_khz) ||
 +                              max_bandwidth_khz) ||
                    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
                                power_rule->max_antenna_gain) ||
                    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
@@@ -5205,11 -5178,9 +5206,11 @@@ static int nl80211_set_reg(struct sk_bu
  
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
                            rem_reg_rules) {
 -              nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
 -                        nla_data(nl_reg_rule), nla_len(nl_reg_rule),
 -                        reg_rule_policy);
 +              r = nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
 +                            nla_data(nl_reg_rule), nla_len(nl_reg_rule),
 +                            reg_rule_policy);
 +              if (r)
 +                      goto bad_reg;
                r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
                if (r)
                        goto bad_reg;
@@@ -5274,7 -5245,7 +5275,7 @@@ static int nl80211_trigger_scan(struct 
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
  
-       if (rdev->scan_req) {
+       if (rdev->scan_req || rdev->scan_msg) {
                err = -EBUSY;
                goto unlock;
        }
@@@ -5472,7 -5443,6 +5473,7 @@@ static int nl80211_start_sched_scan(str
        enum ieee80211_band band;
        size_t ie_len;
        struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
 +      s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
  
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
            !rdev->ops->sched_scan_start)
        if (n_ssids > wiphy->max_sched_scan_ssids)
                return -EINVAL;
  
 -      if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
 +      /*
 +       * First, count the number of 'real' matchsets. Due to an issue with
 +       * the old implementation, matchsets containing only the RSSI attribute
 +       * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
 +       * RSSI for all matchsets, rather than their own matchset for reporting
 +       * all APs with a strong RSSI. This is needed to be compatible with
 +       * older userspace that treated a matchset with only the RSSI as the
 +       * global RSSI for all other matchsets - if there are other matchsets.
 +       */
 +      if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
                nla_for_each_nested(attr,
                                    info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
 -                                  tmp)
 -                      n_match_sets++;
 +                                  tmp) {
 +                      struct nlattr *rssi;
 +
 +                      err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
 +                                      nla_data(attr), nla_len(attr),
 +                                      nl80211_match_policy);
 +                      if (err)
 +                              return err;
 +                      /* add other standalone attributes here */
 +                      if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
 +                              n_match_sets++;
 +                              continue;
 +                      }
 +                      rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
 +                      if (rssi)
 +                              default_match_rssi = nla_get_s32(rssi);
 +              }
 +      }
 +
 +      /* However, if there's no other matchset, add the RSSI one */
 +      if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
 +              n_match_sets = 1;
  
        if (n_match_sets > wiphy->max_match_sets)
                return -EINVAL;
                                    tmp) {
                        struct nlattr *ssid, *rssi;
  
 -                      nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
 -                                nla_data(attr), nla_len(attr),
 -                                nl80211_match_policy);
 +                      err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
 +                                      nla_data(attr), nla_len(attr),
 +                                      nl80211_match_policy);
 +                      if (err)
 +                              goto out_free;
                        ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
                        if (ssid) {
 +                              if (WARN_ON(i >= n_match_sets)) {
 +                                      /* this indicates a programming error,
 +                                       * the loop above should have verified
 +                                       * things properly
 +                                       */
 +                                      err = -EINVAL;
 +                                      goto out_free;
 +                              }
 +
                                if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
                                        err = -EINVAL;
                                        goto out_free;
                                       nla_data(ssid), nla_len(ssid));
                                request->match_sets[i].ssid.ssid_len =
                                        nla_len(ssid);
 +                              /* special attribute - old implemenation w/a */
 +                              request->match_sets[i].rssi_thold =
 +                                      default_match_rssi;
 +                              rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
 +                              if (rssi)
 +                                      request->match_sets[i].rssi_thold =
 +                                              nla_get_s32(rssi);
                        }
 -                      rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
 -                      if (rssi)
 -                              request->rssi_thold = nla_get_u32(rssi);
 -                      else
 -                              request->rssi_thold =
 -                                                 NL80211_SCAN_RSSI_THOLD_OFF;
                        i++;
                }
 +
 +              /* there was no other matchset, so the RSSI one is alone */
 +              if (i == 0)
 +                      request->match_sets[0].rssi_thold = default_match_rssi;
 +
 +              request->min_rssi_thold = INT_MAX;
 +              for (i = 0; i < n_match_sets; i++)
 +                      request->min_rssi_thold =
 +                              min(request->match_sets[i].rssi_thold,
 +                                  request->min_rssi_thold);
 +      } else {
 +              request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
        }
  
        if (info->attrs[NL80211_ATTR_IE]) {
@@@ -5802,7 -5719,7 +5803,7 @@@ static int nl80211_start_radar_detectio
  
        err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
        if (!err) {
 -              wdev->channel = chandef.chan;
 +              wdev->chandef = chandef;
                wdev->cac_started = true;
                wdev->cac_start_time = jiffies;
        }
@@@ -5834,15 -5751,10 +5835,15 @@@ static int nl80211_channel_switch(struc
  
                /* useless if AP is not running */
                if (!wdev->beacon_interval)
 -                      return -EINVAL;
 +                      return -ENOTCONN;
                break;
        case NL80211_IFTYPE_ADHOC:
 +              if (!wdev->ssid_len)
 +                      return -ENOTCONN;
 +              break;
        case NL80211_IFTYPE_MESH_POINT:
 +              if (!wdev->mesh_id_len)
 +                      return -ENOTCONN;
                break;
        default:
                return -EOPNOTSUPP;
@@@ -6280,9 -6192,9 +6281,9 @@@ static int nl80211_authenticate(struct 
                return -EOPNOTSUPP;
  
        bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
 -      chan = ieee80211_get_channel(&rdev->wiphy,
 -              nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
 -      if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
 +      chan = nl80211_get_valid_chan(&rdev->wiphy,
 +                                    info->attrs[NL80211_ATTR_WIPHY_FREQ]);
 +      if (!chan)
                return -EINVAL;
  
        ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@@ -6435,9 -6347,9 +6436,9 @@@ static int nl80211_associate(struct sk_
  
        bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
  
 -      chan = ieee80211_get_channel(&rdev->wiphy,
 -              nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
 -      if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
 +      chan = nl80211_get_valid_chan(&rdev->wiphy,
 +                                    info->attrs[NL80211_ATTR_WIPHY_FREQ]);
 +      if (!chan)
                return -EINVAL;
  
        ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@@ -7073,9 -6985,6 +7074,9 @@@ static int nl80211_connect(struct sk_bu
  
        if (info->attrs[NL80211_ATTR_MAC])
                connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
 +      else if (info->attrs[NL80211_ATTR_MAC_HINT])
 +              connect.bssid_hint =
 +                      nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
        connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
        connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
  
        }
  
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
 -              connect.channel =
 -                      ieee80211_get_channel(wiphy,
 -                          nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
 -              if (!connect.channel ||
 -                  connect.channel->flags & IEEE80211_CHAN_DISABLED)
 +              connect.channel = nl80211_get_valid_chan(
 +                      wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
 +              if (!connect.channel)
 +                      return -EINVAL;
 +      } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) {
 +              connect.channel_hint = nl80211_get_valid_chan(
 +                      wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]);
 +              if (!connect.channel_hint)
                        return -EINVAL;
        }
  
@@@ -7515,7 -7421,6 +7516,7 @@@ static const struct nla_policy nl80211_
        [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
                                .len = NL80211_MAX_SUPP_HT_RATES },
        [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
 +      [NL80211_TXRATE_GI] = { .type = NLA_U8 },
  };
  
  static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
         * directly to the enum ieee80211_band values used in cfg80211.
         */
        BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
 -      nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
 -      {
 +      nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
                enum ieee80211_band band = nla_type(tx_rates);
 +              int err;
 +
                if (band < 0 || band >= IEEE80211_NUM_BANDS)
                        return -EINVAL;
                sband = rdev->wiphy.bands[band];
                if (sband == NULL)
                        return -EINVAL;
 -              nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
 -                        nla_len(tx_rates), nl80211_txattr_policy);
 +              err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
 +                              nla_len(tx_rates), nl80211_txattr_policy);
 +              if (err)
 +                      return err;
                if (tb[NL80211_TXRATE_LEGACY]) {
                        mask.control[band].legacy = rateset_to_mask(
                                sband,
                                        mask.control[band].vht_mcs))
                                return -EINVAL;
                }
 +              if (tb[NL80211_TXRATE_GI]) {
 +                      mask.control[band].gi =
 +                              nla_get_u8(tb[NL80211_TXRATE_GI]);
 +                      if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
 +                              return -EINVAL;
 +              }
  
                if (mask.control[band].legacy == 0) {
                        /* don't allow empty legacy rates if HT or VHT
@@@ -7881,8 -7777,8 +7882,8 @@@ static int nl80211_get_power_save(struc
        return err;
  }
  
 -static struct nla_policy
 -nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
 +static const struct nla_policy
 +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
        [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
@@@ -10116,40 -10012,31 +10117,31 @@@ void nl80211_send_scan_start(struct cfg
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
  }
  
void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
-                           struct wireless_dev *wdev)
struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
+                                      struct wireless_dev *wdev, bool aborted)
  {
        struct sk_buff *msg;
  
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
-               return;
+               return NULL;
  
        if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
-                                 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
+                                 aborted ? NL80211_CMD_SCAN_ABORTED :
+                                           NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
                nlmsg_free(msg);
-               return;
+               return NULL;
        }
  
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_SCAN, GFP_KERNEL);
+       return msg;
  }
  
- void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
-                              struct wireless_dev *wdev)
+ void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
+                             struct sk_buff *msg)
  {
-       struct sk_buff *msg;
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
  
-       if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
-                                 NL80211_CMD_SCAN_ABORTED) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
        genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
  }
@@@ -11220,8 -11107,7 +11212,8 @@@ void cfg80211_ch_switch_notify(struct n
                    wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                return;
  
 -      wdev->channel = chandef->chan;
 +      wdev->chandef = *chandef;
 +      wdev->preset_chandef = *chandef;
        nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
  }
  EXPORT_SYMBOL(cfg80211_ch_switch_notify);
@@@ -11735,35 -11621,6 +11727,35 @@@ void cfg80211_crit_proto_stopped(struc
  }
  EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
  
 +void nl80211_send_ap_stopped(struct wireless_dev *wdev)
 +{
 +      struct wiphy *wiphy = wdev->wiphy;
 +      struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 +      struct sk_buff *msg;
 +      void *hdr;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return;
 +
 +      hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STOP_AP);
 +      if (!hdr)
 +              goto out;
 +
 +      if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
 +          nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) ||
 +          nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
 +              goto out;
 +
 +      genlmsg_end(msg, hdr);
 +
 +      genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
 +                              NL80211_MCGRP_MLME, GFP_KERNEL);
 +      return;
 + out:
 +      nlmsg_free(msg);
 +}
 +
  /* initialisation/exit functions */
  
  int nl80211_init(void)
diff --combined net/wireless/nl80211.h
index cb0216e1a0041b1c66b2a4a3e55405492b7743f7,75799746d845f6fea6dcfa6b6f0b84bdc20aa14e..1e6df9630f42f11f815578a4bbc73fc7b037017d
@@@ -8,10 -8,10 +8,10 @@@ void nl80211_exit(void)
  void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
  void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                             struct wireless_dev *wdev);
void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
-                           struct wireless_dev *wdev);
- void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
-                              struct wireless_dev *wdev);
struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
+                                      struct wireless_dev *wdev, bool aborted);
+ void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
+                             struct sk_buff *msg);
  void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, u32 cmd);
  void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
@@@ -74,8 -74,6 +74,8 @@@ nl80211_radar_notify(struct cfg80211_re
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp);
  
 +void nl80211_send_ap_stopped(struct wireless_dev *wdev);
 +
  void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
  
  #endif /* __NET_WIRELESS_NL80211_H */