ath10k: decouple HTT TX completions
[cascardo/linux.git] / drivers / net / wireless / ath / ath10k / mac.c
index da5c333..9112e6d 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/mac80211.h>
 #include <linux/etherdevice.h>
 
+#include "hif.h"
 #include "core.h"
 #include "debug.h"
 #include "wmi.h"
@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                .macaddr = macaddr,
        };
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
                arg.key_flags = WMI_KEY_PAIRWISE;
        else
@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
        struct ath10k *ar = arvif->ar;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        INIT_COMPLETION(ar->install_key_done);
 
        ret = ath10k_send_key(arvif, key, cmd, macaddr);
@@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        return 0;
 }
 
+static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+{
+       if (value != 0xFFFFFFFF)
+               value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
+                             ATH10K_RTS_MAX);
+
+       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                        value);
+}
+
+static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
+{
+       if (value != 0xFFFFFFFF)
+               value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
+                               ATH10K_FRAGMT_THRESHOLD_MIN,
+                               ATH10K_FRAGMT_THRESHOLD_MAX);
+
+       return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                        WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                        value);
+}
+
 static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 {
        int ret;
@@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
        spin_unlock_bh(&ar->data_lock);
 }
 
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+       struct ath10k_peer *peer, *tmp;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+               list_del(&peer->list);
+               kfree(peer);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 /************************/
 /* Interface management */
 /************************/
@@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
 {
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = wait_for_completion_timeout(&ar->vdev_setup_done,
                                          ATH10K_VDEV_SETUP_TIMEOUT_HZ);
        if (ret == 0)
@@ -416,6 +460,11 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
                arg.ssid_len = arvif->vif->bss_conf.ssid_len;
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac vdev %d start center_freq %d phymode %s\n",
+                  arg.vdev_id, arg.channel.freq,
+                  ath10k_wmi_phymode_str(arg.channel.mode));
+
        ret = ath10k_wmi_vdev_start(ar, &arg);
        if (ret) {
                ath10k_warn("WMI vdev start failed: ret %d\n", ret);
@@ -459,13 +508,10 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 {
        struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
        struct wmi_vdev_start_request_arg arg = {};
-       enum nl80211_channel_type type;
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
-
        arg.vdev_id = vdev_id;
        arg.channel.freq = channel->center_freq;
        arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
@@ -563,7 +609,7 @@ static int ath10k_monitor_create(struct ath10k *ar)
                goto vdev_fail;
        }
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
                   ar->monitor_vdev_id);
 
        ar->monitor_present = true;
@@ -595,7 +641,7 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
        ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
        ar->monitor_present = false;
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
                   ar->monitor_vdev_id);
        return ret;
 }
@@ -605,6 +651,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->enable_beacon) {
                ath10k_vdev_stop(arvif);
                return;
@@ -622,7 +670,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                            arvif->vdev_id);
                return;
        }
-       ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
 }
 
 static void ath10k_control_ibss(struct ath10k_vif *arvif,
@@ -631,6 +679,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (!info->ibss_joined) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
                if (ret)
@@ -680,6 +730,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        enum wmi_sta_ps_mode psmode;
        int ret;
 
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
        if (vif->type != NL80211_IFTYPE_STATION)
                return;
 
@@ -702,14 +754,14 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                psmode = WMI_STA_PS_MODE_DISABLED;
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
+                  arvif->vdev_id, psmode ? "enable" : "disable");
+
        ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
                                             psmode);
        if (ar_iter->ret)
                ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
                            psmode, arvif->vdev_id);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
-                          psmode, arvif->vdev_id);
 }
 
 /**********************/
@@ -722,6 +774,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
                                      struct ieee80211_bss_conf *bss_conf,
                                      struct wmi_peer_assoc_complete_arg *arg)
 {
+       lockdep_assert_held(&ar->conf_mutex);
+
        memcpy(arg->addr, sta->addr, ETH_ALEN);
        arg->vdev_id = arvif->vdev_id;
        arg->peer_aid = sta->aid;
@@ -764,6 +818,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
        const u8 *rsnie = NULL;
        const u8 *wpaie = NULL;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
                               info->bssid, NULL, 0, 0, 0);
        if (bss) {
@@ -804,6 +860,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
        u32 ratemask;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
        ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
        rates = sband->bitrates;
@@ -827,6 +885,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        int smps;
        int i, n;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (!ht_cap->ht_supported)
                return;
 
@@ -891,7 +951,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
        arg->peer_ht_rates.num_rates = n;
        arg->peer_num_spatial_streams = max((n+7) / 8, 1);
 
-       ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
+                  arg->addr,
                   arg->peer_ht_rates.num_rates,
                   arg->peer_num_spatial_streams);
 }
@@ -905,15 +966,17 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
        u32 uapsd = 0;
        u32 max_sp = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (sta->wme)
                arg->peer_flags |= WMI_PEER_QOS;
 
        if (sta->wme && sta->uapsd_queues) {
-               ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+               ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
                           sta->uapsd_queues, sta->max_sp);
 
                arg->peer_flags |= WMI_PEER_APSD;
-               arg->peer_flags |= WMI_RC_UAPSD_FLAG;
+               arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
 
                if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
                        uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
@@ -988,7 +1051,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
        arg->peer_vht_rates.tx_mcs_set =
                __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
 
-       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n",
+                  sta->addr, arg->peer_max_mpdu, arg->peer_flags);
 }
 
 static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
@@ -1016,8 +1080,6 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
 {
        enum wmi_phy_mode phymode = MODE_UNKNOWN;
 
-       /* FIXME: add VHT */
-
        switch (ar->hw->conf.chandef.chan->band) {
        case IEEE80211_BAND_2GHZ:
                if (sta->ht_cap.ht_supported) {
@@ -1031,7 +1093,17 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
 
                break;
        case IEEE80211_BAND_5GHZ:
-               if (sta->ht_cap.ht_supported) {
+               /*
+                * Check VHT first.
+                */
+               if (sta->vht_cap.vht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+                               phymode = MODE_11AC_VHT80;
+                       else if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11AC_VHT40;
+                       else if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
+                               phymode = MODE_11AC_VHT20;
+               } else if (sta->ht_cap.ht_supported) {
                        if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
                                phymode = MODE_11NA_HT40;
                        else
@@ -1045,6 +1117,9 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
                break;
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
+                  sta->addr, ath10k_wmi_phymode_str(phymode));
+
        arg->peer_phymode = phymode;
        WARN_ON(phymode == MODE_UNKNOWN);
 }
@@ -1056,6 +1131,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
 {
        struct wmi_peer_assoc_complete_arg arg;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
 
        ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
@@ -1079,6 +1156,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        struct ieee80211_sta *ap_sta;
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        rcu_read_lock();
 
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
@@ -1098,15 +1177,15 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        rcu_read_unlock();
 
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac vdev %d up (associated) bssid %pM aid %d\n",
+                  arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+
        ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
                                 bss_conf->bssid);
        if (ret)
                ath10k_warn("VDEV: %d up failed: ret %d\n",
                            arvif->vdev_id, ret);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "VDEV: %d associated, BSSID: %pM, AID: %d\n",
-                          arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
 }
 
 /*
@@ -1119,16 +1198,19 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        int ret;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        /*
         * For some reason, calling VDEV-DOWN before VDEV-STOP
         * makes the FW to send frames via HTT after disassociation.
         * No idea why this happens, even though VDEV-DOWN is supposed
         * to be analogous to link down, so just stop the VDEV.
         */
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n",
+                  arvif->vdev_id);
+
+       /* FIXME: check return value */
        ret = ath10k_vdev_stop(arvif);
-       if (!ret)
-               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
-                          arvif->vdev_id);
 
        /*
         * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
@@ -1137,12 +1219,10 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
         * interfaces as it expects there is no rx when no interface is
         * running.
         */
-       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
-       if (ret)
-               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
-                          arvif->vdev_id, ret);
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
 
-       ath10k_wmi_flush_tx(ar);
+       /* FIXME: why don't we print error if wmi call fails? */
+       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
 
        arvif->def_wep_key_index = 0;
 }
@@ -1152,6 +1232,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
        if (ret) {
                ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
@@ -1172,6 +1254,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
 {
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
                ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
@@ -1198,6 +1282,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        int ret;
        int i;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        bands = hw->wiphy->bands;
        for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                if (!bands[band])
@@ -1261,8 +1347,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
                                continue;
 
                        ath10k_dbg(ATH10K_DBG_WMI,
-                                  "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
-                                  __func__, ch - arg.channels, arg.n_channels,
+                                  "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+                                   ch - arg.channels, arg.n_channels,
                                   ch->freq, ch->max_power, ch->max_reg_power,
                                   ch->max_antenna_gain, ch->mode);
 
@@ -1276,21 +1362,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        return ret;
 }
 
-static void ath10k_reg_notifier(struct wiphy *wiphy,
-                               struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
 {
-       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct reg_dmn_pair_mapping *regpair;
-       struct ath10k *ar = hw->priv;
        int ret;
 
-       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+       lockdep_assert_held(&ar->conf_mutex);
 
        ret = ath10k_update_channel_list(ar);
        if (ret)
                ath10k_warn("could not update channel list (%d)\n", ret);
 
        regpair = ar->ath_common.regulatory.regpair;
+
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1303,6 +1387,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
                ath10k_warn("could not set pdev regdomain (%d)\n", ret);
 }
 
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct ath10k *ar = hw->priv;
+
+       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON)
+               ath10k_regd_update(ar);
+       mutex_unlock(&ar->conf_mutex);
+}
+
 /***************/
 /* TX handlers */
 /***************/
@@ -1322,9 +1420,9 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
                return;
 
        qos_ctl = ieee80211_get_qos_ctl(hdr);
-       memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
-               skb->len - ieee80211_hdrlen(hdr->frame_control));
-       skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+       memmove(skb->data + IEEE80211_QOS_CTL_LEN,
+               skb->data, (void *)qos_ctl - (void *)skb->data);
+       skb_pull(skb, IEEE80211_QOS_CTL_LEN);
 }
 
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
@@ -1337,10 +1435,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
        struct ieee80211_key_conf *key = info->control.hw_key;
        int ret;
 
-       /* TODO AP mode should be implemented */
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
-
        if (!ieee80211_has_protected(hdr->frame_control))
                return;
 
@@ -1354,7 +1448,8 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
        if (key->keyidx == arvif->def_wep_key_index)
                return;
 
-       ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n",
+                  arvif->vdev_id, key->keyidx);
 
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                        WMI_VDEV_PARAM_DEF_KEYID,
@@ -1396,17 +1491,24 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        int ret;
 
+       if (ar->htt.target_version_major >= 3) {
+               /* Since HTT 3.0 there is no separate mgmt tx command */
+               ret = ath10k_htt_tx(&ar->htt, skb);
+               goto exit;
+       }
+
        if (ieee80211_is_mgmt(hdr->frame_control))
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else if (ieee80211_is_nullfunc(hdr->frame_control))
                /* FW does not report tx status properly for NullFunc frames
                 * unless they are sent through mgmt tx path. mac80211 sends
                 * those frames when it detects link/beacon loss and depends on
                 * the tx status to be correct. */
-               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+               ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
        else
-               ret = ath10k_htt_tx(ar->htt, skb);
+               ret = ath10k_htt_tx(&ar->htt, skb);
 
+exit:
        if (ret) {
                ath10k_warn("tx failed (%d). dropping packet.\n", ret);
                ieee80211_free_txskb(ar->hw, skb);
@@ -1450,7 +1552,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
 
                mutex_lock(&ar->conf_mutex);
 
-               ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+               ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n",
                           skb);
 
                hdr = (struct ieee80211_hdr *)skb->data;
@@ -1462,6 +1564,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                spin_unlock_bh(&ar->data_lock);
 
                if (peer)
+                       /* FIXME: should this use ath10k_warn()? */
                        ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
                                   peer_addr, vdev_id);
 
@@ -1552,11 +1655,13 @@ static int ath10k_abort_scan(struct ath10k *ar)
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
                ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               spin_unlock_bh(&ar->data_lock);
                return -EIO;
        }
 
-       ath10k_wmi_flush_tx(ar);
-
        ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
        if (ret == 0)
                ath10k_warn("timed out while waiting for scan to stop\n");
@@ -1590,10 +1695,6 @@ static int ath10k_start_scan(struct ath10k *ar,
        if (ret)
                return ret;
 
-       /* make sure we submit the command so the completion
-       * timeout makes sense */
-       ath10k_wmi_flush_tx(ar);
-
        ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
        if (ret == 0) {
                ath10k_abort_scan(ar);
@@ -1639,18 +1740,24 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        /* we must calculate tid before we apply qos workaround
         * as we'd lose the qos control field */
        tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-       if (ieee80211_is_data_qos(hdr->frame_control) &&
-           is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
+       if (ieee80211_is_mgmt(hdr->frame_control)) {
+               tid = HTT_DATA_TX_EXT_TID_MGMT;
+       } else if (ieee80211_is_data_qos(hdr->frame_control) &&
+                  is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
                u8 *qc = ieee80211_get_qos_ctl(hdr);
                tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
        }
 
-       ath10k_tx_h_qos_workaround(hw, control, skb);
-       ath10k_tx_h_update_wep_key(skb);
-       ath10k_tx_h_add_p2p_noa_ie(ar, skb);
-       ath10k_tx_h_seq_no(skb);
+       /* it makes no sense to process injected frames like that */
+       if (info->control.vif &&
+           info->control.vif->type != NL80211_IFTYPE_MONITOR) {
+               ath10k_tx_h_qos_workaround(hw, control, skb);
+               ath10k_tx_h_update_wep_key(skb);
+               ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+               ath10k_tx_h_seq_no(skb);
+       }
 
-       memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
+       ATH10K_SKB_CB(skb)->htt.is_offchan = false;
        ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
        ATH10K_SKB_CB(skb)->htt.tid = tid;
 
@@ -1673,10 +1780,57 @@ static void ath10k_tx(struct ieee80211_hw *hw,
 /*
  * Initialize various parameters with default vaules.
  */
+void ath10k_halt(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+       ath10k_offchan_tx_purge(ar);
+       ath10k_peer_cleanup_all(ar);
+       ath10k_core_stop(ar);
+       ath10k_hif_power_down(ar);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               del_timer(&ar->scan.timeout);
+               ar->scan.in_progress = false;
+               ieee80211_scan_completed(ar->hw, true);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
-       int ret;
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state != ATH10K_STATE_OFF &&
+           ar->state != ATH10K_STATE_RESTARTING) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       ret = ath10k_hif_power_up(ar);
+       if (ret) {
+               ath10k_err("could not init hif (%d)\n", ret);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       ret = ath10k_core_start(ar);
+       if (ret) {
+               ath10k_err("could not init core (%d)\n", ret);
+               ath10k_hif_power_down(ar);
+               ar->state = ATH10K_STATE_OFF;
+               goto exit;
+       }
+
+       if (ar->state == ATH10K_STATE_OFF)
+               ar->state = ATH10K_STATE_ON;
+       else if (ar->state == ATH10K_STATE_RESTARTING)
+               ar->state = ATH10K_STATE_RESTARTED;
 
        ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
        if (ret)
@@ -1688,6 +1842,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
 
+       ath10k_regd_update(ar);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
        return 0;
 }
 
@@ -1695,41 +1853,61 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
 
-       /* avoid leaks in case FW never confirms scan for offchannel */
+       mutex_lock(&ar->conf_mutex);
+       if (ar->state == ATH10K_STATE_ON ||
+           ar->state == ATH10K_STATE_RESTARTED ||
+           ar->state == ATH10K_STATE_WEDGED)
+               ath10k_halt(ar);
+
+       ar->state = ATH10K_STATE_OFF;
+       mutex_unlock(&ar->conf_mutex);
+
        cancel_work_sync(&ar->offchan_tx_work);
-       ath10k_offchan_tx_purge(ar);
+       cancel_work_sync(&ar->restart_work);
 }
 
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
 {
        struct ath10k_generic_iter ar_iter;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar->state == ATH10K_STATE_RESTARTED)
+               return;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       ieee80211_iterate_active_interfaces_atomic(
+               ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_ps_iter, &ar_iter);
+
+       if (ar_iter.ret)
+               ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
        struct ath10k *ar = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
        int ret = 0;
-       u32 flags;
 
        mutex_lock(&ar->conf_mutex);
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
+               ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n",
                           conf->chandef.chan->center_freq);
                spin_lock_bh(&ar->data_lock);
                ar->rx_channel = conf->chandef.chan;
                spin_unlock_bh(&ar->data_lock);
        }
 
-       if (changed & IEEE80211_CONF_CHANGE_PS) {
-               memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
-               ar_iter.ar = ar;
-               flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
-               ieee80211_iterate_active_interfaces_atomic(hw,
-                                                          flags,
-                                                          ath10k_ps_iter,
-                                                          &ar_iter);
-
-               ret = ar_iter.ret;
-       }
+       if (changed & IEEE80211_CONF_CHANGE_PS)
+               ath10k_config_ps(ar);
 
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
                if (conf->flags & IEEE80211_CONF_MONITOR)
@@ -1761,6 +1939,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&ar->conf_mutex);
 
+       memset(arvif, 0, sizeof(*arvif));
+
        arvif->ar = ar;
        arvif->vif = vif;
 
@@ -1807,7 +1987,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                break;
        }
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n",
                   arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
 
        ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
@@ -1859,6 +2039,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
        }
 
+       ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
+       if (ret)
+               ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
+       ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
+       if (ret)
+               ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+                           arvif->vdev_id, ret);
+
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
                ar->monitor_present = true;
 
@@ -1876,7 +2066,12 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&ar->conf_mutex);
 
-       ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+       spin_lock_bh(&ar->data_lock);
+       if (arvif->beacon) {
+               dev_kfree_skb_any(arvif->beacon);
+               arvif->beacon = NULL;
+       }
+       spin_unlock_bh(&ar->data_lock);
 
        ar->free_vdev_map |= 1 << (arvif->vdev_id);
 
@@ -1888,6 +2083,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
                kfree(arvif->u.ap.noa_data);
        }
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev delete %d (remove interface)\n",
+                  arvif->vdev_id);
+
        ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
        if (ret)
                ath10k_warn("WMI vdev delete failed: %d\n", ret);
@@ -1929,18 +2127,20 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw,
 
        if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
            !ar->monitor_enabled) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n",
+                          ar->monitor_vdev_id);
+
                ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
                if (ret)
                        ath10k_warn("Unable to start monitor mode\n");
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
        } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
                   ar->monitor_enabled) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n",
+                          ar->monitor_vdev_id);
+
                ret = ath10k_monitor_stop(ar);
                if (ret)
                        ath10k_warn("Unable to stop monitor mode\n");
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -1965,41 +2165,41 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                                WMI_VDEV_PARAM_BEACON_INTERVAL,
                                                arvif->beacon_interval);
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d beacon_interval %d\n",
+                          arvif->vdev_id, arvif->beacon_interval);
+
                if (ret)
                        ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Beacon interval: %d set for VDEV: %d\n",
-                                  arvif->beacon_interval, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_BEACON) {
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "vdev %d set beacon tx mode to staggered\n",
+                          arvif->vdev_id);
+
                ret = ath10k_wmi_pdev_set_param(ar,
                                                WMI_PDEV_PARAM_BEACON_TX_MODE,
                                                WMI_BEACON_STAGGERED_MODE);
                if (ret)
                        ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set staggered beacon mode for VDEV: %d\n",
-                                  arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_BEACON_INFO) {
                arvif->dtim_period = info->dtim_period;
 
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d dtim_period %d\n",
+                          arvif->vdev_id, arvif->dtim_period);
+
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                                WMI_VDEV_PARAM_DTIM_PERIOD,
                                                arvif->dtim_period);
                if (ret)
                        ath10k_warn("Failed to set dtim period for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set dtim period: %d for VDEV: %d\n",
-                                  arvif->dtim_period, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_SSID &&
@@ -2012,16 +2212,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BSSID) {
                if (!is_zero_ether_addr(info->bssid)) {
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "mac vdev %d create peer %pM\n",
+                                  arvif->vdev_id, info->bssid);
+
                        ret = ath10k_peer_create(ar, arvif->vdev_id,
                                                 info->bssid);
                        if (ret)
                                ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
                                            info->bssid, arvif->vdev_id);
-                       else
-                               ath10k_dbg(ATH10K_DBG_MAC,
-                                          "Added peer: %pM for VDEV: %d\n",
-                                          info->bssid, arvif->vdev_id);
-
 
                        if (vif->type == NL80211_IFTYPE_STATION) {
                                /*
@@ -2031,11 +2230,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                                memcpy(arvif->u.sta.bssid, info->bssid,
                                       ETH_ALEN);
 
+                               ath10k_dbg(ATH10K_DBG_MAC,
+                                          "mac vdev %d start %pM\n",
+                                          arvif->vdev_id, info->bssid);
+
+                               /* FIXME: check return value */
                                ret = ath10k_vdev_start(arvif);
-                               if (!ret)
-                                       ath10k_dbg(ATH10K_DBG_MAC,
-                                                  "VDEV: %d started with BSSID: %pM\n",
-                                                  arvif->vdev_id, info->bssid);
                        }
 
                        /*
@@ -2059,16 +2259,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                else
                        cts_prot = 0;
 
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
+                          arvif->vdev_id, cts_prot);
+
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                                WMI_VDEV_PARAM_ENABLE_RTSCTS,
                                                cts_prot);
                if (ret)
                        ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set CTS prot: %d for VDEV: %d\n",
-                                  cts_prot, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_ERP_SLOT) {
@@ -2079,16 +2278,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                else
                        slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
 
+               ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
+                          arvif->vdev_id, slottime);
+
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                                WMI_VDEV_PARAM_SLOT_TIME,
                                                slottime);
                if (ret)
                        ath10k_warn("Failed to set erp slot for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set slottime: %d for VDEV: %d\n",
-                                  slottime, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -2098,16 +2296,16 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                else
                        preamble = WMI_VDEV_PREAMBLE_LONG;
 
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d preamble %dn",
+                          arvif->vdev_id, preamble);
+
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                                WMI_VDEV_PARAM_PREAMBLE,
                                                preamble);
                if (ret)
                        ath10k_warn("Failed to set preamble for VDEV: %d\n",
                                    arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Set preamble: %d for VDEV: %d\n",
-                                  preamble, arvif->vdev_id);
        }
 
        if (changed & BSS_CHANGED_ASSOC) {
@@ -2164,6 +2362,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
                        arg.ssids[i].len  = req->ssids[i].ssid_len;
                        arg.ssids[i].ssid = req->ssids[i].ssid;
                }
+       } else {
+               arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
        }
 
        if (req->n_channels) {
@@ -2296,27 +2496,26 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New station addition.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d peer create %pM (new sta)\n",
+                          arvif->vdev_id, sta->addr);
+
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
                        ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
                                    sta->addr, arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Added peer: %pM for VDEV: %d\n",
-                                  sta->addr, arvif->vdev_id);
        } else if ((old_state == IEEE80211_STA_NONE &&
                    new_state == IEEE80211_STA_NOTEXIST)) {
                /*
                 * Existing station deletion.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac vdev %d peer delete %pM (sta gone)\n",
+                          arvif->vdev_id, sta->addr);
                ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
                if (ret)
                        ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
                                    sta->addr, arvif->vdev_id);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Removed peer: %pM for VDEV: %d\n",
-                                  sta->addr, arvif->vdev_id);
 
                if (vif->type == NL80211_IFTYPE_STATION)
                        ath10k_bss_disassoc(hw, vif);
@@ -2327,14 +2526,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New association.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
+                          sta->addr);
+
                ret = ath10k_station_assoc(ar, arvif, sta);
                if (ret)
                        ath10k_warn("Failed to associate station: %pM\n",
                                    sta->addr);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Station %pM moved to assoc state\n",
-                                  sta->addr);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH &&
                   (vif->type == NL80211_IFTYPE_AP ||
@@ -2342,14 +2540,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * Disassociation.
                 */
+               ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
+                          sta->addr);
+
                ret = ath10k_station_disassoc(ar, arvif, sta);
                if (ret)
                        ath10k_warn("Failed to disassociate station: %pM\n",
                                    sta->addr);
-               else
-                       ath10k_dbg(ATH10K_DBG_MAC,
-                                  "Station %pM moved to disassociated state\n",
-                                  sta->addr);
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2363,6 +2560,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
        u32 value = 0;
        int ret = 0;
 
+       lockdep_assert_held(&ar->conf_mutex);
+
        if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
                return 0;
 
@@ -2558,18 +2757,22 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
 
-       rts = min_t(u32, rts, ATH10K_RTS_MAX);
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts_threshold %d\n",
+                  arvif->vdev_id, rts);
 
-       ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-                                                WMI_VDEV_PARAM_RTS_THRESHOLD,
-                                                rts);
+       ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
        if (ar_iter->ret)
                ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
                            arvif->vdev_id);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "Set RTS threshold: %d for VDEV: %d\n",
-                          rts, arvif->vdev_id);
 }
 
 static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
@@ -2581,8 +2784,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_rts_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_rts_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2593,24 +2797,23 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath10k_generic_iter *ar_iter = data;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
-       int ret;
 
-       frag = clamp_t(u32, frag,
-                      ATH10K_FRAGMT_THRESHOLD_MIN,
-                      ATH10K_FRAGMT_THRESHOLD_MAX);
+       lockdep_assert_held(&arvif->ar->conf_mutex);
+
+       /* During HW reconfiguration mac80211 reports all interfaces that were
+        * running until reconfiguration was started. Since FW doesn't have any
+        * vdevs at this point we must not iterate over this interface list.
+        * This setting will be updated upon add_interface(). */
+       if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
+               return;
 
-       ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
-                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
-                                       frag);
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation_threshold %d\n",
+                  arvif->vdev_id, frag);
 
-       ar_iter->ret = ret;
+       ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
        if (ar_iter->ret)
                ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
                            arvif->vdev_id);
-       else
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "Set frag threshold: %d for VDEV: %d\n",
-                          frag, arvif->vdev_id);
 }
 
 static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
@@ -2622,8 +2825,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        ar_iter.ar = ar;
 
        mutex_lock(&ar->conf_mutex);
-       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                                           ath10k_set_frag_iter, &ar_iter);
+       ieee80211_iterate_active_interfaces_atomic(
+               hw, IEEE80211_IFACE_ITER_NORMAL,
+               ath10k_set_frag_iter, &ar_iter);
        mutex_unlock(&ar->conf_mutex);
 
        return ar_iter.ret;
@@ -2632,6 +2836,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ath10k *ar = hw->priv;
+       bool skip;
        int ret;
 
        /* mac80211 doesn't care if we really xmit queued frames or not
@@ -2639,16 +2844,28 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
        if (drop)
                return;
 
-       ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->state == ATH10K_STATE_WEDGED)
+               goto skip;
+
+       ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
                        bool empty;
-                       spin_lock_bh(&ar->htt->tx_lock);
-                       empty = bitmap_empty(ar->htt->used_msdu_ids,
-                                            ar->htt->max_num_pending_tx);
-                       spin_unlock_bh(&ar->htt->tx_lock);
-                       (empty);
+
+                       spin_lock_bh(&ar->htt.tx_lock);
+                       empty = (ar->htt.num_pending_tx == 0);
+                       spin_unlock_bh(&ar->htt.tx_lock);
+
+                       skip = (ar->state == ATH10K_STATE_WEDGED);
+
+                       (empty || skip);
                }), ATH10K_FLUSH_TIMEOUT_HZ);
-       if (ret <= 0)
+
+       if (ret <= 0 || skip)
                ath10k_warn("tx not flushed\n");
+
+skip:
+       mutex_unlock(&ar->conf_mutex);
 }
 
 /* TODO: Implement this function properly
@@ -2660,6 +2877,118 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
        return 1;
 }
 
+#ifdef CONFIG_PM
+static int ath10k_suspend(struct ieee80211_hw *hw,
+                         struct cfg80211_wowlan *wowlan)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ar->is_target_paused = false;
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret) {
+               ath10k_warn("could not suspend target (%d)\n", ret);
+               return 1;
+       }
+
+       ret = wait_event_interruptible_timeout(ar->event_queue,
+                                              ar->is_target_paused == true,
+                                              1 * HZ);
+       if (ret < 0) {
+               ath10k_warn("suspend interrupted (%d)\n", ret);
+               goto resume;
+       } else if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               goto resume;
+       }
+
+       ret = ath10k_hif_suspend(ar);
+       if (ret) {
+               ath10k_warn("could not suspend hif (%d)\n", ret);
+               goto resume;
+       }
+
+       return 0;
+resume:
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+       return 1;
+}
+
+static int ath10k_resume(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_hif_resume(ar);
+       if (ret) {
+               ath10k_warn("could not resume hif (%d)\n", ret);
+               return 1;
+       }
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret) {
+               ath10k_warn("could not resume target (%d)\n", ret);
+               return 1;
+       }
+
+       return 0;
+}
+#endif
+
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+
+       /* If device failed to restart it will be in a different state, e.g.
+        * ATH10K_STATE_WEDGED */
+       if (ar->state == ATH10K_STATE_RESTARTED) {
+               ath10k_info("device successfully recovered\n");
+               ar->state = ATH10K_STATE_ON;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
+                            struct survey_info *survey)
+{
+       struct ath10k *ar = hw->priv;
+       struct ieee80211_supported_band *sband;
+       struct survey_info *ar_survey = &ar->survey[idx];
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+       if (sband && idx >= sband->n_channels) {
+               idx -= sband->n_channels;
+               sband = NULL;
+       }
+
+       if (!sband)
+               sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+       if (!sband || idx >= sband->n_channels) {
+               ret = -ENOENT;
+               goto exit;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       memcpy(survey, ar_survey, sizeof(*survey));
+       spin_unlock_bh(&ar->data_lock);
+
+       survey->channel = &sband->channels[idx];
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -2680,6 +3009,12 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
+       .restart_complete               = ath10k_restart_complete,
+       .get_survey                     = ath10k_get_survey,
+#ifdef CONFIG_PM
+       .suspend                        = ath10k_suspend,
+       .resume                         = ath10k_resume,
+#endif
 };
 
 #define RATETAB_ENT(_rate, _rateid, _flags) { \
@@ -2797,9 +3132,15 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
        .max    = 8,
        .types  = BIT(NL80211_IFTYPE_STATION)
                | BIT(NL80211_IFTYPE_P2P_CLIENT)
-               | BIT(NL80211_IFTYPE_P2P_GO)
-               | BIT(NL80211_IFTYPE_AP)
-       }
+       },
+       {
+       .max    = 3,
+       .types  = BIT(NL80211_IFTYPE_P2P_GO)
+       },
+       {
+       .max    = 7,
+       .types  = BIT(NL80211_IFTYPE_AP)
+       },
 };
 
 static const struct ieee80211_iface_combination ath10k_if_comb = {
@@ -2814,19 +3155,18 @@ static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
 {
        struct ieee80211_sta_vht_cap vht_cap = {0};
        u16 mcs_map;
+       int i;
 
        vht_cap.vht_supported = 1;
        vht_cap.cap = ar->vht_cap_info;
 
-       /* FIXME: check dynamically how many streams board supports */
-       mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
-               IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
-               IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
-               IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+       mcs_map = 0;
+       for (i = 0; i < 8; i++) {
+               if (i < ar->num_rf_chains)
+                       mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2);
+               else
+                       mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2);
+       }
 
        vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
        vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
@@ -2889,7 +3229,7 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
        if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
                ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
-       for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+       for (i = 0; i < ar->num_rf_chains; i++)
                ht_cap.mcs.rx_mask[i] = 0xFF;
 
        ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
@@ -2948,8 +3288,10 @@ int ath10k_mac_register(struct ath10k *ar)
                channels = kmemdup(ath10k_2ghz_channels,
                                   sizeof(ath10k_2ghz_channels),
                                   GFP_KERNEL);
-               if (!channels)
-                       return -ENOMEM;
+               if (!channels) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
 
                band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
                band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
@@ -2968,11 +3310,8 @@ int ath10k_mac_register(struct ath10k *ar)
                                   sizeof(ath10k_5ghz_channels),
                                   GFP_KERNEL);
                if (!channels) {
-                       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
-                               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
-                               kfree(band->channels);
-                       }
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto err_free;
                }
 
                band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
@@ -3032,29 +3371,36 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
        ar->hw->wiphy->n_iface_combinations = 1;
 
+       ar->hw->netdev_features = NETIF_F_HW_CSUM;
+
        ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
                            ath10k_reg_notifier);
        if (ret) {
                ath10k_err("Regulatory initialization failed\n");
-               return ret;
+               goto err_free;
        }
 
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
                ath10k_err("ieee80211 registration failed: %d\n", ret);
-               return ret;
+               goto err_free;
        }
 
        if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
                ret = regulatory_hint(ar->hw->wiphy,
                                      ar->ath_common.regulatory.alpha2);
                if (ret)
-                       goto exit;
+                       goto err_unregister;
        }
 
        return 0;
-exit:
+
+err_unregister:
        ieee80211_unregister_hw(ar->hw);
+err_free:
+       kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+       kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
        return ret;
 }