ath10k: move wmi_op_version to struct ath10k_fw_file
[cascardo/linux.git] / drivers / net / wireless / ath / ath10k / mac.c
index 78999c9..5fb912a 100644 (file)
@@ -157,6 +157,26 @@ ath10k_mac_max_vht_nss(const u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
        return 1;
 }
 
+int ath10k_mac_ext_resource_config(struct ath10k *ar, u32 val)
+{
+       enum wmi_host_platform_type platform_type;
+       int ret;
+
+       if (test_bit(WMI_SERVICE_TX_MODE_DYNAMIC, ar->wmi.svc_map))
+               platform_type = WMI_HOST_PLATFORM_LOW_PERF;
+       else
+               platform_type = WMI_HOST_PLATFORM_HIGH_PERF;
+
+       ret = ath10k_wmi_ext_resource_config(ar, platform_type, val);
+
+       if (ret && ret != -EOPNOTSUPP) {
+               ath10k_warn(ar, "failed to configure ext resource: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /**********/
 /* Crypto */
 /**********/
@@ -449,10 +469,10 @@ static int ath10k_mac_vif_update_wep_key(struct ath10k_vif *arvif,
        lockdep_assert_held(&ar->conf_mutex);
 
        list_for_each_entry(peer, &ar->peers, list) {
-               if (!memcmp(peer->addr, arvif->vif->addr, ETH_ALEN))
+               if (ether_addr_equal(peer->addr, arvif->vif->addr))
                        continue;
 
-               if (!memcmp(peer->addr, arvif->bssid, ETH_ALEN))
+               if (ether_addr_equal(peer->addr, arvif->bssid))
                        continue;
 
                if (peer->keys[key->keyidx] == key)
@@ -618,10 +638,15 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
        *def = &conf->def;
 }
 
-static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
+static int ath10k_peer_create(struct ath10k *ar,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_sta *sta,
+                             u32 vdev_id,
+                             const u8 *addr,
                              enum wmi_peer_type peer_type)
 {
        struct ath10k_vif *arvif;
+       struct ath10k_peer *peer;
        int num_peers = 0;
        int ret;
 
@@ -650,6 +675,22 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
                return ret;
        }
 
+       spin_lock_bh(&ar->data_lock);
+
+       peer = ath10k_peer_find(ar, vdev_id, addr);
+       if (!peer) {
+               ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
+                           addr, vdev_id);
+               ath10k_wmi_peer_delete(ar, vdev_id, addr);
+               spin_unlock_bh(&ar->data_lock);
+               return -ENOENT;
+       }
+
+       peer->vif = vif;
+       peer->sta = sta;
+
+       spin_unlock_bh(&ar->data_lock);
+
        ar->num_peers++;
 
        return 0;
@@ -731,6 +772,7 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 {
        struct ath10k_peer *peer, *tmp;
+       int peer_id;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -742,6 +784,11 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
                ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
                            peer->addr, vdev_id);
 
+               for_each_set_bit(peer_id, peer->peer_ids,
+                                ATH10K_MAX_NUM_PEER_IDS) {
+                       ar->peer_map[peer_id] = NULL;
+               }
+
                list_del(&peer->list);
                kfree(peer);
                ar->num_peers--;
@@ -1725,7 +1772,7 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 
        if (enable_ps && ath10k_mac_num_vifs_started(ar) > 1 &&
            !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
-                     ar->fw_features)) {
+                     ar->running_fw->fw_file.fw_features)) {
                ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
                            arvif->vdev_id);
                enable_ps = false;
@@ -2013,7 +2060,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
        }
 
        if (sta->mfp &&
-           test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT, ar->fw_features)) {
+           test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT,
+                    ar->running_fw->fw_file.fw_features)) {
                arg->peer_flags |= ar->wmi.peer_flags->pmf;
        }
 }
@@ -2994,6 +3042,13 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
 /* TX handlers */
 /***************/
 
+enum ath10k_mac_tx_path {
+       ATH10K_MAC_TX_HTT,
+       ATH10K_MAC_TX_HTT_MGMT,
+       ATH10K_MAC_TX_WMI_MGMT,
+       ATH10K_MAC_TX_UNKNOWN,
+};
+
 void ath10k_mac_tx_lock(struct ath10k *ar, int reason)
 {
        lockdep_assert_held(&ar->htt.tx_lock);
@@ -3153,7 +3208,8 @@ ath10k_mac_tx_h_get_txmode(struct ath10k *ar,
         */
        if (ar->htt.target_version_major < 3 &&
            (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) &&
-           !test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, ar->fw_features))
+           !test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+                     ar->running_fw->fw_file.fw_features))
                return ATH10K_HW_TXRX_MGMT;
 
        /* Workaround:
@@ -3271,6 +3327,28 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
        }
 }
 
+static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_txq *txq,
+                                   struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+       cb->flags = 0;
+       if (!ath10k_tx_h_use_hwcrypto(vif, skb))
+               cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
+
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               cb->flags |= ATH10K_SKB_F_MGMT;
+
+       if (ieee80211_is_data_qos(hdr->frame_control))
+               cb->flags |= ATH10K_SKB_F_QOS;
+
+       cb->vif = vif;
+       cb->txq = txq;
+}
+
 bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
 {
        /* FIXME: Not really sure since when the behaviour changed. At some
@@ -3306,26 +3384,50 @@ unlock:
        return ret;
 }
 
-static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
-                         struct sk_buff *skb)
+static enum ath10k_mac_tx_path
+ath10k_mac_tx_h_get_txpath(struct ath10k *ar,
+                          struct sk_buff *skb,
+                          enum ath10k_hw_txrx_mode txmode)
 {
-       struct ath10k_htt *htt = &ar->htt;
-       int ret = 0;
-
        switch (txmode) {
        case ATH10K_HW_TXRX_RAW:
        case ATH10K_HW_TXRX_NATIVE_WIFI:
        case ATH10K_HW_TXRX_ETHERNET:
-               ret = ath10k_htt_tx(htt, txmode, skb);
-               break;
+               return ATH10K_MAC_TX_HTT;
        case ATH10K_HW_TXRX_MGMT:
                if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
-                            ar->fw_features))
-                       ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
+                            ar->running_fw->fw_file.fw_features))
+                       return ATH10K_MAC_TX_WMI_MGMT;
                else if (ar->htt.target_version_major >= 3)
-                       ret = ath10k_htt_tx(htt, txmode, skb);
+                       return ATH10K_MAC_TX_HTT;
                else
-                       ret = ath10k_htt_mgmt_tx(htt, skb);
+                       return ATH10K_MAC_TX_HTT_MGMT;
+       }
+
+       return ATH10K_MAC_TX_UNKNOWN;
+}
+
+static int ath10k_mac_tx_submit(struct ath10k *ar,
+                               enum ath10k_hw_txrx_mode txmode,
+                               enum ath10k_mac_tx_path txpath,
+                               struct sk_buff *skb)
+{
+       struct ath10k_htt *htt = &ar->htt;
+       int ret = -EINVAL;
+
+       switch (txpath) {
+       case ATH10K_MAC_TX_HTT:
+               ret = ath10k_htt_tx(htt, txmode, skb);
+               break;
+       case ATH10K_MAC_TX_HTT_MGMT:
+               ret = ath10k_htt_mgmt_tx(htt, skb);
+               break;
+       case ATH10K_MAC_TX_WMI_MGMT:
+               ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
+               break;
+       case ATH10K_MAC_TX_UNKNOWN:
+               WARN_ON_ONCE(1);
+               ret = -EINVAL;
                break;
        }
 
@@ -3334,6 +3436,64 @@ static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
                            ret);
                ieee80211_free_txskb(ar->hw, skb);
        }
+
+       return ret;
+}
+
+/* This function consumes the sk_buff regardless of return value as far as
+ * caller is concerned so no freeing is necessary afterwards.
+ */
+static int ath10k_mac_tx(struct ath10k *ar,
+                        struct ieee80211_vif *vif,
+                        struct ieee80211_sta *sta,
+                        enum ath10k_hw_txrx_mode txmode,
+                        enum ath10k_mac_tx_path txpath,
+                        struct sk_buff *skb)
+{
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       int ret;
+
+       /* We should disable CCK RATE due to P2P */
+       if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+               ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+       switch (txmode) {
+       case ATH10K_HW_TXRX_MGMT:
+       case ATH10K_HW_TXRX_NATIVE_WIFI:
+               ath10k_tx_h_nwifi(hw, skb);
+               ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
+               ath10k_tx_h_seq_no(vif, skb);
+               break;
+       case ATH10K_HW_TXRX_ETHERNET:
+               ath10k_tx_h_8023(skb);
+               break;
+       case ATH10K_HW_TXRX_RAW:
+               if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+                       WARN_ON_ONCE(1);
+                       ieee80211_free_txskb(hw, skb);
+                       return -ENOTSUPP;
+               }
+       }
+
+       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               if (!ath10k_mac_tx_frm_has_freq(ar)) {
+                       ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
+                                  skb);
+
+                       skb_queue_tail(&ar->offchan_tx_queue, skb);
+                       ieee80211_queue_work(hw, &ar->offchan_tx_work);
+                       return 0;
+               }
+       }
+
+       ret = ath10k_mac_tx_submit(ar, txmode, txpath, skb);
+       if (ret) {
+               ath10k_warn(ar, "failed to submit frame: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
 }
 
 void ath10k_offchan_tx_purge(struct ath10k *ar)
@@ -3354,12 +3514,13 @@ void ath10k_offchan_tx_work(struct work_struct *work)
        struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
        struct ath10k_peer *peer;
        struct ath10k_vif *arvif;
+       enum ath10k_hw_txrx_mode txmode;
+       enum ath10k_mac_tx_path txpath;
        struct ieee80211_hdr *hdr;
        struct ieee80211_vif *vif;
        struct ieee80211_sta *sta;
        struct sk_buff *skb;
        const u8 *peer_addr;
-       enum ath10k_hw_txrx_mode txmode;
        int vdev_id;
        int ret;
        unsigned long time_left;
@@ -3396,7 +3557,8 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                                   peer_addr, vdev_id);
 
                if (!peer) {
-                       ret = ath10k_peer_create(ar, vdev_id, peer_addr,
+                       ret = ath10k_peer_create(ar, NULL, NULL, vdev_id,
+                                                peer_addr,
                                                 WMI_PEER_TYPE_DEFAULT);
                        if (ret)
                                ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n",
@@ -3423,8 +3585,14 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                }
 
                txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+               txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
 
-               ath10k_mac_tx(ar, txmode, skb);
+               ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+               if (ret) {
+                       ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
+                                   ret);
+                       /* not serious */
+               }
 
                time_left =
                wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
@@ -3476,6 +3644,175 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
        }
 }
 
+static void ath10k_mac_txq_init(struct ieee80211_txq *txq)
+{
+       struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+       if (!txq)
+               return;
+
+       INIT_LIST_HEAD(&artxq->list);
+}
+
+static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
+{
+       struct ath10k_txq *artxq = (void *)txq->drv_priv;
+       struct ath10k_skb_cb *cb;
+       struct sk_buff *msdu;
+       int msdu_id;
+
+       if (!txq)
+               return;
+
+       spin_lock_bh(&ar->txqs_lock);
+       if (!list_empty(&artxq->list))
+               list_del_init(&artxq->list);
+       spin_unlock_bh(&ar->txqs_lock);
+
+       spin_lock_bh(&ar->htt.tx_lock);
+       idr_for_each_entry(&ar->htt.pending_tx, msdu, msdu_id) {
+               cb = ATH10K_SKB_CB(msdu);
+               if (cb->txq == txq)
+                       cb->txq = NULL;
+       }
+       spin_unlock_bh(&ar->htt.tx_lock);
+}
+
+struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
+                                           u16 peer_id,
+                                           u8 tid)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       peer = ar->peer_map[peer_id];
+       if (!peer)
+               return NULL;
+
+       if (peer->sta)
+               return peer->sta->txq[tid];
+       else if (peer->vif)
+               return peer->vif->txq;
+       else
+               return NULL;
+}
+
+static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
+                                  struct ieee80211_txq *txq)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+       /* No need to get locks */
+
+       if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH)
+               return true;
+
+       if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed)
+               return true;
+
+       if (artxq->num_fw_queued < artxq->num_push_allowed)
+               return true;
+
+       return false;
+}
+
+int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+                          struct ieee80211_txq *txq)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_htt *htt = &ar->htt;
+       struct ath10k_txq *artxq = (void *)txq->drv_priv;
+       struct ieee80211_vif *vif = txq->vif;
+       struct ieee80211_sta *sta = txq->sta;
+       enum ath10k_hw_txrx_mode txmode;
+       enum ath10k_mac_tx_path txpath;
+       struct sk_buff *skb;
+       size_t skb_len;
+       int ret;
+
+       spin_lock_bh(&ar->htt.tx_lock);
+       ret = ath10k_htt_tx_inc_pending(htt);
+       spin_unlock_bh(&ar->htt.tx_lock);
+
+       if (ret)
+               return ret;
+
+       skb = ieee80211_tx_dequeue(hw, txq);
+       if (!skb) {
+               spin_lock_bh(&ar->htt.tx_lock);
+               ath10k_htt_tx_dec_pending(htt);
+               spin_unlock_bh(&ar->htt.tx_lock);
+
+               return -ENOENT;
+       }
+
+       ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
+
+       skb_len = skb->len;
+       txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+       txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+
+       ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+       if (unlikely(ret)) {
+               ath10k_warn(ar, "failed to push frame: %d\n", ret);
+
+               spin_lock_bh(&ar->htt.tx_lock);
+               ath10k_htt_tx_dec_pending(htt);
+               spin_unlock_bh(&ar->htt.tx_lock);
+
+               return ret;
+       }
+
+       spin_lock_bh(&ar->htt.tx_lock);
+       artxq->num_fw_queued++;
+       spin_unlock_bh(&ar->htt.tx_lock);
+
+       return skb_len;
+}
+
+void ath10k_mac_tx_push_pending(struct ath10k *ar)
+{
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_txq *txq;
+       struct ath10k_txq *artxq;
+       struct ath10k_txq *last;
+       int ret;
+       int max;
+
+       spin_lock_bh(&ar->txqs_lock);
+       rcu_read_lock();
+
+       last = list_last_entry(&ar->txqs, struct ath10k_txq, list);
+       while (!list_empty(&ar->txqs)) {
+               artxq = list_first_entry(&ar->txqs, struct ath10k_txq, list);
+               txq = container_of((void *)artxq, struct ieee80211_txq,
+                                  drv_priv);
+
+               /* Prevent aggressive sta/tid taking over tx queue */
+               max = 16;
+               ret = 0;
+               while (ath10k_mac_tx_can_push(hw, txq) && max--) {
+                       ret = ath10k_mac_tx_push_txq(hw, txq);
+                       if (ret < 0)
+                               break;
+               }
+
+               list_del_init(&artxq->list);
+               if (ret != -ENOENT)
+                       list_add_tail(&artxq->list, &ar->txqs);
+
+               ath10k_htt_tx_txq_update(hw, txq);
+
+               if (artxq == last || (ret < 0 && ret != -ENOENT))
+                       break;
+       }
+
+       rcu_read_unlock();
+       spin_unlock_bh(&ar->txqs_lock);
+}
+
 /************/
 /* Scanning */
 /************/
@@ -3531,7 +3868,7 @@ static int ath10k_scan_stop(struct ath10k *ar)
                goto out;
        }
 
-       ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
+       ret = wait_for_completion_timeout(&ar->scan.completed, 3 * HZ);
        if (ret == 0) {
                ath10k_warn(ar, "failed to receive scan abortion completion: timed out\n");
                ret = -ETIMEDOUT;
@@ -3611,7 +3948,7 @@ static int ath10k_start_scan(struct ath10k *ar,
        if (ret)
                return ret;
 
-       ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
+       ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ);
        if (ret == 0) {
                ret = ath10k_scan_stop(ar);
                if (ret)
@@ -3638,66 +3975,86 @@ static int ath10k_start_scan(struct ath10k *ar,
 /* mac80211 callbacks */
 /**********************/
 
-static void ath10k_tx(struct ieee80211_hw *hw,
-                     struct ieee80211_tx_control *control,
-                     struct sk_buff *skb)
+static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
+                            struct ieee80211_tx_control *control,
+                            struct sk_buff *skb)
 {
        struct ath10k *ar = hw->priv;
-       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       struct ath10k_htt *htt = &ar->htt;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_vif *vif = info->control.vif;
        struct ieee80211_sta *sta = control->sta;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_txq *txq = NULL;
+       struct ieee80211_hdr *hdr = (void *)skb->data;
        enum ath10k_hw_txrx_mode txmode;
+       enum ath10k_mac_tx_path txpath;
+       bool is_htt;
+       bool is_mgmt;
+       bool is_presp;
+       int ret;
 
-       /* We should disable CCK RATE due to P2P */
-       if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
-               ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+       ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
 
        txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+       txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+       is_htt = (txpath == ATH10K_MAC_TX_HTT ||
+                 txpath == ATH10K_MAC_TX_HTT_MGMT);
+       is_mgmt = (txpath == ATH10K_MAC_TX_HTT_MGMT);
 
-       skb_cb->flags = 0;
-       if (!ath10k_tx_h_use_hwcrypto(vif, skb))
-               skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
-
-       if (ieee80211_is_mgmt(hdr->frame_control))
-               skb_cb->flags |= ATH10K_SKB_F_MGMT;
+       if (is_htt) {
+               spin_lock_bh(&ar->htt.tx_lock);
+               is_presp = ieee80211_is_probe_resp(hdr->frame_control);
 
-       if (ieee80211_is_data_qos(hdr->frame_control))
-               skb_cb->flags |= ATH10K_SKB_F_QOS;
-
-       skb_cb->vif = vif;
+               ret = ath10k_htt_tx_inc_pending(htt);
+               if (ret) {
+                       ath10k_warn(ar, "failed to increase tx pending count: %d, dropping\n",
+                                   ret);
+                       spin_unlock_bh(&ar->htt.tx_lock);
+                       ieee80211_free_txskb(ar->hw, skb);
+                       return;
+               }
 
-       switch (txmode) {
-       case ATH10K_HW_TXRX_MGMT:
-       case ATH10K_HW_TXRX_NATIVE_WIFI:
-               ath10k_tx_h_nwifi(hw, skb);
-               ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
-               ath10k_tx_h_seq_no(vif, skb);
-               break;
-       case ATH10K_HW_TXRX_ETHERNET:
-               ath10k_tx_h_8023(skb);
-               break;
-       case ATH10K_HW_TXRX_RAW:
-               if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
-                       WARN_ON_ONCE(1);
-                       ieee80211_free_txskb(hw, skb);
+               ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt, is_presp);
+               if (ret) {
+                       ath10k_dbg(ar, ATH10K_DBG_MAC, "failed to increase tx mgmt pending count: %d, dropping\n",
+                                  ret);
+                       ath10k_htt_tx_dec_pending(htt);
+                       spin_unlock_bh(&ar->htt.tx_lock);
+                       ieee80211_free_txskb(ar->hw, skb);
                        return;
                }
+               spin_unlock_bh(&ar->htt.tx_lock);
        }
 
-       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
-               if (!ath10k_mac_tx_frm_has_freq(ar)) {
-                       ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
-                                  skb);
-
-                       skb_queue_tail(&ar->offchan_tx_queue, skb);
-                       ieee80211_queue_work(hw, &ar->offchan_tx_work);
-                       return;
+       ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+       if (ret) {
+               ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
+               if (is_htt) {
+                       spin_lock_bh(&ar->htt.tx_lock);
+                       ath10k_htt_tx_dec_pending(htt);
+                       if (is_mgmt)
+                               ath10k_htt_tx_mgmt_dec_pending(htt);
+                       spin_unlock_bh(&ar->htt.tx_lock);
                }
+               return;
        }
+}
+
+static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
+                                       struct ieee80211_txq *txq)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+       spin_lock_bh(&ar->txqs_lock);
+       if (list_empty(&artxq->list))
+               list_add_tail(&artxq->list, &ar->txqs);
+       spin_unlock_bh(&ar->txqs_lock);
 
-       ath10k_mac_tx(ar, txmode, skb);
+       if (ath10k_mac_tx_can_push(hw, txq))
+               tasklet_schedule(&ar->htt.txrx_compl_task);
+
+       ath10k_htt_tx_txq_update(hw, txq);
 }
 
 /* Must not be called with conf_mutex held as workers can use that also. */
@@ -4021,7 +4378,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
                goto err_off;
        }
 
-       ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
+       ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL,
+                               &ar->normal_mode_fw);
        if (ret) {
                ath10k_err(ar, "Could not init core: %d\n", ret);
                goto err_power_down;
@@ -4079,7 +4437,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
        }
 
        if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA,
-                    ar->fw_features)) {
+                    ar->running_fw->fw_file.fw_features)) {
                ret = ath10k_wmi_pdev_enable_adaptive_cca(ar, 1,
                                                          WMI_CCA_DETECT_LEVEL_AUTO,
                                                          WMI_CCA_DETECT_MARGIN_AUTO);
@@ -4100,7 +4458,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
 
        ar->ani_enabled = true;
 
-       if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
+       if (ath10k_peer_stats_enabled(ar)) {
                param = ar->wmi.pdev_param->peer_stats_update_period;
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                PEER_DEFAULT_STATS_UPDATE_PERIOD);
@@ -4313,6 +4671,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_peer *peer;
        enum wmi_sta_powersave_param param;
        int ret = 0;
        u32 value;
@@ -4325,6 +4684,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        mutex_lock(&ar->conf_mutex);
 
        memset(arvif, 0, sizeof(*arvif));
+       ath10k_mac_txq_init(vif->txq);
 
        arvif->ar = ar;
        arvif->vif = vif;
@@ -4489,7 +4849,10 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                goto err_vdev_delete;
        }
 
-       if (ar->cfg_tx_chainmask) {
+       /* Configuring number of spatial stream for monitor interface is causing
+        * target assert in qca9888 and qca6174.
+        */
+       if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) {
                u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
 
                vdev_param = ar->wmi.vdev_param->nss;
@@ -4505,13 +4868,31 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
            arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
-               ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr,
-                                        WMI_PEER_TYPE_DEFAULT);
+               ret = ath10k_peer_create(ar, vif, NULL, arvif->vdev_id,
+                                        vif->addr, WMI_PEER_TYPE_DEFAULT);
                if (ret) {
                        ath10k_warn(ar, "failed to create vdev %i peer for AP/IBSS: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_vdev_delete;
                }
+
+               spin_lock_bh(&ar->data_lock);
+
+               peer = ath10k_peer_find(ar, arvif->vdev_id, vif->addr);
+               if (!peer) {
+                       ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
+                                   vif->addr, arvif->vdev_id);
+                       spin_unlock_bh(&ar->data_lock);
+                       ret = -ENOENT;
+                       goto err_peer_delete;
+               }
+
+               arvif->peer_id = find_first_bit(peer->peer_ids,
+                                               ATH10K_MAX_NUM_PEER_IDS);
+
+               spin_unlock_bh(&ar->data_lock);
+       } else {
+               arvif->peer_id = HTT_INVALID_PEERID;
        }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
@@ -4622,7 +5003,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_peer *peer;
        int ret;
+       int i;
 
        cancel_work_sync(&arvif->ap_csa_work);
        cancel_delayed_work_sync(&arvif->connection_loss_work);
@@ -4676,7 +5059,22 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
                spin_unlock_bh(&ar->data_lock);
        }
 
+       spin_lock_bh(&ar->data_lock);
+       for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+               peer = ar->peer_map[i];
+               if (!peer)
+                       continue;
+
+               if (peer->vif == vif) {
+                       ath10k_warn(ar, "found vif peer %pM entry on vdev %i after it was supposedly removed\n",
+                                   vif->addr, arvif->vdev_id);
+                       peer->vif = NULL;
+               }
+       }
+       spin_unlock_bh(&ar->data_lock);
+
        ath10k_peer_cleanup(ar, arvif->vdev_id);
+       ath10k_mac_txq_unref(ar, vif->txq);
 
        if (vif->type == NL80211_IFTYPE_MONITOR) {
                ar->monitor_arvif = NULL;
@@ -4689,6 +5087,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        ath10k_mac_vif_tx_unlock_all(arvif);
        spin_unlock_bh(&ar->htt.tx_lock);
 
+       ath10k_mac_txq_unref(ar, vif->txq);
+
        mutex_unlock(&ar->conf_mutex);
 }
 
@@ -5393,13 +5793,18 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+       struct ath10k_peer *peer;
        int ret = 0;
+       int i;
 
        if (old_state == IEEE80211_STA_NOTEXIST &&
            new_state == IEEE80211_STA_NONE) {
                memset(arsta, 0, sizeof(*arsta));
                arsta->arvif = arvif;
                INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+
+               for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+                       ath10k_mac_txq_init(sta->txq[i]);
        }
 
        /* cancel must be done outside the mutex to avoid deadlock */
@@ -5434,8 +5839,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                if (sta->tdls)
                        peer_type = WMI_PEER_TYPE_TDLS;
 
-               ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr,
-                                        peer_type);
+               ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
+                                        sta->addr, peer_type);
                if (ret) {
                        ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
@@ -5443,6 +5848,24 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        goto exit;
                }
 
+               spin_lock_bh(&ar->data_lock);
+
+               peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
+               if (!peer) {
+                       ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
+                                   vif->addr, arvif->vdev_id);
+                       spin_unlock_bh(&ar->data_lock);
+                       ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+                       ath10k_mac_dec_num_stations(arvif, sta);
+                       ret = -ENOENT;
+                       goto exit;
+               }
+
+               arsta->peer_id = find_first_bit(peer->peer_ids,
+                                               ATH10K_MAX_NUM_PEER_IDS);
+
+               spin_unlock_bh(&ar->data_lock);
+
                if (!sta->tdls)
                        goto exit;
 
@@ -5505,6 +5928,23 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 
                ath10k_mac_dec_num_stations(arvif, sta);
 
+               spin_lock_bh(&ar->data_lock);
+               for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+                       peer = ar->peer_map[i];
+                       if (!peer)
+                               continue;
+
+                       if (peer->sta == sta) {
+                               ath10k_warn(ar, "found sta peer %pM entry on vdev %i after it was supposedly removed\n",
+                                           sta->addr, arvif->vdev_id);
+                               peer->sta = NULL;
+                       }
+               }
+               spin_unlock_bh(&ar->data_lock);
+
+               for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+                       ath10k_mac_txq_unref(ar, sta->txq[i]);
+
                if (!sta->tdls)
                        goto exit;
 
@@ -5751,7 +6191,7 @@ exit:
        return ret;
 }
 
-#define ATH10K_ROC_TIMEOUT_HZ (2*HZ)
+#define ATH10K_ROC_TIMEOUT_HZ (2 * HZ)
 
 static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
@@ -5815,7 +6255,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
                goto exit;
        }
 
-       ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
+       ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ);
        if (ret == 0) {
                ath10k_warn(ar, "failed to switch to channel for roc scan\n");
 
@@ -6379,6 +6819,32 @@ static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        return 0;
 }
 
+static void ath10k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          u64 tsf)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 tsf_offset, vdev_param = ar->wmi.vdev_param->set_tsf;
+       int ret;
+
+       /* Workaround:
+        *
+        * Given tsf argument is entire TSF value, but firmware accepts
+        * only TSF offset to current TSF.
+        *
+        * get_tsf function is used to get offset value, however since
+        * ath10k_get_tsf is not implemented properly, it will return 0 always.
+        * Luckily all the caller functions to set_tsf, as of now, also rely on
+        * get_tsf function to get entire tsf value such get_tsf() + tsf_delta,
+        * final tsf offset value to firmware will be arithmetically correct.
+        */
+       tsf_offset = tsf - ath10k_get_tsf(hw, vif);
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       vdev_param, tsf_offset);
+       if (ret && ret != -EOPNOTSUPP)
+               ath10k_warn(ar, "failed to set tsf offset: %d\n", ret);
+}
+
 static int ath10k_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct ieee80211_ampdu_params *params)
@@ -6450,7 +6916,13 @@ ath10k_mac_update_rx_channel(struct ath10k *ar,
                        def = &vifs[0].new_ctx->def;
 
                ar->rx_channel = def->chan;
-       } else if (ctx && ath10k_mac_num_chanctxs(ar) == 0) {
+       } else if ((ctx && ath10k_mac_num_chanctxs(ar) == 0) ||
+                  (ctx && (ar->state == ATH10K_STATE_RESTARTED))) {
+               /* During driver restart due to firmware assert, since mac80211
+                * already has valid channel context for given radio, channel
+                * context iteration return num_chanctx > 0. So fix rx_channel
+                * when restart is in progress.
+                */
                ar->rx_channel = ctx->def.chan;
        } else {
                ar->rx_channel = NULL;
@@ -6807,7 +7279,8 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
 }
 
 static const struct ieee80211_ops ath10k_ops = {
-       .tx                             = ath10k_tx,
+       .tx                             = ath10k_mac_op_tx,
+       .wake_tx_queue                  = ath10k_mac_op_wake_tx_queue,
        .start                          = ath10k_start,
        .stop                           = ath10k_stop,
        .config                         = ath10k_config,
@@ -6834,6 +7307,7 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_bitrate_mask               = ath10k_mac_op_set_bitrate_mask,
        .sta_rc_update                  = ath10k_sta_rc_update,
        .get_tsf                        = ath10k_get_tsf,
+       .set_tsf                        = ath10k_set_tsf,
        .ampdu_action                   = ath10k_ampdu_action,
        .get_et_sset_count              = ath10k_debug_get_et_sset_count,
        .get_et_stats                   = ath10k_debug_get_et_stats,
@@ -7222,7 +7696,7 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->available_antennas_rx = ar->cfg_rx_chainmask;
        ar->hw->wiphy->available_antennas_tx = ar->cfg_tx_chainmask;
 
-       if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
+       if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->normal_mode_fw.fw_file.fw_features))
                ar->hw->wiphy->interface_modes |=
                        BIT(NL80211_IFTYPE_P2P_DEVICE) |
                        BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -7262,6 +7736,7 @@ int ath10k_mac_register(struct ath10k *ar)
 
        ar->hw->vif_data_size = sizeof(struct ath10k_vif);
        ar->hw->sta_data_size = sizeof(struct ath10k_sta);
+       ar->hw->txq_data_size = sizeof(struct ath10k_txq);
 
        ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
@@ -7286,7 +7761,8 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->max_remain_on_channel_duration = 5000;
 
        ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
-       ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
+       ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+                                  NL80211_FEATURE_AP_SCAN;
 
        ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations;
 
@@ -7310,7 +7786,7 @@ int ath10k_mac_register(struct ath10k *ar)
         */
        ar->hw->offchannel_tx_hw_queue = IEEE80211_MAX_QUEUES - 1;
 
-       switch (ar->wmi.op_version) {
+       switch (ar->running_fw->fw_file.wmi_op_version) {
        case ATH10K_FW_WMI_OP_VERSION_MAIN:
                ar->hw->wiphy->iface_combinations = ath10k_if_comb;
                ar->hw->wiphy->n_iface_combinations =