This repository has been archived on 2024-05-11. You can view files and clone it, but cannot push or open issues or pull requests.
gluon-firmware/patches/openwrt/0017-mac80211-update-ath10k...

33656 lines
1.0 MiB

From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Tue, 10 Mar 2015 12:40:53 +0100
Subject: mac80211: update ath10k to compat-wireless-2015-03-05
Taken from http://openwrt.reigndropsfall.net/
diff --git a/package/kernel/mac80211/patches/917-mac80211-rx-reordering.patch b/package/kernel/mac80211/patches/917-mac80211-rx-reordering.patch
new file mode 100644
index 0000000..1d0c559
--- /dev/null
+++ b/package/kernel/mac80211/patches/917-mac80211-rx-reordering.patch
@@ -0,0 +1,271 @@
+commit 08cf42e843f9a7e253502011c81677f61f7e5c42
+Author: Michal Kazior <michal.kazior@tieto.com>
+Date: Wed Jul 16 12:12:15 2014 +0200
+
+ mac80211: add support for Rx reordering offloading
+
+ Some drivers may be performing most of Tx/Rx
+ aggregation on their own (e.g. in firmware)
+ including AddBa/DelBa negotiations but may
+ otherwise require Rx reordering assistance.
+
+ The patch exports 2 new functions for establishing
+ Rx aggregation sessions in assumption device
+ driver has taken care of the necessary
+ negotiations.
+
+ Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
+ [fix endian bug]
+ Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4481,6 +4481,40 @@ void ieee80211_stop_rx_ba_session(struct
+ */
+ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
+
++/**
++ * ieee80211_start_rx_ba_session_offl - start a Rx BA session
++ *
++ * Some device drivers may offload part of the Rx aggregation flow including
++ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
++ * reordering.
++ *
++ * Create structures responsible for reordering so device drivers may call here
++ * when they complete AddBa negotiation.
++ *
++ * @vif: &struct ieee80211_vif pointer from the add_interface callback
++ * @addr: station mac address
++ * @tid: the rx tid
++ */
++void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
++ const u8 *addr, u16 tid);
++
++/**
++ * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
++ *
++ * Some device drivers may offload part of the Rx aggregation flow including
++ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
++ * reordering.
++ *
++ * Destroy structures responsible for reordering so device drivers may call here
++ * when they complete DelBa negotiation.
++ *
++ * @vif: &struct ieee80211_vif pointer from the add_interface callback
++ * @addr: station mac address
++ * @tid: the rx tid
++ */
++void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
++ const u8 *addr, u16 tid);
++
+ /* Rate control API */
+
+ /**
+--- a/net/mac80211/agg-rx.c
++++ b/net/mac80211/agg-rx.c
+@@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(st
+ ieee80211_tx_skb(sdata, skb);
+ }
+
+-void ieee80211_process_addba_request(struct ieee80211_local *local,
+- struct sta_info *sta,
+- struct ieee80211_mgmt *mgmt,
+- size_t len)
++void __ieee80211_start_rx_ba_session(struct sta_info *sta,
++ u8 dialog_token, u16 timeout,
++ u16 start_seq_num, u16 ba_policy, u16 tid,
++ u16 buf_size, bool tx)
+ {
++ struct ieee80211_local *local = sta->sdata->local;
+ struct tid_ampdu_rx *tid_agg_rx;
+- u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
+- u8 dialog_token;
+ int ret = -EOPNOTSUPP;
+-
+- /* extract session parameters from addba request frame */
+- dialog_token = mgmt->u.action.u.addba_req.dialog_token;
+- timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+- start_seq_num =
+- le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
+-
+- capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+- ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
+- tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+- buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+-
+- status = WLAN_STATUS_REQUEST_DECLINED;
++ u16 status = WLAN_STATUS_REQUEST_DECLINED;
+
+ if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
+ ht_dbg(sta->sdata,
+@@ -264,7 +251,7 @@ void ieee80211_process_addba_request(str
+ status = WLAN_STATUS_INVALID_QOS_PARAM;
+ ht_dbg_ratelimited(sta->sdata,
+ "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
+- mgmt->sa, tid, ba_policy, buf_size);
++ sta->sta.addr, tid, ba_policy, buf_size);
+ goto end_no_lock;
+ }
+ /* determine default buffer size */
+@@ -281,7 +268,7 @@ void ieee80211_process_addba_request(str
+ if (sta->ampdu_mlme.tid_rx[tid]) {
+ ht_dbg_ratelimited(sta->sdata,
+ "unexpected AddBA Req from %pM on tid %u\n",
+- mgmt->sa, tid);
++ sta->sta.addr, tid);
+
+ /* delete existing Rx BA session on the same tid */
+ ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
+@@ -350,6 +337,74 @@ end:
+ mutex_unlock(&sta->ampdu_mlme.mtx);
+
+ end_no_lock:
+- ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
+- dialog_token, status, 1, buf_size, timeout);
++ if (tx)
++ ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
++ dialog_token, status, 1, buf_size,
++ timeout);
++}
++
++void ieee80211_process_addba_request(struct ieee80211_local *local,
++ struct sta_info *sta,
++ struct ieee80211_mgmt *mgmt,
++ size_t len)
++{
++ u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
++ u8 dialog_token;
++
++ /* extract session parameters from addba request frame */
++ dialog_token = mgmt->u.action.u.addba_req.dialog_token;
++ timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
++ start_seq_num =
++ le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
++
++ capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
++ ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
++ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
++ buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
++
++ __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
++ start_seq_num, ba_policy, tid,
++ buf_size, true);
++}
++
++void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
++ const u8 *addr, u16 tid)
++{
++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_rx_agg *rx_agg;
++ struct sk_buff *skb = dev_alloc_skb(0);
++
++ if (unlikely(!skb))
++ return;
++
++ rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
++ memcpy(&rx_agg->addr, addr, ETH_ALEN);
++ rx_agg->tid = tid;
++
++ skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
++ skb_queue_tail(&sdata->skb_queue, skb);
++ ieee80211_queue_work(&local->hw, &sdata->work);
++}
++EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);
++
++void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
++ const u8 *addr, u16 tid)
++{
++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
++ struct ieee80211_local *local = sdata->local;
++ struct ieee80211_rx_agg *rx_agg;
++ struct sk_buff *skb = dev_alloc_skb(0);
++
++ if (unlikely(!skb))
++ return;
++
++ rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
++ memcpy(&rx_agg->addr, addr, ETH_ALEN);
++ rx_agg->tid = tid;
++
++ skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
++ skb_queue_tail(&sdata->skb_queue, skb);
++ ieee80211_queue_work(&local->hw, &sdata->work);
+ }
++EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -902,10 +902,17 @@ ieee80211_vif_get_shift(struct ieee80211
+ return shift;
+ }
+
++struct ieee80211_rx_agg {
++ u8 addr[ETH_ALEN];
++ u16 tid;
++};
++
+ enum sdata_queue_type {
+ IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
+ IEEE80211_SDATA_QUEUE_AGG_START = 1,
+ IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
++ IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
++ IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
+ };
+
+ enum {
+@@ -1554,6 +1561,10 @@ void ___ieee80211_stop_rx_ba_session(str
+ u16 initiator, u16 reason, bool stop);
+ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
+ u16 initiator, u16 reason, bool stop);
++void __ieee80211_start_rx_ba_session(struct sta_info *sta,
++ u8 dialog_token, u16 timeout,
++ u16 start_seq_num, u16 ba_policy, u16 tid,
++ u16 buf_size, bool tx);
+ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
+ enum ieee80211_agg_stop_reason reason);
+ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -1154,6 +1154,7 @@ static void ieee80211_iface_work(struct
+ struct sk_buff *skb;
+ struct sta_info *sta;
+ struct ieee80211_ra_tid *ra_tid;
++ struct ieee80211_rx_agg *rx_agg;
+
+ if (!ieee80211_sdata_running(sdata))
+ return;
+@@ -1181,6 +1182,34 @@ static void ieee80211_iface_work(struct
+ ra_tid = (void *)&skb->cb;
+ ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
+ ra_tid->tid);
++ } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
++ rx_agg = (void *)&skb->cb;
++ mutex_lock(&local->sta_mtx);
++ sta = sta_info_get_bss(sdata, rx_agg->addr);
++ if (sta) {
++ u16 last_seq;
++
++ last_seq = le16_to_cpu(
++ sta->last_seq_ctrl[rx_agg->tid]);
++
++ __ieee80211_start_rx_ba_session(sta,
++ 0, 0,
++ ieee80211_sn_inc(last_seq),
++ 1, rx_agg->tid,
++ IEEE80211_MAX_AMPDU_BUF,
++ false);
++ }
++ mutex_unlock(&local->sta_mtx);
++ } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
++ rx_agg = (void *)&skb->cb;
++ mutex_lock(&local->sta_mtx);
++ sta = sta_info_get_bss(sdata, rx_agg->addr);
++ if (sta)
++ __ieee80211_stop_rx_ba_session(sta,
++ rx_agg->tid,
++ WLAN_BACK_RECIPIENT, 0,
++ false);
++ mutex_unlock(&local->sta_mtx);
+ } else if (ieee80211_is_action(mgmt->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_BACK) {
+ int len = skb->len;
diff --git a/package/kernel/mac80211/patches/918-ath-spectral-debugfs.patch b/package/kernel/mac80211/patches/918-ath-spectral-debugfs.patch
new file mode 100644
index 0000000..d0c1bbd
--- /dev/null
+++ b/package/kernel/mac80211/patches/918-ath-spectral-debugfs.patch
@@ -0,0 +1,192 @@
+--- a/drivers/net/wireless/ath/ath9k/spectral.h
++++ b/drivers/net/wireless/ath/ath9k/spectral.h
+@@ -17,6 +17,8 @@
+ #ifndef SPECTRAL_H
+ #define SPECTRAL_H
+
++#include "../spectral_common.h"
++
+ /* enum spectral_mode:
+ *
+ * @SPECTRAL_DISABLED: spectral mode is disabled
+@@ -54,8 +56,6 @@ struct ath_ht20_mag_info {
+ u8 max_exp;
+ } __packed;
+
+-#define SPECTRAL_HT20_NUM_BINS 56
+-
+ /* WARNING: don't actually use this struct! MAC may vary the amount of
+ * data by -1/+2. This struct is for reference only.
+ */
+@@ -83,8 +83,6 @@ struct ath_ht20_40_mag_info {
+ u8 max_exp;
+ } __packed;
+
+-#define SPECTRAL_HT20_40_NUM_BINS 128
+-
+ /* WARNING: don't actually use this struct! MAC may vary the amount of
+ * data. This struct is for reference only.
+ */
+@@ -125,71 +123,6 @@ static inline u8 spectral_bitmap_weight(
+ return bins[0] & 0x3f;
+ }
+
+-/* FFT sample format given to userspace via debugfs.
+- *
+- * Please keep the type/length at the front position and change
+- * other fields after adding another sample type
+- *
+- * TODO: this might need rework when switching to nl80211-based
+- * interface.
+- */
+-enum ath_fft_sample_type {
+- ATH_FFT_SAMPLE_HT20 = 1,
+- ATH_FFT_SAMPLE_HT20_40,
+-};
+-
+-struct fft_sample_tlv {
+- u8 type; /* see ath_fft_sample */
+- __be16 length;
+- /* type dependent data follows */
+-} __packed;
+-
+-struct fft_sample_ht20 {
+- struct fft_sample_tlv tlv;
+-
+- u8 max_exp;
+-
+- __be16 freq;
+- s8 rssi;
+- s8 noise;
+-
+- __be16 max_magnitude;
+- u8 max_index;
+- u8 bitmap_weight;
+-
+- __be64 tsf;
+-
+- u8 data[SPECTRAL_HT20_NUM_BINS];
+-} __packed;
+-
+-struct fft_sample_ht20_40 {
+- struct fft_sample_tlv tlv;
+-
+- u8 channel_type;
+- __be16 freq;
+-
+- s8 lower_rssi;
+- s8 upper_rssi;
+-
+- __be64 tsf;
+-
+- s8 lower_noise;
+- s8 upper_noise;
+-
+- __be16 lower_max_magnitude;
+- __be16 upper_max_magnitude;
+-
+- u8 lower_max_index;
+- u8 upper_max_index;
+-
+- u8 lower_bitmap_weight;
+- u8 upper_bitmap_weight;
+-
+- u8 max_exp;
+-
+- u8 data[SPECTRAL_HT20_40_NUM_BINS];
+-} __packed;
+-
+ void ath9k_spectral_init_debug(struct ath_softc *sc);
+ void ath9k_spectral_deinit_debug(struct ath_softc *sc);
+
+--- /dev/null
++++ b/drivers/net/wireless/ath/spectral_common.h
+@@ -0,0 +1,88 @@
++/*
++ * Copyright (c) 2013 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef SPECTRAL_COMMON_H
++#define SPECTRAL_COMMON_H
++
++#define SPECTRAL_HT20_NUM_BINS 56
++#define SPECTRAL_HT20_40_NUM_BINS 128
++
++/* FFT sample format given to userspace via debugfs.
++ *
++ * Please keep the type/length at the front position and change
++ * other fields after adding another sample type
++ *
++ * TODO: this might need rework when switching to nl80211-based
++ * interface.
++ */
++enum ath_fft_sample_type {
++ ATH_FFT_SAMPLE_HT20 = 1,
++ ATH_FFT_SAMPLE_HT20_40,
++};
++
++struct fft_sample_tlv {
++ u8 type; /* see ath_fft_sample */
++ __be16 length;
++ /* type dependent data follows */
++} __packed;
++
++struct fft_sample_ht20 {
++ struct fft_sample_tlv tlv;
++
++ u8 max_exp;
++
++ __be16 freq;
++ s8 rssi;
++ s8 noise;
++
++ __be16 max_magnitude;
++ u8 max_index;
++ u8 bitmap_weight;
++
++ __be64 tsf;
++
++ u8 data[SPECTRAL_HT20_NUM_BINS];
++} __packed;
++
++struct fft_sample_ht20_40 {
++ struct fft_sample_tlv tlv;
++
++ u8 channel_type;
++ __be16 freq;
++
++ s8 lower_rssi;
++ s8 upper_rssi;
++
++ __be64 tsf;
++
++ s8 lower_noise;
++ s8 upper_noise;
++
++ __be16 lower_max_magnitude;
++ __be16 upper_max_magnitude;
++
++ u8 lower_max_index;
++ u8 upper_max_index;
++
++ u8 lower_bitmap_weight;
++ u8 upper_bitmap_weight;
++
++ u8 max_exp;
++
++ u8 data[SPECTRAL_HT20_40_NUM_BINS];
++} __packed;
++
++#endif /* SPECTRAL_COMMON_H */
diff --git a/package/kernel/mac80211/patches/919-update-ath10k.patch b/package/kernel/mac80211/patches/919-update-ath10k.patch
new file mode 100644
index 0000000..45fccb8
--- /dev/null
+++ b/package/kernel/mac80211/patches/919-update-ath10k.patch
@@ -0,0 +1,33023 @@
+--- a/drivers/net/wireless/ath/ath10k/Kconfig
++++ b/drivers/net/wireless/ath/ath10k/Kconfig
+@@ -26,13 +26,15 @@ config ATH10K_DEBUG
+
+ config ATH10K_DEBUGFS
+ bool "Atheros ath10k debugfs support"
+- depends on ATH10K
++ depends on ATH10K && DEBUG_FS
++ depends on RELAY
+ ---help---
+ Enabled debugfs support
+
+ If unsure, say Y to make it easier to debug problems.
+
+ config ATH10K_TRACING
++ depends on !KERNEL_3_4
+ bool "Atheros ath10k tracing support"
+ depends on ATH10K
+ depends on EVENT_TRACING
+--- a/drivers/net/wireless/ath/ath10k/Makefile
++++ b/drivers/net/wireless/ath/ath10k/Makefile
+@@ -8,9 +8,15 @@ ath10k_core-y += mac.o \
+ htt_tx.o \
+ txrx.o \
+ wmi.o \
+- bmi.o
++ wmi-tlv.o \
++ bmi.o \
++ hw.o
+
++ath10k_core-$(CPTCFG_ATH10K_DEBUGFS) += spectral.o
++ath10k_core-$(CPTCFG_NL80211_TESTMODE) += testmode.o
+ ath10k_core-$(CPTCFG_ATH10K_TRACING) += trace.o
++ath10k_core-$(CONFIG_THERMAL) += thermal.o
++ath10k_core-$(CPTCFG_MAC80211_DEBUGFS) += debugfs_sta.o
+
+ obj-$(CPTCFG_ATH10K_PCI) += ath10k_pci.o
+ ath10k_pci-y += pci.o \
+--- a/drivers/net/wireless/ath/ath10k/bmi.c
++++ b/drivers/net/wireless/ath/ath10k/bmi.c
+@@ -22,7 +22,7 @@
+
+ void ath10k_bmi_start(struct ath10k *ar)
+ {
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n");
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");
+
+ ar->bmi.done_sent = false;
+ }
+@@ -33,10 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar)
+ u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n");
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");
+
+ if (ar->bmi.done_sent) {
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n");
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");
+ return 0;
+ }
+
+@@ -45,7 +45,7 @@ int ath10k_bmi_done(struct ath10k *ar)
+
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+ if (ret) {
+- ath10k_warn("unable to write to the device: %d\n", ret);
++ ath10k_warn(ar, "unable to write to the device: %d\n", ret);
+ return ret;
+ }
+
+@@ -61,10 +61,10 @@ int ath10k_bmi_get_target_info(struct at
+ u32 resplen = sizeof(resp.get_target_info);
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n");
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");
+
+ if (ar->bmi.done_sent) {
+- ath10k_warn("BMI Get Target Info Command disallowed\n");
++ ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
+ return -EBUSY;
+ }
+
+@@ -72,12 +72,12 @@ int ath10k_bmi_get_target_info(struct at
+
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+ if (ret) {
+- ath10k_warn("unable to get target info from device\n");
++ ath10k_warn(ar, "unable to get target info from device\n");
+ return ret;
+ }
+
+ if (resplen < sizeof(resp.get_target_info)) {
+- ath10k_warn("invalid get_target_info response length (%d)\n",
++ ath10k_warn(ar, "invalid get_target_info response length (%d)\n",
+ resplen);
+ return -EIO;
+ }
+@@ -97,11 +97,11 @@ int ath10k_bmi_read_memory(struct ath10k
+ u32 rxlen;
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
+ address, length);
+
+ if (ar->bmi.done_sent) {
+- ath10k_warn("command disallowed\n");
++ ath10k_warn(ar, "command disallowed\n");
+ return -EBUSY;
+ }
+
+@@ -115,7 +115,7 @@ int ath10k_bmi_read_memory(struct ath10k
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
+ &resp, &rxlen);
+ if (ret) {
+- ath10k_warn("unable to read from the device (%d)\n",
++ ath10k_warn(ar, "unable to read from the device (%d)\n",
+ ret);
+ return ret;
+ }
+@@ -137,11 +137,11 @@ int ath10k_bmi_write_memory(struct ath10
+ u32 txlen;
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
+ address, length);
+
+ if (ar->bmi.done_sent) {
+- ath10k_warn("command disallowed\n");
++ ath10k_warn(ar, "command disallowed\n");
+ return -EBUSY;
+ }
+
+@@ -159,7 +159,7 @@ int ath10k_bmi_write_memory(struct ath10
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+ NULL, NULL);
+ if (ret) {
+- ath10k_warn("unable to write to the device (%d)\n",
++ ath10k_warn(ar, "unable to write to the device (%d)\n",
+ ret);
+ return ret;
+ }
+@@ -183,11 +183,11 @@ int ath10k_bmi_execute(struct ath10k *ar
+ u32 resplen = sizeof(resp.execute);
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
+ address, param);
+
+ if (ar->bmi.done_sent) {
+- ath10k_warn("command disallowed\n");
++ ath10k_warn(ar, "command disallowed\n");
+ return -EBUSY;
+ }
+
+@@ -197,19 +197,19 @@ int ath10k_bmi_execute(struct ath10k *ar
+
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+ if (ret) {
+- ath10k_warn("unable to read from the device\n");
++ ath10k_warn(ar, "unable to read from the device\n");
+ return ret;
+ }
+
+ if (resplen < sizeof(resp.execute)) {
+- ath10k_warn("invalid execute response length (%d)\n",
++ ath10k_warn(ar, "invalid execute response length (%d)\n",
+ resplen);
+ return -EIO;
+ }
+
+ *result = __le32_to_cpu(resp.execute.result);
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
+
+ return 0;
+ }
+@@ -221,11 +221,11 @@ int ath10k_bmi_lz_data(struct ath10k *ar
+ u32 txlen;
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",
+ buffer, length);
+
+ if (ar->bmi.done_sent) {
+- ath10k_warn("command disallowed\n");
++ ath10k_warn(ar, "command disallowed\n");
+ return -EBUSY;
+ }
+
+@@ -241,7 +241,7 @@ int ath10k_bmi_lz_data(struct ath10k *ar
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+ NULL, NULL);
+ if (ret) {
+- ath10k_warn("unable to write to the device\n");
++ ath10k_warn(ar, "unable to write to the device\n");
+ return ret;
+ }
+
+@@ -258,11 +258,11 @@ int ath10k_bmi_lz_stream_start(struct at
+ u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
++ ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
+ address);
+
+ if (ar->bmi.done_sent) {
+- ath10k_warn("command disallowed\n");
++ ath10k_warn(ar, "command disallowed\n");
+ return -EBUSY;
+ }
+
+@@ -271,7 +271,7 @@ int ath10k_bmi_lz_stream_start(struct at
+
+ ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+ if (ret) {
+- ath10k_warn("unable to Start LZ Stream to the device\n");
++ ath10k_warn(ar, "unable to Start LZ Stream to the device\n");
+ return ret;
+ }
+
+@@ -286,7 +286,7 @@ int ath10k_bmi_fast_download(struct ath1
+ u32 trailer_len = length - head_len;
+ int ret;
+
+- ath10k_dbg(ATH10K_DBG_BMI,
++ ath10k_dbg(ar, ATH10K_DBG_BMI,
+ "bmi fast download address 0x%x buffer 0x%p length %d\n",
+ address, buffer, length);
+
+--- a/drivers/net/wireless/ath/ath10k/bmi.h
++++ b/drivers/net/wireless/ath/ath10k/bmi.h
+@@ -177,7 +177,6 @@ struct bmi_target_info {
+ u32 type;
+ };
+
+-
+ /* in msec */
+ #define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ)
+
+@@ -201,7 +200,8 @@ int ath10k_bmi_write_memory(struct ath10
+ \
+ addr = host_interest_item_address(HI_ITEM(item)); \
+ ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
+- *val = __le32_to_cpu(tmp); \
++ if (!ret) \
++ *val = __le32_to_cpu(tmp); \
+ ret; \
+ })
+
+--- a/drivers/net/wireless/ath/ath10k/ce.c
++++ b/drivers/net/wireless/ath/ath10k/ce.c
+@@ -260,7 +260,6 @@ static inline void ath10k_ce_engine_int_
+ ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+ }
+
+-
+ /*
+ * Guts of ath10k_ce_send, used by both ath10k_ce_send and
+ * ath10k_ce_sendlist_send.
+@@ -284,13 +283,9 @@ int ath10k_ce_send_nolock(struct ath10k_
+ int ret = 0;
+
+ if (nbytes > ce_state->src_sz_max)
+- ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
++ ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
+ __func__, nbytes, ce_state->src_sz_max);
+
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return ret;
+-
+ if (unlikely(CE_RING_DELTA(nentries_mask,
+ write_index, sw_index - 1) <= 0)) {
+ ret = -ENOSR;
+@@ -325,10 +320,36 @@ int ath10k_ce_send_nolock(struct ath10k_
+
+ src_ring->write_index = write_index;
+ exit:
+- ath10k_pci_sleep(ar);
+ return ret;
+ }
+
++void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
++{
++ struct ath10k *ar = pipe->ar;
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct ath10k_ce_ring *src_ring = pipe->src_ring;
++ u32 ctrl_addr = pipe->ctrl_addr;
++
++ lockdep_assert_held(&ar_pci->ce_lock);
++
++ /*
++ * This function must be called only if there is an incomplete
++ * scatter-gather transfer (before index register is updated)
++ * that needs to be cleaned up.
++ */
++ if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index))
++ return;
++
++ if (WARN_ON_ONCE(src_ring->write_index ==
++ ath10k_ce_src_ring_write_index_get(ar, ctrl_addr)))
++ return;
++
++ src_ring->write_index--;
++ src_ring->write_index &= src_ring->nentries_mask;
++
++ src_ring->per_transfer_context[src_ring->write_index] = NULL;
++}
++
+ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
+ void *per_transfer_context,
+ u32 buffer,
+@@ -363,49 +384,56 @@ int ath10k_ce_num_free_src_entries(struc
+ return delta;
+ }
+
+-int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
+- void *per_recv_context,
+- u32 buffer)
++int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe)
+ {
+- struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
+- u32 ctrl_addr = ce_state->ctrl_addr;
+- struct ath10k *ar = ce_state->ar;
++ struct ath10k *ar = pipe->ar;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
+ unsigned int nentries_mask = dest_ring->nentries_mask;
+- unsigned int write_index;
+- unsigned int sw_index;
+- int ret;
++ unsigned int write_index = dest_ring->write_index;
++ unsigned int sw_index = dest_ring->sw_index;
+
+- spin_lock_bh(&ar_pci->ce_lock);
+- write_index = dest_ring->write_index;
+- sw_index = dest_ring->sw_index;
++ lockdep_assert_held(&ar_pci->ce_lock);
+
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- goto out;
++ return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
++}
+
+- if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
+- struct ce_desc *base = dest_ring->base_addr_owner_space;
+- struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
++int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
++{
++ struct ath10k *ar = pipe->ar;
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
++ unsigned int nentries_mask = dest_ring->nentries_mask;
++ unsigned int write_index = dest_ring->write_index;
++ unsigned int sw_index = dest_ring->sw_index;
++ struct ce_desc *base = dest_ring->base_addr_owner_space;
++ struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
++ u32 ctrl_addr = pipe->ctrl_addr;
+
+- /* Update destination descriptor */
+- desc->addr = __cpu_to_le32(buffer);
+- desc->nbytes = 0;
++ lockdep_assert_held(&ar_pci->ce_lock);
+
+- dest_ring->per_transfer_context[write_index] =
+- per_recv_context;
++ if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
++ return -EIO;
+
+- /* Update Destination Ring Write Index */
+- write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+- ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
+- dest_ring->write_index = write_index;
+- ret = 0;
+- } else {
+- ret = -EIO;
+- }
+- ath10k_pci_sleep(ar);
++ desc->addr = __cpu_to_le32(paddr);
++ desc->nbytes = 0;
++
++ dest_ring->per_transfer_context[write_index] = ctx;
++ write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
++ ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
++ dest_ring->write_index = write_index;
++
++ return 0;
++}
+
+-out:
++int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
++{
++ struct ath10k *ar = pipe->ar;
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ int ret;
++
++ spin_lock_bh(&ar_pci->ce_lock);
++ ret = __ath10k_ce_rx_post_buf(pipe, ctx, paddr);
+ spin_unlock_bh(&ar_pci->ce_lock);
+
+ return ret;
+@@ -415,12 +443,12 @@ out:
+ * Guts of ath10k_ce_completed_recv_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+-static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
+- void **per_transfer_contextp,
+- u32 *bufferp,
+- unsigned int *nbytesp,
+- unsigned int *transfer_idp,
+- unsigned int *flagsp)
++int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
++ void **per_transfer_contextp,
++ u32 *bufferp,
++ unsigned int *nbytesp,
++ unsigned int *transfer_idp,
++ unsigned int *flagsp)
+ {
+ struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
+ unsigned int nentries_mask = dest_ring->nentries_mask;
+@@ -530,6 +558,7 @@ int ath10k_ce_revoke_recv_next(struct at
+
+ /* sanity */
+ dest_ring->per_transfer_context[sw_index] = NULL;
++ desc->nbytes = 0;
+
+ /* Update sw_index */
+ sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+@@ -548,11 +577,11 @@ int ath10k_ce_revoke_recv_next(struct at
+ * Guts of ath10k_ce_completed_send_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+-static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
+- void **per_transfer_contextp,
+- u32 *bufferp,
+- unsigned int *nbytesp,
+- unsigned int *transfer_idp)
++int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
++ void **per_transfer_contextp,
++ u32 *bufferp,
++ unsigned int *nbytesp,
++ unsigned int *transfer_idp)
+ {
+ struct ath10k_ce_ring *src_ring = ce_state->src_ring;
+ u32 ctrl_addr = ce_state->ctrl_addr;
+@@ -561,7 +590,6 @@ static int ath10k_ce_completed_send_next
+ unsigned int sw_index = src_ring->sw_index;
+ struct ce_desc *sdesc, *sbase;
+ unsigned int read_index;
+- int ret;
+
+ if (src_ring->hw_index == sw_index) {
+ /*
+@@ -572,20 +600,17 @@ static int ath10k_ce_completed_send_next
+ * value of the HW index has become stale.
+ */
+
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return ret;
+-
+- src_ring->hw_index =
+- ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+- src_ring->hw_index &= nentries_mask;
++ read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
++ if (read_index == 0xffffffff)
++ return -ENODEV;
+
+- ath10k_pci_sleep(ar);
++ read_index &= nentries_mask;
++ src_ring->hw_index = read_index;
+ }
+
+ read_index = src_ring->hw_index;
+
+- if ((read_index == sw_index) || (read_index == 0xffffffff))
++ if (read_index == sw_index)
+ return -EIO;
+
+ sbase = src_ring->shadow_base;
+@@ -701,11 +726,6 @@ void ath10k_ce_per_engine_service(struct
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+ u32 ctrl_addr = ce_state->ctrl_addr;
+- int ret;
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return;
+
+ spin_lock_bh(&ar_pci->ce_lock);
+
+@@ -730,7 +750,6 @@ void ath10k_ce_per_engine_service(struct
+ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+
+ spin_unlock_bh(&ar_pci->ce_lock);
+- ath10k_pci_sleep(ar);
+ }
+
+ /*
+@@ -741,13 +760,9 @@ void ath10k_ce_per_engine_service(struct
+
+ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
+ {
+- int ce_id, ret;
++ int ce_id;
+ u32 intr_summary;
+
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return;
+-
+ intr_summary = CE_INTERRUPT_SUMMARY(ar);
+
+ for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) {
+@@ -759,8 +774,6 @@ void ath10k_ce_per_engine_service_any(st
+
+ ath10k_ce_per_engine_service(ar, ce_id);
+ }
+-
+- ath10k_pci_sleep(ar);
+ }
+
+ /*
+@@ -770,16 +783,11 @@ void ath10k_ce_per_engine_service_any(st
+ *
+ * Called with ce_lock held.
+ */
+-static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state,
+- int disable_copy_compl_intr)
++static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state)
+ {
+ u32 ctrl_addr = ce_state->ctrl_addr;
+ struct ath10k *ar = ce_state->ar;
+- int ret;
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return;
++ bool disable_copy_compl_intr = ce_state->attr_flags & CE_ATTR_DIS_INTR;
+
+ if ((!disable_copy_compl_intr) &&
+ (ce_state->send_cb || ce_state->recv_cb))
+@@ -788,54 +796,33 @@ static void ath10k_ce_per_engine_handler
+ ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+
+ ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+-
+- ath10k_pci_sleep(ar);
+ }
+
+ int ath10k_ce_disable_interrupts(struct ath10k *ar)
+ {
+- int ce_id, ret;
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return ret;
++ int ce_id;
+
+ for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
+- u32 ctrl_addr = ath10k_ce_base_address(ce_id);
++ u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+
+ ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+ ath10k_ce_error_intr_disable(ar, ctrl_addr);
+ ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+ }
+
+- ath10k_pci_sleep(ar);
+-
+ return 0;
+ }
+
+-void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
+- void (*send_cb)(struct ath10k_ce_pipe *),
+- int disable_interrupts)
++void ath10k_ce_enable_interrupts(struct ath10k *ar)
+ {
+- struct ath10k *ar = ce_state->ar;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ int ce_id;
+
+- spin_lock_bh(&ar_pci->ce_lock);
+- ce_state->send_cb = send_cb;
+- ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts);
+- spin_unlock_bh(&ar_pci->ce_lock);
+-}
+-
+-void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
+- void (*recv_cb)(struct ath10k_ce_pipe *))
+-{
+- struct ath10k *ar = ce_state->ar;
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+-
+- spin_lock_bh(&ar_pci->ce_lock);
+- ce_state->recv_cb = recv_cb;
+- ath10k_ce_per_engine_handler_adjust(ce_state, 0);
+- spin_unlock_bh(&ar_pci->ce_lock);
++ /* Skip the last copy engine, CE7 the diagnostic window, as that
++ * uses polling and isn't initialized for interrupts.
++ */
++ for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++)
++ ath10k_ce_per_engine_handler_adjust(&ar_pci->ce_states[ce_id]);
+ }
+
+ static int ath10k_ce_init_src_ring(struct ath10k *ar,
+@@ -845,12 +832,12 @@ static int ath10k_ce_init_src_ring(struc
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+ struct ath10k_ce_ring *src_ring = ce_state->src_ring;
+- u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
++ u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+
+ nentries = roundup_pow_of_two(attr->src_nentries);
+
+- memset(src_ring->per_transfer_context, 0,
+- nentries * sizeof(*src_ring->per_transfer_context));
++ memset(src_ring->base_addr_owner_space, 0,
++ nentries * sizeof(struct ce_desc));
+
+ src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+ src_ring->sw_index &= src_ring->nentries_mask;
+@@ -868,7 +855,7 @@ static int ath10k_ce_init_src_ring(struc
+ ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
+ ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
+
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot init ce src ring id %d entries %d base_addr %p\n",
+ ce_id, nentries, src_ring->base_addr_owner_space);
+
+@@ -882,12 +869,12 @@ static int ath10k_ce_init_dest_ring(stru
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+ struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
+- u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
++ u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+
+ nentries = roundup_pow_of_two(attr->dest_nentries);
+
+- memset(dest_ring->per_transfer_context, 0,
+- nentries * sizeof(*dest_ring->per_transfer_context));
++ memset(dest_ring->base_addr_owner_space, 0,
++ nentries * sizeof(struct ce_desc));
+
+ dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+ dest_ring->sw_index &= dest_ring->nentries_mask;
+@@ -902,7 +889,7 @@ static int ath10k_ce_init_dest_ring(stru
+ ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
+ ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
+
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot ce dest ring id %d entries %d base_addr %p\n",
+ ce_id, nentries, dest_ring->base_addr_owner_space);
+
+@@ -1039,59 +1026,32 @@ ath10k_ce_alloc_dest_ring(struct ath10k
+ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
+ const struct ce_attr *attr)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+ int ret;
+
+- /*
+- * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
+- * additional TX locking checks.
+- *
+- * For the lack of a better place do the check here.
+- */
+- BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
+- (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+- BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
+- (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return ret;
+-
+- spin_lock_bh(&ar_pci->ce_lock);
+- ce_state->ar = ar;
+- ce_state->id = ce_id;
+- ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
+- ce_state->attr_flags = attr->flags;
+- ce_state->src_sz_max = attr->src_sz_max;
+- spin_unlock_bh(&ar_pci->ce_lock);
+-
+ if (attr->src_nentries) {
+ ret = ath10k_ce_init_src_ring(ar, ce_id, attr);
+ if (ret) {
+- ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n",
++ ath10k_err(ar, "Failed to initialize CE src ring for ID: %d (%d)\n",
+ ce_id, ret);
+- goto out;
++ return ret;
+ }
+ }
+
+ if (attr->dest_nentries) {
+ ret = ath10k_ce_init_dest_ring(ar, ce_id, attr);
+ if (ret) {
+- ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n",
++ ath10k_err(ar, "Failed to initialize CE dest ring for ID: %d (%d)\n",
+ ce_id, ret);
+- goto out;
++ return ret;
+ }
+ }
+
+-out:
+- ath10k_pci_sleep(ar);
+- return ret;
++ return 0;
+ }
+
+ static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
+ {
+- u32 ctrl_addr = ath10k_ce_base_address(ce_id);
++ u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+
+ ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0);
+ ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0);
+@@ -1101,7 +1061,7 @@ static void ath10k_ce_deinit_src_ring(st
+
+ static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id)
+ {
+- u32 ctrl_addr = ath10k_ce_base_address(ce_id);
++ u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+
+ ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0);
+ ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0);
+@@ -1110,30 +1070,49 @@ static void ath10k_ce_deinit_dest_ring(s
+
+ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
+ {
+- int ret;
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret)
+- return;
+-
+ ath10k_ce_deinit_src_ring(ar, ce_id);
+ ath10k_ce_deinit_dest_ring(ar, ce_id);
+-
+- ath10k_pci_sleep(ar);
+ }
+
+ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
+- const struct ce_attr *attr)
++ const struct ce_attr *attr,
++ void (*send_cb)(struct ath10k_ce_pipe *),
++ void (*recv_cb)(struct ath10k_ce_pipe *))
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+ int ret;
+
++ /*
++ * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
++ * additional TX locking checks.
++ *
++ * For the lack of a better place do the check here.
++ */
++ BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
++ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
++ BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
++ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
++ BUILD_BUG_ON(2*TARGET_TLV_NUM_MSDU_DESC >
++ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
++
++ ce_state->ar = ar;
++ ce_state->id = ce_id;
++ ce_state->ctrl_addr = ath10k_ce_base_address(ar, ce_id);
++ ce_state->attr_flags = attr->flags;
++ ce_state->src_sz_max = attr->src_sz_max;
++
++ if (attr->src_nentries)
++ ce_state->send_cb = send_cb;
++
++ if (attr->dest_nentries)
++ ce_state->recv_cb = recv_cb;
++
+ if (attr->src_nentries) {
+ ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
+ if (IS_ERR(ce_state->src_ring)) {
+ ret = PTR_ERR(ce_state->src_ring);
+- ath10k_err("failed to allocate copy engine source ring %d: %d\n",
++ ath10k_err(ar, "failed to allocate copy engine source ring %d: %d\n",
+ ce_id, ret);
+ ce_state->src_ring = NULL;
+ return ret;
+@@ -1145,7 +1124,7 @@ int ath10k_ce_alloc_pipe(struct ath10k *
+ attr);
+ if (IS_ERR(ce_state->dest_ring)) {
+ ret = PTR_ERR(ce_state->dest_ring);
+- ath10k_err("failed to allocate copy engine destination ring %d: %d\n",
++ ath10k_err(ar, "failed to allocate copy engine destination ring %d: %d\n",
+ ce_id, ret);
+ ce_state->dest_ring = NULL;
+ return ret;
+--- a/drivers/net/wireless/ath/ath10k/ce.h
++++ b/drivers/net/wireless/ath/ath10k/ce.h
+@@ -20,7 +20,6 @@
+
+ #include "hif.h"
+
+-
+ /* Maximum number of Copy Engine's supported */
+ #define CE_COUNT_MAX 8
+ #define CE_HTT_H2T_MSG_SRC_NENTRIES 4096
+@@ -37,11 +36,10 @@
+
+ struct ath10k_ce_pipe;
+
+-
+ #define CE_DESC_FLAGS_GATHER (1 << 0)
+ #define CE_DESC_FLAGS_BYTE_SWAP (1 << 1)
+ #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
+-#define CE_DESC_FLAGS_META_DATA_LSB 3
++#define CE_DESC_FLAGS_META_DATA_LSB 2
+
+ struct ce_desc {
+ __le32 addr;
+@@ -160,30 +158,15 @@ int ath10k_ce_send_nolock(struct ath10k_
+ unsigned int transfer_id,
+ unsigned int flags);
+
+-void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
+- void (*send_cb)(struct ath10k_ce_pipe *),
+- int disable_interrupts);
++void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe);
+
+ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe);
+
+ /*==================Recv=======================*/
+
+-/*
+- * Make a buffer available to receive. The buffer must be at least of a
+- * minimal size appropriate for this copy engine (src_sz_max attribute).
+- * ce - which copy engine to use
+- * per_transfer_recv_context - context passed back to caller's recv_cb
+- * buffer - address of buffer in CE space
+- * Returns 0 on success; otherwise an error status.
+- *
+- * Implemenation note: Pushes a buffer to Dest ring.
+- */
+-int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state,
+- void *per_transfer_recv_context,
+- u32 buffer);
+-
+-void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
+- void (*recv_cb)(struct ath10k_ce_pipe *));
++int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe);
++int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr);
++int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr);
+
+ /* recv flags */
+ /* Data is byte-swapped */
+@@ -204,10 +187,16 @@ int ath10k_ce_completed_recv_next(struct
+ * Pops 1 completed send buffer from Source ring.
+ */
+ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
+- void **per_transfer_contextp,
+- u32 *bufferp,
+- unsigned int *nbytesp,
+- unsigned int *transfer_idp);
++ void **per_transfer_contextp,
++ u32 *bufferp,
++ unsigned int *nbytesp,
++ unsigned int *transfer_idp);
++
++int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
++ void **per_transfer_contextp,
++ u32 *bufferp,
++ unsigned int *nbytesp,
++ unsigned int *transfer_idp);
+
+ /*==================CE Engine Initialization=======================*/
+
+@@ -215,7 +204,9 @@ int ath10k_ce_init_pipe(struct ath10k *a
+ const struct ce_attr *attr);
+ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
+ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
+- const struct ce_attr *attr);
++ const struct ce_attr *attr,
++ void (*send_cb)(struct ath10k_ce_pipe *),
++ void (*recv_cb)(struct ath10k_ce_pipe *));
+ void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
+
+ /*==================CE Engine Shutdown=======================*/
+@@ -228,6 +219,13 @@ int ath10k_ce_revoke_recv_next(struct at
+ void **per_transfer_contextp,
+ u32 *bufferp);
+
++int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
++ void **per_transfer_contextp,
++ u32 *bufferp,
++ unsigned int *nbytesp,
++ unsigned int *transfer_idp,
++ unsigned int *flagsp);
++
+ /*
+ * Support clean shutdown by allowing the caller to cancel
+ * pending sends. Target DMA must be stopped before using
+@@ -243,6 +241,7 @@ int ath10k_ce_cancel_send_next(struct at
+ void ath10k_ce_per_engine_service_any(struct ath10k *ar);
+ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
+ int ath10k_ce_disable_interrupts(struct ath10k *ar);
++void ath10k_ce_enable_interrupts(struct ath10k *ar);
+
+ /* ce_attr.flags values */
+ /* Use NonSnooping PCIe accesses? */
+@@ -395,8 +394,7 @@ struct ce_attr {
+ #define DST_WATERMARK_HIGH_RESET 0
+ #define DST_WATERMARK_ADDRESS 0x0050
+
+-
+-static inline u32 ath10k_ce_base_address(unsigned int ce_id)
++static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
+ {
+ return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
+ }
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -17,6 +17,7 @@
+
+ #include <linux/module.h>
+ #include <linux/firmware.h>
++#include <linux/of.h>
+
+ #include "core.h"
+ #include "mac.h"
+@@ -26,68 +27,88 @@
+ #include "bmi.h"
+ #include "debug.h"
+ #include "htt.h"
++#include "testmode.h"
++#include "wmi-ops.h"
+
+ unsigned int ath10k_debug_mask;
+ static bool uart_print;
+-static unsigned int ath10k_p2p;
++static bool skip_otp;
++
+ module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
+ module_param(uart_print, bool, 0644);
+-module_param_named(p2p, ath10k_p2p, uint, 0644);
++module_param(skip_otp, bool, 0644);
++
+ MODULE_PARM_DESC(debug_mask, "Debugging mask");
+ MODULE_PARM_DESC(uart_print, "Uart target debugging");
+-MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
++MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
+
+ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+ {
+ .id = QCA988X_HW_2_0_VERSION,
+ .name = "qca988x hw2.0",
+ .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
++ .uart_pin = 7,
+ .fw = {
+ .dir = QCA988X_HW_2_0_FW_DIR,
+ .fw = QCA988X_HW_2_0_FW_FILE,
+ .otp = QCA988X_HW_2_0_OTP_FILE,
+ .board = QCA988X_HW_2_0_BOARD_DATA_FILE,
++ .board_size = QCA988X_BOARD_DATA_SZ,
++ .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ,
++ },
++ },
++ {
++ .id = QCA6174_HW_2_1_VERSION,
++ .name = "qca6174 hw2.1",
++ .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
++ .uart_pin = 6,
++ .fw = {
++ .dir = QCA6174_HW_2_1_FW_DIR,
++ .fw = QCA6174_HW_2_1_FW_FILE,
++ .otp = QCA6174_HW_2_1_OTP_FILE,
++ .board = QCA6174_HW_2_1_BOARD_DATA_FILE,
++ .board_size = QCA6174_BOARD_DATA_SZ,
++ .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
++ },
++ },
++ {
++ .id = QCA6174_HW_3_0_VERSION,
++ .name = "qca6174 hw3.0",
++ .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
++ .uart_pin = 6,
++ .fw = {
++ .dir = QCA6174_HW_3_0_FW_DIR,
++ .fw = QCA6174_HW_3_0_FW_FILE,
++ .otp = QCA6174_HW_3_0_OTP_FILE,
++ .board = QCA6174_HW_3_0_BOARD_DATA_FILE,
++ .board_size = QCA6174_BOARD_DATA_SZ,
++ .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
++ },
++ },
++ {
++ .id = QCA6174_HW_3_2_VERSION,
++ .name = "qca6174 hw3.2",
++ .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
++ .uart_pin = 6,
++ .fw = {
++ /* uses same binaries as hw3.0 */
++ .dir = QCA6174_HW_3_0_FW_DIR,
++ .fw = QCA6174_HW_3_0_FW_FILE,
++ .otp = QCA6174_HW_3_0_OTP_FILE,
++ .board = QCA6174_HW_3_0_BOARD_DATA_FILE,
++ .board_size = QCA6174_BOARD_DATA_SZ,
++ .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
+ },
+ },
+ };
+
+ static void ath10k_send_suspend_complete(struct ath10k *ar)
+ {
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n");
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot suspend complete\n");
+
+ complete(&ar->target_suspend);
+ }
+
+-static int ath10k_init_connect_htc(struct ath10k *ar)
+-{
+- int status;
+-
+- status = ath10k_wmi_connect_htc_service(ar);
+- if (status)
+- goto conn_fail;
+-
+- /* Start HTC */
+- status = ath10k_htc_start(&ar->htc);
+- if (status)
+- goto conn_fail;
+-
+- /* Wait for WMI event to be ready */
+- status = ath10k_wmi_wait_for_service_ready(ar);
+- if (status <= 0) {
+- ath10k_warn("wmi service ready event not received");
+- status = -ETIMEDOUT;
+- goto timeout;
+- }
+-
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n");
+- return 0;
+-
+-timeout:
+- ath10k_htc_stop(&ar->htc);
+-conn_fail:
+- return status;
+-}
+-
+ static int ath10k_init_configure_target(struct ath10k *ar)
+ {
+ u32 param_host;
+@@ -97,14 +118,14 @@ static int ath10k_init_configure_target(
+ ret = ath10k_bmi_write32(ar, hi_app_host_interest,
+ HTC_PROTOCOL_VERSION);
+ if (ret) {
+- ath10k_err("settings HTC version failed\n");
++ ath10k_err(ar, "settings HTC version failed\n");
+ return ret;
+ }
+
+ /* set the firmware mode to STA/IBSS/AP */
+ ret = ath10k_bmi_read32(ar, hi_option_flag, &param_host);
+ if (ret) {
+- ath10k_err("setting firmware mode (1/2) failed\n");
++ ath10k_err(ar, "setting firmware mode (1/2) failed\n");
+ return ret;
+ }
+
+@@ -123,14 +144,14 @@ static int ath10k_init_configure_target(
+
+ ret = ath10k_bmi_write32(ar, hi_option_flag, param_host);
+ if (ret) {
+- ath10k_err("setting firmware mode (2/2) failed\n");
++ ath10k_err(ar, "setting firmware mode (2/2) failed\n");
+ return ret;
+ }
+
+ /* We do all byte-swapping on the host */
+ ret = ath10k_bmi_write32(ar, hi_be, 0);
+ if (ret) {
+- ath10k_err("setting host CPU BE mode failed\n");
++ ath10k_err(ar, "setting host CPU BE mode failed\n");
+ return ret;
+ }
+
+@@ -138,7 +159,7 @@ static int ath10k_init_configure_target(
+ ret = ath10k_bmi_write32(ar, hi_fw_swap, 0);
+
+ if (ret) {
+- ath10k_err("setting FW data/desc swap flags failed\n");
++ ath10k_err(ar, "setting FW data/desc swap flags failed\n");
+ return ret;
+ }
+
+@@ -167,79 +188,83 @@ static const struct firmware *ath10k_fet
+ return fw;
+ }
+
+-static int ath10k_push_board_ext_data(struct ath10k *ar)
++static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data,
++ size_t data_len)
+ {
+- u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+- u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
++ u32 board_data_size = ar->hw_params.fw.board_size;
++ u32 board_ext_data_size = ar->hw_params.fw.board_ext_size;
+ u32 board_ext_data_addr;
+ int ret;
+
+ ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr);
+ if (ret) {
+- ath10k_err("could not read board ext data addr (%d)\n", ret);
++ ath10k_err(ar, "could not read board ext data addr (%d)\n",
++ ret);
+ return ret;
+ }
+
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot push board extended data addr 0x%x\n",
+ board_ext_data_addr);
+
+ if (board_ext_data_addr == 0)
+ return 0;
+
+- if (ar->board_len != (board_data_size + board_ext_data_size)) {
+- ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
+- ar->board_len, board_data_size, board_ext_data_size);
++ if (data_len != (board_data_size + board_ext_data_size)) {
++ ath10k_err(ar, "invalid board (ext) data sizes %zu != %d+%d\n",
++ data_len, board_data_size, board_ext_data_size);
+ return -EINVAL;
+ }
+
+ ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
+- ar->board_data + board_data_size,
++ data + board_data_size,
+ board_ext_data_size);
+ if (ret) {
+- ath10k_err("could not write board ext data (%d)\n", ret);
++ ath10k_err(ar, "could not write board ext data (%d)\n", ret);
+ return ret;
+ }
+
+ ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
+ (board_ext_data_size << 16) | 1);
+ if (ret) {
+- ath10k_err("could not write board ext data bit (%d)\n", ret);
++ ath10k_err(ar, "could not write board ext data bit (%d)\n",
++ ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+-static int ath10k_download_board_data(struct ath10k *ar)
++static int ath10k_download_board_data(struct ath10k *ar, const void *data,
++ size_t data_len)
+ {
+- u32 board_data_size = QCA988X_BOARD_DATA_SZ;
++ u32 board_data_size = ar->hw_params.fw.board_size;
+ u32 address;
+ int ret;
+
+- ret = ath10k_push_board_ext_data(ar);
++ ret = ath10k_push_board_ext_data(ar, data, data_len);
+ if (ret) {
+- ath10k_err("could not push board ext data (%d)\n", ret);
++ ath10k_err(ar, "could not push board ext data (%d)\n", ret);
+ goto exit;
+ }
+
+ ret = ath10k_bmi_read32(ar, hi_board_data, &address);
+ if (ret) {
+- ath10k_err("could not read board data addr (%d)\n", ret);
++ ath10k_err(ar, "could not read board data addr (%d)\n", ret);
+ goto exit;
+ }
+
+- ret = ath10k_bmi_write_memory(ar, address, ar->board_data,
++ ret = ath10k_bmi_write_memory(ar, address, data,
+ min_t(u32, board_data_size,
+- ar->board_len));
++ data_len));
+ if (ret) {
+- ath10k_err("could not write board data (%d)\n", ret);
++ ath10k_err(ar, "could not write board data (%d)\n", ret);
+ goto exit;
+ }
+
+ ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1);
+ if (ret) {
+- ath10k_err("could not write board data bit (%d)\n", ret);
++ ath10k_err(ar, "could not write board data bit (%d)\n", ret);
+ goto exit;
+ }
+
+@@ -247,73 +272,182 @@ exit:
+ return ret;
+ }
+
++static int ath10k_download_cal_file(struct ath10k *ar)
++{
++ int ret;
++
++ if (!ar->cal_file)
++ return -ENOENT;
++
++ if (IS_ERR(ar->cal_file))
++ return PTR_ERR(ar->cal_file);
++
++ ret = ath10k_download_board_data(ar, ar->cal_file->data,
++ ar->cal_file->size);
++ if (ret) {
++ ath10k_err(ar, "failed to download cal_file data: %d\n", ret);
++ return ret;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cal file downloaded\n");
++
++ return 0;
++}
++
++static int ath10k_download_cal_dt(struct ath10k *ar)
++{
++ struct device_node *node;
++ int data_len;
++ void *data;
++ int ret;
++
++ node = ar->dev->of_node;
++ if (!node)
++ /* Device Tree is optional, don't print any warnings if
++ * there's no node for ath10k.
++ */
++ return -ENOENT;
++
++ if (!of_get_property(node, "qcom,ath10k-calibration-data",
++ &data_len)) {
++ /* The calibration data node is optional */
++ return -ENOENT;
++ }
++
++ if (data_len != QCA988X_CAL_DATA_LEN) {
++ ath10k_warn(ar, "invalid calibration data length in DT: %d\n",
++ data_len);
++ ret = -EMSGSIZE;
++ goto out;
++ }
++
++ data = kmalloc(data_len, GFP_KERNEL);
++ if (!data) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ ret = of_property_read_u8_array(node, "qcom,ath10k-calibration-data",
++ data, data_len);
++ if (ret) {
++ ath10k_warn(ar, "failed to read calibration data from DT: %d\n",
++ ret);
++ goto out_free;
++ }
++
++ ret = ath10k_download_board_data(ar, data, data_len);
++ if (ret) {
++ ath10k_warn(ar, "failed to download calibration data from Device Tree: %d\n",
++ ret);
++ goto out_free;
++ }
++
++ ret = 0;
++
++out_free:
++ kfree(data);
++
++out:
++ return ret;
++}
++
+ static int ath10k_download_and_run_otp(struct ath10k *ar)
+ {
+ u32 result, address = ar->hw_params.patch_load_addr;
+ int ret;
+
++ ret = ath10k_download_board_data(ar, ar->board_data, ar->board_len);
++ if (ret) {
++ ath10k_err(ar, "failed to download board data: %d\n", ret);
++ return ret;
++ }
++
+ /* OTP is optional */
+
+ if (!ar->otp_data || !ar->otp_len) {
+- ath10k_warn("Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
++ ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
+ ar->otp_data, ar->otp_len);
+ return 0;
+ }
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n",
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n",
+ address, ar->otp_len);
+
+ ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
+ if (ret) {
+- ath10k_err("could not write otp (%d)\n", ret);
++ ath10k_err(ar, "could not write otp (%d)\n", ret);
+ return ret;
+ }
+
+ ret = ath10k_bmi_execute(ar, address, 0, &result);
+ if (ret) {
+- ath10k_err("could not execute otp (%d)\n", ret);
++ ath10k_err(ar, "could not execute otp (%d)\n", ret);
+ return ret;
+ }
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
+
+- if (result != 0) {
+- ath10k_err("otp calibration failed: %d", result);
++ if (!skip_otp && result != 0) {
++ ath10k_err(ar, "otp calibration failed: %d", result);
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+-static int ath10k_download_fw(struct ath10k *ar)
++static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
+ {
+- u32 address;
++ u32 address, data_len;
++ const char *mode_name;
++ const void *data;
+ int ret;
+
+ address = ar->hw_params.patch_load_addr;
+
+- ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data,
+- ar->firmware_len);
++ switch (mode) {
++ case ATH10K_FIRMWARE_MODE_NORMAL:
++ data = ar->firmware_data;
++ data_len = ar->firmware_len;
++ mode_name = "normal";
++ break;
++ case ATH10K_FIRMWARE_MODE_UTF:
++ data = ar->testmode.utf->data;
++ data_len = ar->testmode.utf->size;
++ mode_name = "utf";
++ break;
++ default:
++ ath10k_err(ar, "unknown firmware mode: %d\n", mode);
++ return -EINVAL;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
++ "boot uploading firmware image %p len %d mode %s\n",
++ data, data_len, mode_name);
++
++ ret = ath10k_bmi_fast_download(ar, address, data, data_len);
+ if (ret) {
+- ath10k_err("could not write fw (%d)\n", ret);
+- goto exit;
++ ath10k_err(ar, "failed to download %s firmware: %d\n",
++ mode_name, ret);
++ return ret;
+ }
+
+-exit:
+ return ret;
+ }
+
+ static void ath10k_core_free_firmware_files(struct ath10k *ar)
+ {
+- if (ar->board && !IS_ERR(ar->board))
++ if (!IS_ERR(ar->board))
+ release_firmware(ar->board);
+
+- if (ar->otp && !IS_ERR(ar->otp))
++ if (!IS_ERR(ar->otp))
+ release_firmware(ar->otp);
+
+- if (ar->firmware && !IS_ERR(ar->firmware))
++ if (!IS_ERR(ar->firmware))
+ release_firmware(ar->firmware);
+
++ if (!IS_ERR(ar->cal_file))
++ release_firmware(ar->cal_file);
++
+ ar->board = NULL;
+ ar->board_data = NULL;
+ ar->board_len = 0;
+@@ -325,6 +459,27 @@ static void ath10k_core_free_firmware_fi
+ ar->firmware = NULL;
+ ar->firmware_data = NULL;
+ ar->firmware_len = 0;
++
++ ar->cal_file = NULL;
++}
++
++static int ath10k_fetch_cal_file(struct ath10k *ar)
++{
++ char filename[100];
++
++ /* cal-<bus>-<id>.bin */
++ scnprintf(filename, sizeof(filename), "cal-%s-%s.bin",
++ ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
++
++ ar->cal_file = ath10k_fetch_fw_file(ar, ATH10K_FW_DIR, filename);
++ if (IS_ERR(ar->cal_file))
++ /* calibration file is optional, don't print any warnings */
++ return PTR_ERR(ar->cal_file);
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "found calibration file %s/%s\n",
++ ATH10K_FW_DIR, filename);
++
++ return 0;
+ }
+
+ static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
+@@ -332,12 +487,12 @@ static int ath10k_core_fetch_firmware_ap
+ int ret = 0;
+
+ if (ar->hw_params.fw.fw == NULL) {
+- ath10k_err("firmware file not defined\n");
++ ath10k_err(ar, "firmware file not defined\n");
+ return -EINVAL;
+ }
+
+ if (ar->hw_params.fw.board == NULL) {
+- ath10k_err("board data file not defined");
++ ath10k_err(ar, "board data file not defined");
+ return -EINVAL;
+ }
+
+@@ -346,7 +501,7 @@ static int ath10k_core_fetch_firmware_ap
+ ar->hw_params.fw.board);
+ if (IS_ERR(ar->board)) {
+ ret = PTR_ERR(ar->board);
+- ath10k_err("could not fetch board data (%d)\n", ret);
++ ath10k_err(ar, "could not fetch board data (%d)\n", ret);
+ goto err;
+ }
+
+@@ -358,7 +513,7 @@ static int ath10k_core_fetch_firmware_ap
+ ar->hw_params.fw.fw);
+ if (IS_ERR(ar->firmware)) {
+ ret = PTR_ERR(ar->firmware);
+- ath10k_err("could not fetch firmware (%d)\n", ret);
++ ath10k_err(ar, "could not fetch firmware (%d)\n", ret);
+ goto err;
+ }
+
+@@ -374,7 +529,7 @@ static int ath10k_core_fetch_firmware_ap
+ ar->hw_params.fw.otp);
+ if (IS_ERR(ar->otp)) {
+ ret = PTR_ERR(ar->otp);
+- ath10k_err("could not fetch otp (%d)\n", ret);
++ ath10k_err(ar, "could not fetch otp (%d)\n", ret);
+ goto err;
+ }
+
+@@ -394,12 +549,12 @@ static int ath10k_core_fetch_firmware_ap
+ int ie_id, i, index, bit, ret;
+ struct ath10k_fw_ie *hdr;
+ const u8 *data;
+- __le32 *timestamp;
++ __le32 *timestamp, *version;
+
+ /* first fetch the firmware file (firmware-*.bin) */
+ ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name);
+ if (IS_ERR(ar->firmware)) {
+- ath10k_err("could not fetch firmware file '%s/%s': %ld\n",
++ ath10k_err(ar, "could not fetch firmware file '%s/%s': %ld\n",
+ ar->hw_params.fw.dir, name, PTR_ERR(ar->firmware));
+ return PTR_ERR(ar->firmware);
+ }
+@@ -411,14 +566,14 @@ static int ath10k_core_fetch_firmware_ap
+ magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
+
+ if (len < magic_len) {
+- ath10k_err("firmware file '%s/%s' too small to contain magic: %zu\n",
++ ath10k_err(ar, "firmware file '%s/%s' too small to contain magic: %zu\n",
+ ar->hw_params.fw.dir, name, len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
+- ath10k_err("invalid firmware magic\n");
++ ath10k_err(ar, "invalid firmware magic\n");
+ ret = -EINVAL;
+ goto err;
+ }
+@@ -440,7 +595,7 @@ static int ath10k_core_fetch_firmware_ap
+ data += sizeof(*hdr);
+
+ if (len < ie_len) {
+- ath10k_err("invalid length for FW IE %d (%zu < %zu)\n",
++ ath10k_err(ar, "invalid length for FW IE %d (%zu < %zu)\n",
+ ie_id, len, ie_len);
+ ret = -EINVAL;
+ goto err;
+@@ -454,7 +609,7 @@ static int ath10k_core_fetch_firmware_ap
+ memcpy(ar->hw->wiphy->fw_version, data, ie_len);
+ ar->hw->wiphy->fw_version[ie_len] = '\0';
+
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "found fw version %s\n",
+ ar->hw->wiphy->fw_version);
+ break;
+@@ -464,11 +619,11 @@ static int ath10k_core_fetch_firmware_ap
+
+ timestamp = (__le32 *)data;
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw timestamp %d\n",
+ le32_to_cpup(timestamp));
+ break;
+ case ATH10K_FW_IE_FEATURES:
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "found firmware features ie (%zd B)\n",
+ ie_len);
+
+@@ -480,19 +635,19 @@ static int ath10k_core_fetch_firmware_ap
+ break;
+
+ if (data[index] & (1 << bit)) {
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "Enabling feature bit: %i\n",
+ i);
+ __set_bit(i, ar->fw_features);
+ }
+ }
+
+- ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "",
++ ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "features", "",
+ ar->fw_features,
+ sizeof(ar->fw_features));
+ break;
+ case ATH10K_FW_IE_FW_IMAGE:
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "found fw image ie (%zd B)\n",
+ ie_len);
+
+@@ -501,7 +656,7 @@ static int ath10k_core_fetch_firmware_ap
+
+ break;
+ case ATH10K_FW_IE_OTP_IMAGE:
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "found otp image ie (%zd B)\n",
+ ie_len);
+
+@@ -509,8 +664,19 @@ static int ath10k_core_fetch_firmware_ap
+ ar->otp_len = ie_len;
+
+ break;
++ case ATH10K_FW_IE_WMI_OP_VERSION:
++ if (ie_len != sizeof(u32))
++ break;
++
++ version = (__le32 *)data;
++
++ ar->wmi.op_version = le32_to_cpup(version);
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n",
++ ar->wmi.op_version);
++ break;
+ default:
+- ath10k_warn("Unknown FW IE: %u\n",
++ ath10k_warn(ar, "Unknown FW IE: %u\n",
+ le32_to_cpu(hdr->id));
+ break;
+ }
+@@ -523,7 +689,7 @@ static int ath10k_core_fetch_firmware_ap
+ }
+
+ if (!ar->firmware_data || !ar->firmware_len) {
+- ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n",
++ ath10k_warn(ar, "No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n",
+ ar->hw_params.fw.dir, name);
+ ret = -ENOMEDIUM;
+ goto err;
+@@ -531,7 +697,7 @@ static int ath10k_core_fetch_firmware_ap
+
+ /* now fetch the board file */
+ if (ar->hw_params.fw.board == NULL) {
+- ath10k_err("board data file not defined");
++ ath10k_err(ar, "board data file not defined");
+ ret = -EINVAL;
+ goto err;
+ }
+@@ -541,7 +707,7 @@ static int ath10k_core_fetch_firmware_ap
+ ar->hw_params.fw.board);
+ if (IS_ERR(ar->board)) {
+ ret = PTR_ERR(ar->board);
+- ath10k_err("could not fetch board data '%s/%s' (%d)\n",
++ ath10k_err(ar, "could not fetch board data '%s/%s' (%d)\n",
+ ar->hw_params.fw.dir, ar->hw_params.fw.board,
+ ret);
+ goto err;
+@@ -561,49 +727,79 @@ static int ath10k_core_fetch_firmware_fi
+ {
+ int ret;
+
++ /* calibration file is optional, don't check for any errors */
++ ath10k_fetch_cal_file(ar);
++
++ ar->fw_api = 4;
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
++
++ ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API4_FILE);
++ if (ret == 0)
++ goto success;
++
++ ar->fw_api = 3;
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
++
++ ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API3_FILE);
++ if (ret == 0)
++ goto success;
++
+ ar->fw_api = 2;
+- ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
+
+ ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE);
+ if (ret == 0)
+ goto success;
+
+ ar->fw_api = 1;
+- ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
+
+ ret = ath10k_core_fetch_firmware_api_1(ar);
+ if (ret)
+ return ret;
+
+ success:
+- ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
+
+ return 0;
+ }
+
+-static int ath10k_init_download_firmware(struct ath10k *ar)
++static int ath10k_download_cal_data(struct ath10k *ar)
+ {
+ int ret;
+
+- ret = ath10k_download_board_data(ar);
+- if (ret) {
+- ath10k_err("failed to download board data: %d\n", ret);
+- return ret;
++ ret = ath10k_download_cal_file(ar);
++ if (ret == 0) {
++ ar->cal_mode = ATH10K_CAL_MODE_FILE;
++ goto done;
+ }
+
+- ret = ath10k_download_and_run_otp(ar);
+- if (ret) {
+- ath10k_err("failed to run otp: %d\n", ret);
+- return ret;
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
++ "boot did not find a calibration file, try DT next: %d\n",
++ ret);
++
++ ret = ath10k_download_cal_dt(ar);
++ if (ret == 0) {
++ ar->cal_mode = ATH10K_CAL_MODE_DT;
++ goto done;
+ }
+
+- ret = ath10k_download_fw(ar);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
++ "boot did not find DT entry, try OTP next: %d\n",
++ ret);
++
++ ret = ath10k_download_and_run_otp(ar);
+ if (ret) {
+- ath10k_err("failed to download firmware: %d\n", ret);
++ ath10k_err(ar, "failed to run otp: %d\n", ret);
+ return ret;
+ }
+
+- return ret;
++ ar->cal_mode = ATH10K_CAL_MODE_OTP;
++
++done:
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
++ ath10k_cal_mode_str(ar->cal_mode));
++ return 0;
+ }
+
+ static int ath10k_init_uart(struct ath10k *ar)
+@@ -616,33 +812,33 @@ static int ath10k_init_uart(struct ath10
+ */
+ ret = ath10k_bmi_write32(ar, hi_serial_enable, 0);
+ if (ret) {
+- ath10k_warn("could not disable UART prints (%d)\n", ret);
++ ath10k_warn(ar, "could not disable UART prints (%d)\n", ret);
+ return ret;
+ }
+
+ if (!uart_print)
+ return 0;
+
+- ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
++ ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin);
+ if (ret) {
+- ath10k_warn("could not enable UART prints (%d)\n", ret);
++ ath10k_warn(ar, "could not enable UART prints (%d)\n", ret);
+ return ret;
+ }
+
+ ret = ath10k_bmi_write32(ar, hi_serial_enable, 1);
+ if (ret) {
+- ath10k_warn("could not enable UART prints (%d)\n", ret);
++ ath10k_warn(ar, "could not enable UART prints (%d)\n", ret);
+ return ret;
+ }
+
+ /* Set the UART baud rate to 19200. */
+ ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200);
+ if (ret) {
+- ath10k_warn("could not set the baud rate (%d)\n", ret);
++ ath10k_warn(ar, "could not set the baud rate (%d)\n", ret);
+ return ret;
+ }
+
+- ath10k_info("UART prints enabled\n");
++ ath10k_info(ar, "UART prints enabled\n");
+ return 0;
+ }
+
+@@ -659,14 +855,14 @@ static int ath10k_init_hw_params(struct
+ }
+
+ if (i == ARRAY_SIZE(ath10k_hw_params_list)) {
+- ath10k_err("Unsupported hardware version: 0x%x\n",
++ ath10k_err(ar, "Unsupported hardware version: 0x%x\n",
+ ar->target_version);
+ return -EINVAL;
+ }
+
+ ar->hw_params = *hw_params;
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n",
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n",
+ ar->hw_params.name, ar->target_version);
+
+ return 0;
+@@ -676,101 +872,124 @@ static void ath10k_core_restart(struct w
+ {
+ struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
++ set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
++
++ /* Place a barrier to make sure the compiler doesn't reorder
++ * CRASH_FLUSH and calling other functions.
++ */
++ barrier();
++
++ ieee80211_stop_queues(ar->hw);
++ ath10k_drain_tx(ar);
++ complete_all(&ar->scan.started);
++ complete_all(&ar->scan.completed);
++ complete_all(&ar->scan.on_channel);
++ complete_all(&ar->offchan_tx_completed);
++ complete_all(&ar->install_key_done);
++ complete_all(&ar->vdev_setup_done);
++ complete_all(&ar->thermal.wmi_sync);
++ wake_up(&ar->htt.empty_tx_wq);
++ wake_up(&ar->wmi.tx_credits_wq);
++ wake_up(&ar->peer_mapping_wq);
++
+ mutex_lock(&ar->conf_mutex);
+
+ switch (ar->state) {
+ case ATH10K_STATE_ON:
+ ar->state = ATH10K_STATE_RESTARTING;
+- ath10k_halt(ar);
++ ath10k_hif_stop(ar);
++ ath10k_scan_finish(ar);
+ ieee80211_restart_hw(ar->hw);
+ break;
+ case ATH10K_STATE_OFF:
+ /* this can happen if driver is being unloaded
+ * or if the crash happens during FW probing */
+- ath10k_warn("cannot restart a device that hasn't been started\n");
++ ath10k_warn(ar, "cannot restart a device that hasn't been started\n");
+ break;
+ case ATH10K_STATE_RESTARTING:
++ /* hw restart might be requested from multiple places */
++ break;
+ case ATH10K_STATE_RESTARTED:
+ ar->state = ATH10K_STATE_WEDGED;
+ /* fall through */
+ case ATH10K_STATE_WEDGED:
+- ath10k_warn("device is wedged, will not restart\n");
++ ath10k_warn(ar, "device is wedged, will not restart\n");
++ break;
++ case ATH10K_STATE_UTF:
++ ath10k_warn(ar, "firmware restart in UTF mode not supported\n");
+ break;
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+ }
+
+-struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+- const struct ath10k_hif_ops *hif_ops)
++static int ath10k_core_init_firmware_features(struct ath10k *ar)
+ {
+- struct ath10k *ar;
+-
+- ar = ath10k_mac_create();
+- if (!ar)
+- return NULL;
+-
+- ar->ath_common.priv = ar;
+- ar->ath_common.hw = ar->hw;
+-
+- ar->p2p = !!ath10k_p2p;
+- ar->dev = dev;
+-
+- ar->hif.priv = hif_priv;
+- ar->hif.ops = hif_ops;
+-
+- init_completion(&ar->scan.started);
+- init_completion(&ar->scan.completed);
+- init_completion(&ar->scan.on_channel);
+- init_completion(&ar->target_suspend);
+-
+- init_completion(&ar->install_key_done);
+- init_completion(&ar->vdev_setup_done);
+-
+- setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
+-
+- ar->workqueue = create_singlethread_workqueue("ath10k_wq");
+- if (!ar->workqueue)
+- goto err_wq;
+-
+- mutex_init(&ar->conf_mutex);
+- spin_lock_init(&ar->data_lock);
+-
+- INIT_LIST_HEAD(&ar->peers);
+- init_waitqueue_head(&ar->peer_mapping_wq);
+-
+- init_completion(&ar->offchan_tx_completed);
+- INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
+- skb_queue_head_init(&ar->offchan_tx_queue);
+-
+- INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
+- skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
+-
+- INIT_WORK(&ar->restart_work, ath10k_core_restart);
++ if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) &&
++ !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
++ ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well");
++ return -EINVAL;
++ }
+
+- return ar;
++ if (ar->wmi.op_version >= ATH10K_FW_WMI_OP_VERSION_MAX) {
++ ath10k_err(ar, "unsupported WMI OP version (max %d): %d\n",
++ ATH10K_FW_WMI_OP_VERSION_MAX, ar->wmi.op_version);
++ return -EINVAL;
++ }
+
+-err_wq:
+- ath10k_mac_destroy(ar);
+- return NULL;
+-}
+-EXPORT_SYMBOL(ath10k_core_create);
++ /* Backwards compatibility for firmwares without
++ * ATH10K_FW_IE_WMI_OP_VERSION.
++ */
++ if (ar->wmi.op_version == ATH10K_FW_WMI_OP_VERSION_UNSET) {
++ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
++ if (test_bit(ATH10K_FW_FEATURE_WMI_10_2,
++ ar->fw_features))
++ ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_2;
++ else
++ ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
++ } else {
++ ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_MAIN;
++ }
++ }
+
+-void ath10k_core_destroy(struct ath10k *ar)
+-{
+- flush_workqueue(ar->workqueue);
+- destroy_workqueue(ar->workqueue);
++ switch (ar->wmi.op_version) {
++ case ATH10K_FW_WMI_OP_VERSION_MAIN:
++ ar->max_num_peers = TARGET_NUM_PEERS;
++ ar->max_num_stations = TARGET_NUM_STATIONS;
++ ar->max_num_vdevs = TARGET_NUM_VDEVS;
++ ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_10_1:
++ case ATH10K_FW_WMI_OP_VERSION_10_2:
++ case ATH10K_FW_WMI_OP_VERSION_10_2_4:
++ ar->max_num_peers = TARGET_10X_NUM_PEERS;
++ ar->max_num_stations = TARGET_10X_NUM_STATIONS;
++ ar->max_num_vdevs = TARGET_10X_NUM_VDEVS;
++ ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_TLV:
++ ar->max_num_peers = TARGET_TLV_NUM_PEERS;
++ ar->max_num_stations = TARGET_TLV_NUM_STATIONS;
++ ar->max_num_vdevs = TARGET_TLV_NUM_VDEVS;
++ ar->htt.max_num_pending_tx = TARGET_TLV_NUM_MSDU_DESC;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_UNSET:
++ case ATH10K_FW_WMI_OP_VERSION_MAX:
++ WARN_ON(1);
++ return -EINVAL;
++ }
+
+- ath10k_mac_destroy(ar);
++ return 0;
+ }
+-EXPORT_SYMBOL(ath10k_core_destroy);
+
+-int ath10k_core_start(struct ath10k *ar)
++int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
+ {
+ int status;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
++ clear_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
++
+ ath10k_bmi_start(ar);
+
+ if (ath10k_init_configure_target(ar)) {
+@@ -778,7 +997,11 @@ int ath10k_core_start(struct ath10k *ar)
+ goto err;
+ }
+
+- status = ath10k_init_download_firmware(ar);
++ status = ath10k_download_cal_data(ar);
++ if (status)
++ goto err;
++
++ status = ath10k_download_fw(ar, mode);
+ if (status)
+ goto err;
+
+@@ -791,7 +1014,7 @@ int ath10k_core_start(struct ath10k *ar)
+
+ status = ath10k_htc_init(ar);
+ if (status) {
+- ath10k_err("could not init HTC (%d)\n", status);
++ ath10k_err(ar, "could not init HTC (%d)\n", status);
+ goto err;
+ }
+
+@@ -801,79 +1024,123 @@ int ath10k_core_start(struct ath10k *ar)
+
+ status = ath10k_wmi_attach(ar);
+ if (status) {
+- ath10k_err("WMI attach failed: %d\n", status);
++ ath10k_err(ar, "WMI attach failed: %d\n", status);
+ goto err;
+ }
+
+- status = ath10k_hif_start(ar);
++ status = ath10k_htt_init(ar);
++ if (status) {
++ ath10k_err(ar, "failed to init htt: %d\n", status);
++ goto err_wmi_detach;
++ }
++
++ status = ath10k_htt_tx_alloc(&ar->htt);
+ if (status) {
+- ath10k_err("could not start HIF: %d\n", status);
++ ath10k_err(ar, "failed to alloc htt tx: %d\n", status);
+ goto err_wmi_detach;
+ }
+
++ status = ath10k_htt_rx_alloc(&ar->htt);
++ if (status) {
++ ath10k_err(ar, "failed to alloc htt rx: %d\n", status);
++ goto err_htt_tx_detach;
++ }
++
++ status = ath10k_hif_start(ar);
++ if (status) {
++ ath10k_err(ar, "could not start HIF: %d\n", status);
++ goto err_htt_rx_detach;
++ }
++
+ status = ath10k_htc_wait_target(&ar->htc);
+ if (status) {
+- ath10k_err("failed to connect to HTC: %d\n", status);
++ ath10k_err(ar, "failed to connect to HTC: %d\n", status);
+ goto err_hif_stop;
+ }
+
+- status = ath10k_htt_attach(ar);
++ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
++ status = ath10k_htt_connect(&ar->htt);
++ if (status) {
++ ath10k_err(ar, "failed to connect htt (%d)\n", status);
++ goto err_hif_stop;
++ }
++ }
++
++ status = ath10k_wmi_connect(ar);
+ if (status) {
+- ath10k_err("could not attach htt (%d)\n", status);
++ ath10k_err(ar, "could not connect wmi: %d\n", status);
+ goto err_hif_stop;
+ }
+
+- status = ath10k_init_connect_htc(ar);
+- if (status)
+- goto err_htt_detach;
++ status = ath10k_htc_start(&ar->htc);
++ if (status) {
++ ath10k_err(ar, "failed to start htc: %d\n", status);
++ goto err_hif_stop;
++ }
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n",
++ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
++ status = ath10k_wmi_wait_for_service_ready(ar);
++ if (status <= 0) {
++ ath10k_warn(ar, "wmi service ready event not received");
++ status = -ETIMEDOUT;
++ goto err_hif_stop;
++ }
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "firmware %s booted\n",
+ ar->hw->wiphy->fw_version);
+
+ status = ath10k_wmi_cmd_init(ar);
+ if (status) {
+- ath10k_err("could not send WMI init command (%d)\n", status);
+- goto err_disconnect_htc;
++ ath10k_err(ar, "could not send WMI init command (%d)\n",
++ status);
++ goto err_hif_stop;
+ }
+
+ status = ath10k_wmi_wait_for_unified_ready(ar);
+ if (status <= 0) {
+- ath10k_err("wmi unified ready event not received\n");
++ ath10k_err(ar, "wmi unified ready event not received\n");
+ status = -ETIMEDOUT;
+- goto err_disconnect_htc;
++ goto err_hif_stop;
+ }
+
+- status = ath10k_htt_attach_target(&ar->htt);
+- if (status)
+- goto err_disconnect_htc;
++ /* If firmware indicates Full Rx Reorder support it must be used in a
++ * slightly different manner. Let HTT code know.
++ */
++ ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
++ ar->wmi.svc_map));
++
++ status = ath10k_htt_rx_ring_refill(ar);
++ if (status) {
++ ath10k_err(ar, "failed to refill htt rx ring: %d\n", status);
++ goto err_hif_stop;
++ }
++
++ /* we don't care about HTT in UTF mode */
++ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
++ status = ath10k_htt_setup(&ar->htt);
++ if (status) {
++ ath10k_err(ar, "failed to setup htt: %d\n", status);
++ goto err_hif_stop;
++ }
++ }
+
+ status = ath10k_debug_start(ar);
+ if (status)
+- goto err_disconnect_htc;
+-
+- ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+- INIT_LIST_HEAD(&ar->arvifs);
++ goto err_hif_stop;
+
+- if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+- ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
+- ar->hw_params.name,
+- ar->target_version,
+- ar->chip_id,
+- ar->hw->wiphy->fw_version,
+- ar->fw_api,
+- ar->htt.target_version_major,
+- ar->htt.target_version_minor);
++ ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1;
+
+- __set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags);
++ INIT_LIST_HEAD(&ar->arvifs);
+
+ return 0;
+
+-err_disconnect_htc:
+- ath10k_htc_stop(&ar->htc);
+-err_htt_detach:
+- ath10k_htt_detach(&ar->htt);
+ err_hif_stop:
+ ath10k_hif_stop(ar);
++err_htt_rx_detach:
++ ath10k_htt_rx_free(&ar->htt);
++err_htt_tx_detach:
++ ath10k_htt_tx_free(&ar->htt);
+ err_wmi_detach:
+ ath10k_wmi_detach(ar);
+ err:
+@@ -889,14 +1156,14 @@ int ath10k_wait_for_suspend(struct ath10
+
+ ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt);
+ if (ret) {
+- ath10k_warn("could not suspend target (%d)\n", ret);
++ ath10k_warn(ar, "could not suspend target (%d)\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ);
+
+ if (ret == 0) {
+- ath10k_warn("suspend timed out - target pause event never came\n");
++ ath10k_warn(ar, "suspend timed out - target pause event never came\n");
+ return -ETIMEDOUT;
+ }
+
+@@ -908,12 +1175,14 @@ void ath10k_core_stop(struct ath10k *ar)
+ lockdep_assert_held(&ar->conf_mutex);
+
+ /* try to suspend target */
+- if (ar->state != ATH10K_STATE_RESTARTING)
++ if (ar->state != ATH10K_STATE_RESTARTING &&
++ ar->state != ATH10K_STATE_UTF)
+ ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
+
+ ath10k_debug_stop(ar);
+- ath10k_htc_stop(&ar->htc);
+- ath10k_htt_detach(&ar->htt);
++ ath10k_hif_stop(ar);
++ ath10k_htt_tx_free(&ar->htt);
++ ath10k_htt_rx_free(&ar->htt);
+ ath10k_wmi_detach(ar);
+ }
+ EXPORT_SYMBOL(ath10k_core_stop);
+@@ -929,16 +1198,15 @@ static int ath10k_core_probe_fw(struct a
+
+ ret = ath10k_hif_power_up(ar);
+ if (ret) {
+- ath10k_err("could not start pci hif (%d)\n", ret);
++ ath10k_err(ar, "could not start pci hif (%d)\n", ret);
+ return ret;
+ }
+
+ memset(&target_info, 0, sizeof(target_info));
+ ret = ath10k_bmi_get_target_info(ar, &target_info);
+ if (ret) {
+- ath10k_err("could not get target info (%d)\n", ret);
+- ath10k_hif_power_down(ar);
+- return ret;
++ ath10k_err(ar, "could not get target info (%d)\n", ret);
++ goto err_power_down;
+ }
+
+ ar->target_version = target_info.version;
+@@ -946,118 +1214,233 @@ static int ath10k_core_probe_fw(struct a
+
+ ret = ath10k_init_hw_params(ar);
+ if (ret) {
+- ath10k_err("could not get hw params (%d)\n", ret);
+- ath10k_hif_power_down(ar);
+- return ret;
++ ath10k_err(ar, "could not get hw params (%d)\n", ret);
++ goto err_power_down;
+ }
+
+ ret = ath10k_core_fetch_firmware_files(ar);
+ if (ret) {
+- ath10k_err("could not fetch firmware files (%d)\n", ret);
+- ath10k_hif_power_down(ar);
+- return ret;
++ ath10k_err(ar, "could not fetch firmware files (%d)\n", ret);
++ goto err_power_down;
++ }
++
++ ret = ath10k_core_init_firmware_features(ar);
++ if (ret) {
++ ath10k_err(ar, "fatal problem with firmware features: %d\n",
++ ret);
++ goto err_free_firmware_files;
+ }
+
+ mutex_lock(&ar->conf_mutex);
+
+- ret = ath10k_core_start(ar);
++ ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
+ if (ret) {
+- ath10k_err("could not init core (%d)\n", ret);
+- ath10k_core_free_firmware_files(ar);
+- ath10k_hif_power_down(ar);
+- mutex_unlock(&ar->conf_mutex);
+- return ret;
++ ath10k_err(ar, "could not init core (%d)\n", ret);
++ goto err_unlock;
+ }
+
++ ath10k_print_driver_info(ar);
+ ath10k_core_stop(ar);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ ath10k_hif_power_down(ar);
+ return 0;
+-}
+-
+-static int ath10k_core_check_chip_id(struct ath10k *ar)
+-{
+- u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV);
+-
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n",
+- ar->chip_id, hw_revision);
+
+- /* Check that we are not using hw1.0 (some of them have same pci id
+- * as hw2.0) before doing anything else as ath10k crashes horribly
+- * due to missing hw1.0 workarounds. */
+- switch (hw_revision) {
+- case QCA988X_HW_1_0_CHIP_ID_REV:
+- ath10k_err("ERROR: qca988x hw1.0 is not supported\n");
+- return -EOPNOTSUPP;
++err_unlock:
++ mutex_unlock(&ar->conf_mutex);
+
+- case QCA988X_HW_2_0_CHIP_ID_REV:
+- /* known hardware revision, continue normally */
+- return 0;
++err_free_firmware_files:
++ ath10k_core_free_firmware_files(ar);
+
+- default:
+- ath10k_warn("Warning: hardware revision unknown (0x%x), expect problems\n",
+- ar->chip_id);
+- return 0;
+- }
++err_power_down:
++ ath10k_hif_power_down(ar);
+
+- return 0;
++ return ret;
+ }
+
+-int ath10k_core_register(struct ath10k *ar, u32 chip_id)
++static void ath10k_core_register_work(struct work_struct *work)
+ {
++ struct ath10k *ar = container_of(work, struct ath10k, register_work);
+ int status;
+
+- ar->chip_id = chip_id;
+-
+- status = ath10k_core_check_chip_id(ar);
+- if (status) {
+- ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id);
+- return status;
+- }
+-
+ status = ath10k_core_probe_fw(ar);
+ if (status) {
+- ath10k_err("could not probe fw (%d)\n", status);
+- return status;
++ ath10k_err(ar, "could not probe fw (%d)\n", status);
++ goto err;
+ }
+
+ status = ath10k_mac_register(ar);
+ if (status) {
+- ath10k_err("could not register to mac80211 (%d)\n", status);
++ ath10k_err(ar, "could not register to mac80211 (%d)\n", status);
+ goto err_release_fw;
+ }
+
+- status = ath10k_debug_create(ar);
++ status = ath10k_debug_register(ar);
+ if (status) {
+- ath10k_err("unable to initialize debugfs\n");
++ ath10k_err(ar, "unable to initialize debugfs\n");
+ goto err_unregister_mac;
+ }
+
+- return 0;
++ status = ath10k_spectral_create(ar);
++ if (status) {
++ ath10k_err(ar, "failed to initialize spectral\n");
++ goto err_debug_destroy;
++ }
+
++ status = ath10k_thermal_register(ar);
++ if (status) {
++ ath10k_err(ar, "could not register thermal device: %d\n",
++ status);
++ goto err_spectral_destroy;
++ }
++
++ set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags);
++ return;
++
++err_spectral_destroy:
++ ath10k_spectral_destroy(ar);
++err_debug_destroy:
++ ath10k_debug_destroy(ar);
+ err_unregister_mac:
+ ath10k_mac_unregister(ar);
+ err_release_fw:
+ ath10k_core_free_firmware_files(ar);
+- return status;
++err:
++ /* TODO: It's probably a good idea to release device from the driver
++ * but calling device_release_driver() here will cause a deadlock.
++ */
++ return;
++}
++
++int ath10k_core_register(struct ath10k *ar, u32 chip_id)
++{
++ ar->chip_id = chip_id;
++ queue_work(ar->workqueue, &ar->register_work);
++
++ return 0;
+ }
+ EXPORT_SYMBOL(ath10k_core_register);
+
+ void ath10k_core_unregister(struct ath10k *ar)
+ {
++ cancel_work_sync(&ar->register_work);
++
++ if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
++ return;
++
++ ath10k_thermal_unregister(ar);
++ /* Stop spectral before unregistering from mac80211 to remove the
++ * relayfs debugfs file cleanly. Otherwise the parent debugfs tree
++ * would be already be free'd recursively, leading to a double free.
++ */
++ ath10k_spectral_destroy(ar);
++
+ /* We must unregister from mac80211 before we stop HTC and HIF.
+ * Otherwise we will fail to submit commands to FW and mac80211 will be
+ * unhappy about callback failures. */
+ ath10k_mac_unregister(ar);
+
++ ath10k_testmode_destroy(ar);
++
+ ath10k_core_free_firmware_files(ar);
+
+- ath10k_debug_destroy(ar);
++ ath10k_debug_unregister(ar);
+ }
+ EXPORT_SYMBOL(ath10k_core_unregister);
+
++struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
++ enum ath10k_bus bus,
++ enum ath10k_hw_rev hw_rev,
++ const struct ath10k_hif_ops *hif_ops)
++{
++ struct ath10k *ar;
++ int ret;
++
++ ar = ath10k_mac_create(priv_size);
++ if (!ar)
++ return NULL;
++
++ ar->ath_common.priv = ar;
++ ar->ath_common.hw = ar->hw;
++ ar->dev = dev;
++ ar->hw_rev = hw_rev;
++ ar->hif.ops = hif_ops;
++ ar->hif.bus = bus;
++
++ switch (hw_rev) {
++ case ATH10K_HW_QCA988X:
++ ar->regs = &qca988x_regs;
++ break;
++ case ATH10K_HW_QCA6174:
++ ar->regs = &qca6174_regs;
++ break;
++ default:
++ ath10k_err(ar, "unsupported core hardware revision %d\n",
++ hw_rev);
++ ret = -ENOTSUPP;
++ goto err_free_mac;
++ }
++
++ init_completion(&ar->scan.started);
++ init_completion(&ar->scan.completed);
++ init_completion(&ar->scan.on_channel);
++ init_completion(&ar->target_suspend);
++
++ init_completion(&ar->install_key_done);
++ init_completion(&ar->vdev_setup_done);
++ init_completion(&ar->thermal.wmi_sync);
++
++ INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work);
++
++ ar->workqueue = create_singlethread_workqueue("ath10k_wq");
++ if (!ar->workqueue)
++ goto err_free_mac;
++
++ mutex_init(&ar->conf_mutex);
++ spin_lock_init(&ar->data_lock);
++
++ INIT_LIST_HEAD(&ar->peers);
++ init_waitqueue_head(&ar->peer_mapping_wq);
++ init_waitqueue_head(&ar->htt.empty_tx_wq);
++ init_waitqueue_head(&ar->wmi.tx_credits_wq);
++
++ init_completion(&ar->offchan_tx_completed);
++ INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
++ skb_queue_head_init(&ar->offchan_tx_queue);
++
++ INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
++ skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
++
++ INIT_WORK(&ar->register_work, ath10k_core_register_work);
++ INIT_WORK(&ar->restart_work, ath10k_core_restart);
++
++ ret = ath10k_debug_create(ar);
++ if (ret)
++ goto err_free_wq;
++
++ return ar;
++
++err_free_wq:
++ destroy_workqueue(ar->workqueue);
++
++err_free_mac:
++ ath10k_mac_destroy(ar);
++
++ return NULL;
++}
++EXPORT_SYMBOL(ath10k_core_create);
++
++void ath10k_core_destroy(struct ath10k *ar)
++{
++ flush_workqueue(ar->workqueue);
++ destroy_workqueue(ar->workqueue);
++
++ ath10k_debug_destroy(ar);
++ ath10k_mac_destroy(ar);
++}
++EXPORT_SYMBOL(ath10k_core_destroy);
++
+ MODULE_AUTHOR("Qualcomm Atheros");
+ MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
+ MODULE_LICENSE("Dual BSD/GPL");
+--- a/drivers/net/wireless/ath/ath10k/core.h
++++ b/drivers/net/wireless/ath/ath10k/core.h
+@@ -22,6 +22,8 @@
+ #include <linux/if_ether.h>
+ #include <linux/types.h>
+ #include <linux/pci.h>
++#include <linux/uuid.h>
++#include <linux/time.h>
+
+ #include "htt.h"
+ #include "htc.h"
+@@ -31,6 +33,8 @@
+ #include "../ath.h"
+ #include "../regd.h"
+ #include "../dfs_pattern_detector.h"
++#include "spectral.h"
++#include "thermal.h"
+
+ #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
+ #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
+@@ -60,12 +64,28 @@
+
+ struct ath10k;
+
++enum ath10k_bus {
++ ATH10K_BUS_PCI,
++};
++
++static inline const char *ath10k_bus_str(enum ath10k_bus bus)
++{
++ switch (bus) {
++ case ATH10K_BUS_PCI:
++ return "pci";
++ }
++
++ return "unknown";
++}
++
+ struct ath10k_skb_cb {
+ dma_addr_t paddr;
++ u8 eid;
+ u8 vdev_id;
+
+ struct {
+ u8 tid;
++ u16 freq;
+ bool is_offchan;
+ struct ath10k_htt_txbuf *txbuf;
+ u32 txbuf_paddr;
+@@ -77,6 +97,11 @@ struct ath10k_skb_cb {
+ } bcn;
+ } __packed;
+
++struct ath10k_skb_rxcb {
++ dma_addr_t paddr;
++ struct hlist_node hlist;
++};
++
+ static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
+ {
+ BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
+@@ -84,6 +109,15 @@ static inline struct ath10k_skb_cb *ATH1
+ return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
+ }
+
++static inline struct ath10k_skb_rxcb *ATH10K_SKB_RXCB(struct sk_buff *skb)
++{
++ BUILD_BUG_ON(sizeof(struct ath10k_skb_rxcb) > sizeof(skb->cb));
++ return (struct ath10k_skb_rxcb *)skb->cb;
++}
++
++#define ATH10K_RXCB_SKB(rxcb) \
++ container_of((void *)rxcb, struct sk_buff, cb)
++
+ static inline u32 host_interest_item_address(u32 item_offset)
+ {
+ return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
+@@ -93,8 +127,6 @@ struct ath10k_bmi {
+ bool done_sent;
+ };
+
+-#define ATH10K_MAX_MEM_REQS 16
+-
+ struct ath10k_mem_chunk {
+ void *vaddr;
+ dma_addr_t paddr;
+@@ -103,26 +135,52 @@ struct ath10k_mem_chunk {
+ };
+
+ struct ath10k_wmi {
++ enum ath10k_fw_wmi_op_version op_version;
+ enum ath10k_htc_ep_id eid;
+ struct completion service_ready;
+ struct completion unified_ready;
+ wait_queue_head_t tx_credits_wq;
++ DECLARE_BITMAP(svc_map, WMI_SERVICE_MAX);
+ struct wmi_cmd_map *cmd;
+ struct wmi_vdev_param_map *vdev_param;
+ struct wmi_pdev_param_map *pdev_param;
++ const struct wmi_ops *ops;
+
+ u32 num_mem_chunks;
+- struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS];
++ struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS];
+ };
+
+-struct ath10k_peer_stat {
++struct ath10k_fw_stats_peer {
++ struct list_head list;
++
+ u8 peer_macaddr[ETH_ALEN];
+ u32 peer_rssi;
+ u32 peer_tx_rate;
+ u32 peer_rx_rate; /* 10x only */
+ };
+
+-struct ath10k_target_stats {
++struct ath10k_fw_stats_vdev {
++ struct list_head list;
++
++ u32 vdev_id;
++ u32 beacon_snr;
++ u32 data_snr;
++ u32 num_tx_frames[4];
++ u32 num_rx_frames;
++ u32 num_tx_frames_retries[4];
++ u32 num_tx_frames_failures[4];
++ u32 num_rts_fail;
++ u32 num_rts_success;
++ u32 num_rx_err;
++ u32 num_rx_discard;
++ u32 num_tx_not_acked;
++ u32 tx_rate_history[10];
++ u32 beacon_rssi_history[10];
++};
++
++struct ath10k_fw_stats_pdev {
++ struct list_head list;
++
+ /* PDEV stats */
+ s32 ch_noise_floor;
+ u32 tx_frame_count;
+@@ -177,15 +235,12 @@ struct ath10k_target_stats {
+ s32 phy_errs;
+ s32 phy_err_drop;
+ s32 mpdu_errs;
++};
+
+- /* VDEV STATS */
+-
+- /* PEER STATS */
+- u8 peers;
+- struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
+-
+- /* TODO: Beacon filter stats */
+-
++struct ath10k_fw_stats {
++ struct list_head pdevs;
++ struct list_head vdevs;
++ struct list_head peers;
+ };
+
+ struct ath10k_dfs_stats {
+@@ -203,6 +258,8 @@ struct ath10k_peer {
+ int vdev_id;
+ u8 addr[ETH_ALEN];
+ DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
++
++ /* protected by ar->data_lock */
+ struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
+ };
+
+@@ -216,10 +273,21 @@ struct ath10k_sta {
+ u32 smps;
+
+ struct work_struct update_wk;
++
++#ifdef CPTCFG_MAC80211_DEBUGFS
++ /* protected by conf_mutex */
++ bool aggr_mode;
++#endif
+ };
+
+ #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
+
++enum ath10k_beacon_state {
++ ATH10K_BEACON_SCHEDULED = 0,
++ ATH10K_BEACON_SENDING,
++ ATH10K_BEACON_SENT,
++};
++
+ struct ath10k_vif {
+ struct list_head list;
+
+@@ -230,20 +298,22 @@ struct ath10k_vif {
+ u32 dtim_period;
+ struct sk_buff *beacon;
+ /* protected by data_lock */
+- bool beacon_sent;
++ enum ath10k_beacon_state beacon_state;
++ void *beacon_buf;
++ dma_addr_t beacon_paddr;
+
+ struct ath10k *ar;
+ struct ieee80211_vif *vif;
+
+ bool is_started;
+ bool is_up;
++ bool spectral_enabled;
++ bool ps;
+ u32 aid;
+ u8 bssid[ETH_ALEN];
+
+- struct work_struct wep_key_work;
+ struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
+- u8 def_wep_key_idx;
+- u8 def_wep_key_newidx;
++ s8 def_wep_key_idx;
+
+ u16 tx_seq_no;
+
+@@ -269,6 +339,8 @@ struct ath10k_vif {
+ u8 force_sgi;
+ bool use_cts_prot;
+ int num_legacy_stations;
++ int txpower;
++ struct wmi_wmm_params_all_arg wmm_params;
+ };
+
+ struct ath10k_vif_iter {
+@@ -276,20 +348,38 @@ struct ath10k_vif_iter {
+ struct ath10k_vif *arvif;
+ };
+
++/* used for crash-dump storage, protected by data-lock */
++struct ath10k_fw_crash_data {
++ bool crashed_since_read;
++
++ uuid_le uuid;
++ struct timespec timestamp;
++ __le32 registers[REG_DUMP_COUNT_QCA988X];
++};
++
+ struct ath10k_debug {
+ struct dentry *debugfs_phy;
+
+- struct ath10k_target_stats target_stats;
+- u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+-
+- struct completion event_stats_compl;
++ struct ath10k_fw_stats fw_stats;
++ struct completion fw_stats_complete;
++ bool fw_stats_done;
+
+ unsigned long htt_stats_mask;
+ struct delayed_work htt_stats_dwork;
+ struct ath10k_dfs_stats dfs_stats;
+ struct ath_dfs_pool_stats dfs_pool_stats;
+
++ /* protected by conf_mutex */
+ u32 fw_dbglog_mask;
++ u32 fw_dbglog_level;
++ u32 pktlog_filter;
++ u32 reg_addr;
++ u32 nf_cal_period;
++
++ u8 htt_max_amsdu;
++ u8 htt_max_ampdu;
++
++ struct ath10k_fw_crash_data *fw_crash_data;
+ };
+
+ enum ath10k_state {
+@@ -312,13 +402,24 @@ enum ath10k_state {
+ * prevents completion timeouts and makes the driver more responsive to
+ * userspace commands. This is also prevents recursive recovery. */
+ ATH10K_STATE_WEDGED,
++
++ /* factory tests */
++ ATH10K_STATE_UTF,
++};
++
++enum ath10k_firmware_mode {
++ /* the default mode, standard 802.11 functionality */
++ ATH10K_FIRMWARE_MODE_NORMAL,
++
++ /* factory tests etc */
++ ATH10K_FIRMWARE_MODE_UTF,
+ };
+
+ enum ath10k_fw_features {
+ /* wmi_mgmt_rx_hdr contains extra RSSI information */
+ ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0,
+
+- /* firmware from 10X branch */
++ /* Firmware from 10X branch. Deprecated, don't use in new code. */
+ ATH10K_FW_FEATURE_WMI_10X = 1,
+
+ /* firmware support tx frame management over WMI, otherwise it's HTT */
+@@ -327,6 +428,18 @@ enum ath10k_fw_features {
+ /* Firmware does not support P2P */
+ ATH10K_FW_FEATURE_NO_P2P = 3,
+
++ /* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature
++ * bit is required to be set as well. Deprecated, don't use in new
++ * code.
++ */
++ ATH10K_FW_FEATURE_WMI_10_2 = 4,
++
++ /* Some firmware revisions lack proper multi-interface client powersave
++ * implementation. Enabling PS could result in connection drops,
++ * traffic stalls, etc.
++ */
++ ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5,
++
+ /* keep last */
+ ATH10K_FW_FEATURE_COUNT,
+ };
+@@ -334,15 +447,64 @@ enum ath10k_fw_features {
+ enum ath10k_dev_flags {
+ /* Indicates that ath10k device is during CAC phase of DFS */
+ ATH10K_CAC_RUNNING,
+- ATH10K_FLAG_FIRST_BOOT_DONE,
++ ATH10K_FLAG_CORE_REGISTERED,
++
++ /* Device has crashed and needs to restart. This indicates any pending
++ * waiters should immediately cancel instead of waiting for a time out.
++ */
++ ATH10K_FLAG_CRASH_FLUSH,
++};
++
++enum ath10k_cal_mode {
++ ATH10K_CAL_MODE_FILE,
++ ATH10K_CAL_MODE_OTP,
++ ATH10K_CAL_MODE_DT,
++};
++
++static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode)
++{
++ switch (mode) {
++ case ATH10K_CAL_MODE_FILE:
++ return "file";
++ case ATH10K_CAL_MODE_OTP:
++ return "otp";
++ case ATH10K_CAL_MODE_DT:
++ return "dt";
++ }
++
++ return "unknown";
++}
++
++enum ath10k_scan_state {
++ ATH10K_SCAN_IDLE,
++ ATH10K_SCAN_STARTING,
++ ATH10K_SCAN_RUNNING,
++ ATH10K_SCAN_ABORTING,
+ };
+
++static inline const char *ath10k_scan_state_str(enum ath10k_scan_state state)
++{
++ switch (state) {
++ case ATH10K_SCAN_IDLE:
++ return "idle";
++ case ATH10K_SCAN_STARTING:
++ return "starting";
++ case ATH10K_SCAN_RUNNING:
++ return "running";
++ case ATH10K_SCAN_ABORTING:
++ return "aborting";
++ }
++
++ return "unknown";
++}
++
+ struct ath10k {
+ struct ath_common ath_common;
+ struct ieee80211_hw *hw;
+ struct device *dev;
+ u8 mac_addr[ETH_ALEN];
+
++ enum ath10k_hw_rev hw_rev;
+ u32 chip_id;
+ u32 target_version;
+ u8 fw_version_major;
+@@ -358,18 +520,16 @@ struct ath10k {
+
+ DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT);
+
+- struct targetdef *targetdef;
+- struct hostdef *hostdef;
+-
+ bool p2p;
+
+ struct {
+- void *priv;
++ enum ath10k_bus bus;
+ const struct ath10k_hif_ops *ops;
+ } hif;
+
+ struct completion target_suspend;
+
++ const struct ath10k_hw_regs *regs;
+ struct ath10k_bmi bmi;
+ struct ath10k_wmi wmi;
+ struct ath10k_htc htc;
+@@ -379,12 +539,15 @@ struct ath10k {
+ u32 id;
+ const char *name;
+ u32 patch_load_addr;
++ int uart_pin;
+
+ struct ath10k_hw_params_fw {
+ const char *dir;
+ const char *fw;
+ const char *otp;
+ const char *board;
++ size_t board_size;
++ size_t board_ext_size;
+ } fw;
+ } hw_params;
+
+@@ -400,16 +563,18 @@ struct ath10k {
+ const void *firmware_data;
+ size_t firmware_len;
+
++ const struct firmware *cal_file;
++
+ int fw_api;
++ enum ath10k_cal_mode cal_mode;
+
+ struct {
+ struct completion started;
+ struct completion completed;
+ struct completion on_channel;
+- struct timer_list timeout;
++ struct delayed_work timeout;
++ enum ath10k_scan_state state;
+ bool is_roc;
+- bool in_progress;
+- bool aborting;
+ int vdev_id;
+ int roc_freq;
+ } scan;
+@@ -427,8 +592,7 @@ struct ath10k {
+ /* current operating channel definition */
+ struct cfg80211_chan_def chandef;
+
+- int free_vdev_map;
+- bool promisc;
++ unsigned long long free_vdev_map;
+ bool monitor;
+ int monitor_vdev_id;
+ bool monitor_started;
+@@ -440,7 +604,12 @@ struct ath10k {
+ bool radar_enabled;
+ int num_started_vdevs;
+
+- struct wmi_pdev_set_wmm_params_arg wmm_params;
++ /* Protected by conf-mutex */
++ u8 supp_tx_chainmask;
++ u8 supp_rx_chainmask;
++ u8 cfg_tx_chainmask;
++ u8 cfg_rx_chainmask;
++
+ struct completion install_key_done;
+
+ struct completion vdev_setup_done;
+@@ -457,8 +626,13 @@ struct ath10k {
+ struct list_head peers;
+ wait_queue_head_t peer_mapping_wq;
+
+- /* number of created peers; protected by data_lock */
++ /* protected by conf_mutex */
+ int num_peers;
++ int num_stations;
++
++ int max_num_peers;
++ int max_num_stations;
++ int max_num_vdevs;
+
+ struct work_struct offchan_tx_work;
+ struct sk_buff_head offchan_tx_queue;
+@@ -470,6 +644,7 @@ struct ath10k {
+
+ enum ath10k_state state;
+
++ struct work_struct register_work;
+ struct work_struct restart_work;
+
+ /* cycle count is reported twice for each visited channel during scan.
+@@ -483,13 +658,46 @@ struct ath10k {
+ #ifdef CPTCFG_ATH10K_DEBUGFS
+ struct ath10k_debug debug;
+ #endif
++
++ struct {
++ /* relay(fs) channel for spectral scan */
++ struct rchan *rfs_chan_spec_scan;
++
++ /* spectral_mode and spec_config are protected by conf_mutex */
++ enum ath10k_spectral_mode mode;
++ struct ath10k_spec_scan config;
++ } spectral;
++
++ struct {
++ /* protected by conf_mutex */
++ const struct firmware *utf;
++ DECLARE_BITMAP(orig_fw_features, ATH10K_FW_FEATURE_COUNT);
++ enum ath10k_fw_wmi_op_version orig_wmi_op_version;
++
++ /* protected by data_lock */
++ bool utf_monitor;
++ } testmode;
++
++ struct {
++ /* protected by data_lock */
++ u32 fw_crash_counter;
++ u32 fw_warm_reset_counter;
++ u32 fw_cold_reset_counter;
++ } stats;
++
++ struct ath10k_thermal thermal;
++
++ /* must be last */
++ u8 drv_priv[0] __aligned(sizeof(void *));
+ };
+
+-struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
++struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
++ enum ath10k_bus bus,
++ enum ath10k_hw_rev hw_rev,
+ const struct ath10k_hif_ops *hif_ops);
+ void ath10k_core_destroy(struct ath10k *ar);
+
+-int ath10k_core_start(struct ath10k *ar);
++int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode);
+ int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt);
+ void ath10k_core_stop(struct ath10k *ar);
+ int ath10k_core_register(struct ath10k *ar, u32 chip_id);
+--- a/drivers/net/wireless/ath/ath10k/debug.c
++++ b/drivers/net/wireless/ath/ath10k/debug.c
+@@ -17,107 +17,176 @@
+
+ #include <linux/module.h>
+ #include <linux/debugfs.h>
++#include <linux/vmalloc.h>
++#include <linux/utsname.h>
+
+ #include "core.h"
+ #include "debug.h"
++#include "hif.h"
++#include "wmi-ops.h"
+
+ /* ms */
+ #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
+
+-static int ath10k_printk(const char *level, const char *fmt, ...)
+-{
+- struct va_format vaf;
+- va_list args;
+- int rtn;
++#define ATH10K_FW_CRASH_DUMP_VERSION 1
+
+- va_start(args, fmt);
++/**
++ * enum ath10k_fw_crash_dump_type - types of data in the dump file
++ * @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format
++ */
++enum ath10k_fw_crash_dump_type {
++ ATH10K_FW_CRASH_DUMP_REGISTERS = 0,
+
+- vaf.fmt = fmt;
+- vaf.va = &args;
++ ATH10K_FW_CRASH_DUMP_MAX,
++};
+
+- rtn = printk("%sath10k: %pV", level, &vaf);
++struct ath10k_tlv_dump_data {
++ /* see ath10k_fw_crash_dump_type above */
++ __le32 type;
+
+- va_end(args);
++ /* in bytes */
++ __le32 tlv_len;
+
+- return rtn;
+-}
++ /* pad to 32-bit boundaries as needed */
++ u8 tlv_data[];
++} __packed;
++
++struct ath10k_dump_file_data {
++ /* dump file information */
++
++ /* "ATH10K-FW-DUMP" */
++ char df_magic[16];
++
++ __le32 len;
++
++ /* file dump version */
++ __le32 version;
++
++ /* some info we can get from ath10k struct that might help */
++
++ u8 uuid[16];
++
++ __le32 chip_id;
++
++ /* 0 for now, in place for later hardware */
++ __le32 bus_type;
++
++ __le32 target_version;
++ __le32 fw_version_major;
++ __le32 fw_version_minor;
++ __le32 fw_version_release;
++ __le32 fw_version_build;
++ __le32 phy_capability;
++ __le32 hw_min_tx_power;
++ __le32 hw_max_tx_power;
++ __le32 ht_cap_info;
++ __le32 vht_cap_info;
++ __le32 num_rf_chains;
++
++ /* firmware version string */
++ char fw_ver[ETHTOOL_FWVERS_LEN];
++
++ /* Kernel related information */
++
++ /* time-of-day stamp */
++ __le64 tv_sec;
++
++ /* time-of-day stamp, nano-seconds */
++ __le64 tv_nsec;
++
++ /* LINUX_VERSION_CODE */
++ __le32 kernel_ver_code;
++
++ /* VERMAGIC_STRING */
++ char kernel_ver[64];
+
+-int ath10k_info(const char *fmt, ...)
++ /* room for growth w/out changing binary format */
++ u8 unused[128];
++
++ /* struct ath10k_tlv_dump_data + more */
++ u8 data[0];
++} __packed;
++
++void ath10k_info(struct ath10k *ar, const char *fmt, ...)
+ {
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+- int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+- ret = ath10k_printk(KERN_INFO, "%pV", &vaf);
+- trace_ath10k_log_info(&vaf);
++ dev_info(ar->dev, "%pV", &vaf);
++ trace_ath10k_log_info(ar, &vaf);
+ va_end(args);
+-
+- return ret;
+ }
+ EXPORT_SYMBOL(ath10k_info);
+
+-int ath10k_err(const char *fmt, ...)
++void ath10k_print_driver_info(struct ath10k *ar)
++{
++ ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d cal %s max_sta %d\n",
++ ar->hw_params.name,
++ ar->target_version,
++ ar->chip_id,
++ ar->hw->wiphy->fw_version,
++ ar->fw_api,
++ ar->htt.target_version_major,
++ ar->htt.target_version_minor,
++ ar->wmi.op_version,
++ ath10k_cal_mode_str(ar->cal_mode),
++ ar->max_num_stations);
++ ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
++ config_enabled(CPTCFG_ATH10K_DEBUG),
++ config_enabled(CPTCFG_ATH10K_DEBUGFS),
++ config_enabled(CPTCFG_ATH10K_TRACING),
++ config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED),
++ config_enabled(CPTCFG_NL80211_TESTMODE));
++}
++EXPORT_SYMBOL(ath10k_print_driver_info);
++
++void ath10k_err(struct ath10k *ar, const char *fmt, ...)
+ {
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+- int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+- ret = ath10k_printk(KERN_ERR, "%pV", &vaf);
+- trace_ath10k_log_err(&vaf);
++ dev_err(ar->dev, "%pV", &vaf);
++ trace_ath10k_log_err(ar, &vaf);
+ va_end(args);
+-
+- return ret;
+ }
+ EXPORT_SYMBOL(ath10k_err);
+
+-int ath10k_warn(const char *fmt, ...)
++void ath10k_warn(struct ath10k *ar, const char *fmt, ...)
+ {
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+- int ret = 0;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+-
+- if (net_ratelimit())
+- ret = ath10k_printk(KERN_WARNING, "%pV", &vaf);
+-
+- trace_ath10k_log_warn(&vaf);
++ dev_warn_ratelimited(ar->dev, "%pV", &vaf);
++ trace_ath10k_log_warn(ar, &vaf);
+
+ va_end(args);
+-
+- return ret;
+ }
+ EXPORT_SYMBOL(ath10k_warn);
+
+ #ifdef CPTCFG_ATH10K_DEBUGFS
+
+-void ath10k_debug_read_service_map(struct ath10k *ar,
+- void *service_map,
+- size_t map_size)
+-{
+- memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
+-}
+-
+ static ssize_t ath10k_read_wmi_services(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+ {
+ struct ath10k *ar = file->private_data;
+ char *buf;
+- unsigned int len = 0, buf_len = 1500;
+- const char *status;
++ unsigned int len = 0, buf_len = 4096;
++ const char *name;
+ ssize_t ret_cnt;
++ bool enabled;
+ int i;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+@@ -129,16 +198,25 @@ static ssize_t ath10k_read_wmi_services(
+ if (len > buf_len)
+ len = buf_len;
+
+- for (i = 0; i < WMI_SERVICE_LAST; i++) {
+- if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i))
+- status = "enabled";
+- else
+- status = "disabled";
++ spin_lock_bh(&ar->data_lock);
++ for (i = 0; i < WMI_SERVICE_MAX; i++) {
++ enabled = test_bit(i, ar->wmi.svc_map);
++ name = wmi_service_name(i);
++
++ if (!name) {
++ if (enabled)
++ len += scnprintf(buf + len, buf_len - len,
++ "%-40s %s (bit %d)\n",
++ "unknown", "enabled", i);
++
++ continue;
++ }
+
+ len += scnprintf(buf + len, buf_len - len,
+- "0x%02x - %20s - %s\n",
+- i, wmi_service_name(i), status);
++ "%-40s %s\n",
++ name, enabled ? "enabled" : "-");
+ }
++ spin_unlock_bh(&ar->data_lock);
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+@@ -155,169 +233,221 @@ static const struct file_operations fops
+ .llseek = default_llseek,
+ };
+
+-void ath10k_debug_read_target_stats(struct ath10k *ar,
+- struct wmi_stats_event *ev)
++static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
+ {
+- u8 *tmp = ev->data;
+- struct ath10k_target_stats *stats;
+- int num_pdev_stats, num_vdev_stats, num_peer_stats;
+- struct wmi_pdev_stats_10x *ps;
+- int i;
++ struct ath10k_fw_stats_pdev *i, *tmp;
+
++ list_for_each_entry_safe(i, tmp, head, list) {
++ list_del(&i->list);
++ kfree(i);
++ }
++}
++
++static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head)
++{
++ struct ath10k_fw_stats_vdev *i, *tmp;
++
++ list_for_each_entry_safe(i, tmp, head, list) {
++ list_del(&i->list);
++ kfree(i);
++ }
++}
++
++static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
++{
++ struct ath10k_fw_stats_peer *i, *tmp;
++
++ list_for_each_entry_safe(i, tmp, head, list) {
++ list_del(&i->list);
++ kfree(i);
++ }
++}
++
++static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
++{
+ spin_lock_bh(&ar->data_lock);
++ ar->debug.fw_stats_done = false;
++ ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
++ ath10k_debug_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
++ ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
++ spin_unlock_bh(&ar->data_lock);
++}
+
+- stats = &ar->debug.target_stats;
++static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
++{
++ struct ath10k_fw_stats_peer *i;
++ size_t num = 0;
+
+- num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
+- num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
+- num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
+-
+- if (num_pdev_stats) {
+- ps = (struct wmi_pdev_stats_10x *)tmp;
+-
+- stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
+- stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
+- stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
+- stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
+- stats->cycle_count = __le32_to_cpu(ps->cycle_count);
+- stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
+- stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
+-
+- stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
+- stats->comp_delivered =
+- __le32_to_cpu(ps->wal.tx.comp_delivered);
+- stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
+- stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
+- stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
+- stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
+- stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
+- stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
+- stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
+- stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
+- stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
+- stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
+- stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
+- stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
+- stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
+- stats->sw_retry_failure =
+- __le32_to_cpu(ps->wal.tx.sw_retry_failure);
+- stats->illgl_rate_phy_err =
+- __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
+- stats->pdev_cont_xretry =
+- __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
+- stats->pdev_tx_timeout =
+- __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
+- stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
+- stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
+- stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
+-
+- stats->mid_ppdu_route_change =
+- __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
+- stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
+- stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
+- stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
+- stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
+- stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
+- stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
+- stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
+- stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
+- stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
+- stats->oversize_amsdu =
+- __le32_to_cpu(ps->wal.rx.oversize_amsdu);
+- stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
+- stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
+- stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
+-
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
+- ar->fw_features)) {
+- stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad);
+- stats->rts_bad = __le32_to_cpu(ps->rts_bad);
+- stats->rts_good = __le32_to_cpu(ps->rts_good);
+- stats->fcs_bad = __le32_to_cpu(ps->fcs_bad);
+- stats->no_beacons = __le32_to_cpu(ps->no_beacons);
+- stats->mib_int_count = __le32_to_cpu(ps->mib_int_count);
+- tmp += sizeof(struct wmi_pdev_stats_10x);
+- } else {
+- tmp += sizeof(struct wmi_pdev_stats_old);
+- }
++ list_for_each_entry(i, head, list)
++ ++num;
++
++ return num;
++}
++
++static size_t ath10k_debug_fw_stats_num_vdevs(struct list_head *head)
++{
++ struct ath10k_fw_stats_vdev *i;
++ size_t num = 0;
++
++ list_for_each_entry(i, head, list)
++ ++num;
++
++ return num;
++}
++
++void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct ath10k_fw_stats stats = {};
++ bool is_start, is_started, is_end;
++ size_t num_peers;
++ size_t num_vdevs;
++ int ret;
++
++ INIT_LIST_HEAD(&stats.pdevs);
++ INIT_LIST_HEAD(&stats.vdevs);
++ INIT_LIST_HEAD(&stats.peers);
++
++ spin_lock_bh(&ar->data_lock);
++ ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
++ if (ret) {
++ ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
++ goto unlock;
+ }
+
+- /* 0 or max vdevs */
+- /* Currently firmware does not support VDEV stats */
+- if (num_vdev_stats) {
+- struct wmi_vdev_stats *vdev_stats;
+-
+- for (i = 0; i < num_vdev_stats; i++) {
+- vdev_stats = (struct wmi_vdev_stats *)tmp;
+- tmp += sizeof(struct wmi_vdev_stats);
+- }
++ /* Stat data may exceed htc-wmi buffer limit. In such case firmware
++ * splits the stats data and delivers it in a ping-pong fashion of
++ * request cmd-update event.
++ *
++ * However there is no explicit end-of-data. Instead start-of-data is
++ * used as an implicit one. This works as follows:
++ * a) discard stat update events until one with pdev stats is
++ * delivered - this skips session started at end of (b)
++ * b) consume stat update events until another one with pdev stats is
++ * delivered which is treated as end-of-data and is itself discarded
++ */
++
++ if (ar->debug.fw_stats_done) {
++ ath10k_warn(ar, "received unsolicited stats update event\n");
++ goto free;
+ }
+
+- if (num_peer_stats) {
+- struct wmi_peer_stats_10x *peer_stats;
+- struct ath10k_peer_stat *s;
+-
+- stats->peers = num_peer_stats;
+-
+- for (i = 0; i < num_peer_stats; i++) {
+- peer_stats = (struct wmi_peer_stats_10x *)tmp;
+- s = &stats->peer_stat[i];
+-
+- memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr,
+- ETH_ALEN);
+- s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
+- s->peer_tx_rate =
+- __le32_to_cpu(peer_stats->peer_tx_rate);
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
+- ar->fw_features)) {
+- s->peer_rx_rate =
+- __le32_to_cpu(peer_stats->peer_rx_rate);
+- tmp += sizeof(struct wmi_peer_stats_10x);
+-
+- } else {
+- tmp += sizeof(struct wmi_peer_stats_old);
+- }
++ num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
++ num_vdevs = ath10k_debug_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs);
++ is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
++ !list_empty(&stats.pdevs));
++ is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
++ !list_empty(&stats.pdevs));
++
++ if (is_start)
++ list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
++
++ if (is_end)
++ ar->debug.fw_stats_done = true;
++
++ is_started = !list_empty(&ar->debug.fw_stats.pdevs);
++
++ if (is_started && !is_end) {
++ if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {
++ /* Although this is unlikely impose a sane limit to
++ * prevent firmware from DoS-ing the host.
++ */
++ ath10k_warn(ar, "dropping fw peer stats\n");
++ goto free;
+ }
++
++ if (num_vdevs >= BITS_PER_LONG) {
++ ath10k_warn(ar, "dropping fw vdev stats\n");
++ goto free;
++ }
++
++ list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
++ list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
+ }
+
++ complete(&ar->debug.fw_stats_complete);
++
++free:
++ /* In some cases lists have been spliced and cleared. Free up
++ * resources if that is not the case.
++ */
++ ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
++ ath10k_debug_fw_stats_vdevs_free(&stats.vdevs);
++ ath10k_debug_fw_stats_peers_free(&stats.peers);
++
++unlock:
+ spin_unlock_bh(&ar->data_lock);
+- complete(&ar->debug.event_stats_compl);
+ }
+
+-static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+- size_t count, loff_t *ppos)
++static int ath10k_debug_fw_stats_request(struct ath10k *ar)
+ {
+- struct ath10k *ar = file->private_data;
+- struct ath10k_target_stats *fw_stats;
+- char *buf = NULL;
+- unsigned int len = 0, buf_len = 8000;
+- ssize_t ret_cnt = 0;
+- long left;
+- int i;
++ unsigned long timeout;
+ int ret;
+
+- fw_stats = &ar->debug.target_stats;
++ lockdep_assert_held(&ar->conf_mutex);
+
+- mutex_lock(&ar->conf_mutex);
++ timeout = jiffies + msecs_to_jiffies(1*HZ);
+
+- if (ar->state != ATH10K_STATE_ON)
+- goto exit;
++ ath10k_debug_fw_stats_reset(ar);
+
+- buf = kzalloc(buf_len, GFP_KERNEL);
+- if (!buf)
+- goto exit;
++ for (;;) {
++ if (time_after(jiffies, timeout))
++ return -ETIMEDOUT;
+
+- ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+- if (ret) {
+- ath10k_warn("could not request stats (%d)\n", ret);
+- goto exit;
++ reinit_completion(&ar->debug.fw_stats_complete);
++
++ ret = ath10k_wmi_request_stats(ar,
++ WMI_STAT_PDEV |
++ WMI_STAT_VDEV |
++ WMI_STAT_PEER);
++ if (ret) {
++ ath10k_warn(ar, "could not request stats (%d)\n", ret);
++ return ret;
++ }
++
++ ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete,
++ 1*HZ);
++ if (ret == 0)
++ return -ETIMEDOUT;
++
++ spin_lock_bh(&ar->data_lock);
++ if (ar->debug.fw_stats_done) {
++ spin_unlock_bh(&ar->data_lock);
++ break;
++ }
++ spin_unlock_bh(&ar->data_lock);
+ }
+
+- left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+- if (left <= 0)
+- goto exit;
++ return 0;
++}
++
++/* FIXME: How to calculate the buffer size sanely? */
++#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
++
++static void ath10k_fw_stats_fill(struct ath10k *ar,
++ struct ath10k_fw_stats *fw_stats,
++ char *buf)
++{
++ unsigned int len = 0;
++ unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
++ const struct ath10k_fw_stats_pdev *pdev;
++ const struct ath10k_fw_stats_vdev *vdev;
++ const struct ath10k_fw_stats_peer *peer;
++ size_t num_peers;
++ size_t num_vdevs;
++ int i;
+
+ spin_lock_bh(&ar->data_lock);
++
++ pdev = list_first_entry_or_null(&fw_stats->pdevs,
++ struct ath10k_fw_stats_pdev, list);
++ if (!pdev) {
++ ath10k_warn(ar, "failed to get pdev stats\n");
++ goto unlock;
++ }
++
++ num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
++ num_vdevs = ath10k_debug_fw_stats_num_vdevs(&fw_stats->vdevs);
++
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath10k PDEV stats");
+@@ -325,29 +455,29 @@ static ssize_t ath10k_read_fw_stats(stru
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Channel noise floor", fw_stats->ch_noise_floor);
++ "Channel noise floor", pdev->ch_noise_floor);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "Channel TX power", fw_stats->chan_tx_power);
++ "Channel TX power", pdev->chan_tx_power);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "TX frame count", fw_stats->tx_frame_count);
++ "TX frame count", pdev->tx_frame_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "RX frame count", fw_stats->rx_frame_count);
++ "RX frame count", pdev->rx_frame_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "RX clear count", fw_stats->rx_clear_count);
++ "RX clear count", pdev->rx_clear_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "Cycle count", fw_stats->cycle_count);
++ "Cycle count", pdev->cycle_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "PHY error count", fw_stats->phy_err_count);
++ "PHY error count", pdev->phy_err_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "RTS bad count", fw_stats->rts_bad);
++ "RTS bad count", pdev->rts_bad);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "RTS good count", fw_stats->rts_good);
++ "RTS good count", pdev->rts_good);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "FCS bad count", fw_stats->fcs_bad);
++ "FCS bad count", pdev->fcs_bad);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "No beacon count", fw_stats->no_beacons);
++ "No beacon count", pdev->no_beacons);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+- "MIB int count", fw_stats->mib_int_count);
++ "MIB int count", pdev->mib_int_count);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+@@ -356,51 +486,51 @@ static ssize_t ath10k_read_fw_stats(stru
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "HTT cookies queued", fw_stats->comp_queued);
++ "HTT cookies queued", pdev->comp_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "HTT cookies disp.", fw_stats->comp_delivered);
++ "HTT cookies disp.", pdev->comp_delivered);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MSDU queued", fw_stats->msdu_enqued);
++ "MSDU queued", pdev->msdu_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MPDU queued", fw_stats->mpdu_enqued);
++ "MPDU queued", pdev->mpdu_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MSDUs dropped", fw_stats->wmm_drop);
++ "MSDUs dropped", pdev->wmm_drop);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Local enqued", fw_stats->local_enqued);
++ "Local enqued", pdev->local_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Local freed", fw_stats->local_freed);
++ "Local freed", pdev->local_freed);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "HW queued", fw_stats->hw_queued);
++ "HW queued", pdev->hw_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "PPDUs reaped", fw_stats->hw_reaped);
++ "PPDUs reaped", pdev->hw_reaped);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Num underruns", fw_stats->underrun);
++ "Num underruns", pdev->underrun);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "PPDUs cleaned", fw_stats->tx_abort);
++ "PPDUs cleaned", pdev->tx_abort);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MPDUs requed", fw_stats->mpdus_requed);
++ "MPDUs requed", pdev->mpdus_requed);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Excessive retries", fw_stats->tx_ko);
++ "Excessive retries", pdev->tx_ko);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "HW rate", fw_stats->data_rc);
++ "HW rate", pdev->data_rc);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Sched self tiggers", fw_stats->self_triggers);
++ "Sched self tiggers", pdev->self_triggers);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Dropped due to SW retries",
+- fw_stats->sw_retry_failure);
++ pdev->sw_retry_failure);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Illegal rate phy errors",
+- fw_stats->illgl_rate_phy_err);
++ pdev->illgl_rate_phy_err);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Pdev continous xretry", fw_stats->pdev_cont_xretry);
++ "Pdev continous xretry", pdev->pdev_cont_xretry);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "TX timeout", fw_stats->pdev_tx_timeout);
++ "TX timeout", pdev->pdev_tx_timeout);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "PDEV resets", fw_stats->pdev_resets);
++ "PDEV resets", pdev->pdev_resets);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "PHY underrun", fw_stats->phy_underrun);
++ "PHY underrun", pdev->phy_underrun);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MPDU is more than txop limit", fw_stats->txop_ovf);
++ "MPDU is more than txop limit", pdev->txop_ovf);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+@@ -410,84 +540,254 @@ static ssize_t ath10k_read_fw_stats(stru
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Mid PPDU route change",
+- fw_stats->mid_ppdu_route_change);
++ pdev->mid_ppdu_route_change);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Tot. number of statuses", fw_stats->status_rcvd);
++ "Tot. number of statuses", pdev->status_rcvd);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Extra frags on rings 0", fw_stats->r0_frags);
++ "Extra frags on rings 0", pdev->r0_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Extra frags on rings 1", fw_stats->r1_frags);
++ "Extra frags on rings 1", pdev->r1_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Extra frags on rings 2", fw_stats->r2_frags);
++ "Extra frags on rings 2", pdev->r2_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Extra frags on rings 3", fw_stats->r3_frags);
++ "Extra frags on rings 3", pdev->r3_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MSDUs delivered to HTT", fw_stats->htt_msdus);
++ "MSDUs delivered to HTT", pdev->htt_msdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MPDUs delivered to HTT", fw_stats->htt_mpdus);
++ "MPDUs delivered to HTT", pdev->htt_mpdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MSDUs delivered to stack", fw_stats->loc_msdus);
++ "MSDUs delivered to stack", pdev->loc_msdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MPDUs delivered to stack", fw_stats->loc_mpdus);
++ "MPDUs delivered to stack", pdev->loc_mpdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "Oversized AMSUs", fw_stats->oversize_amsdu);
++ "Oversized AMSUs", pdev->oversize_amsdu);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "PHY errors", fw_stats->phy_errs);
++ "PHY errors", pdev->phy_errs);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "PHY errors drops", fw_stats->phy_err_drop);
++ "PHY errors drops", pdev->phy_err_drop);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+- "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
++ "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
++
++ len += scnprintf(buf + len, buf_len - len, "\n");
++ len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
++ "ath10k VDEV stats", num_vdevs);
++ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
++ "=================");
++
++ list_for_each_entry(vdev, &fw_stats->vdevs, list) {
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "vdev id", vdev->vdev_id);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "beacon snr", vdev->beacon_snr);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "data snr", vdev->data_snr);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "num rx frames", vdev->num_rx_frames);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "num rts fail", vdev->num_rts_fail);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "num rts success", vdev->num_rts_success);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "num rx err", vdev->num_rx_err);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "num rx discard", vdev->num_rx_discard);
++ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
++ "num tx not acked", vdev->num_tx_not_acked);
++
++ for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++)
++ len += scnprintf(buf + len, buf_len - len,
++ "%25s [%02d] %u\n",
++ "num tx frames", i,
++ vdev->num_tx_frames[i]);
++
++ for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++)
++ len += scnprintf(buf + len, buf_len - len,
++ "%25s [%02d] %u\n",
++ "num tx frames retries", i,
++ vdev->num_tx_frames_retries[i]);
++
++ for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++)
++ len += scnprintf(buf + len, buf_len - len,
++ "%25s [%02d] %u\n",
++ "num tx frames failures", i,
++ vdev->num_tx_frames_failures[i]);
++
++ for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++)
++ len += scnprintf(buf + len, buf_len - len,
++ "%25s [%02d] 0x%08x\n",
++ "tx rate history", i,
++ vdev->tx_rate_history[i]);
++
++ for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++)
++ len += scnprintf(buf + len, buf_len - len,
++ "%25s [%02d] %u\n",
++ "beacon rssi history", i,
++ vdev->beacon_rssi_history[i]);
++
++ len += scnprintf(buf + len, buf_len - len, "\n");
++ }
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+- len += scnprintf(buf + len, buf_len - len, "%30s (%d)\n",
+- "ath10k PEER stats", fw_stats->peers);
++ len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
++ "ath10k PEER stats", num_peers);
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+- for (i = 0; i < fw_stats->peers; i++) {
++ list_for_each_entry(peer, &fw_stats->peers, list) {
+ len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+- "Peer MAC address",
+- fw_stats->peer_stat[i].peer_macaddr);
++ "Peer MAC address", peer->peer_macaddr);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+- "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
++ "Peer RSSI", peer->peer_rssi);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+- "Peer TX rate",
+- fw_stats->peer_stat[i].peer_tx_rate);
++ "Peer TX rate", peer->peer_tx_rate);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+- "Peer RX rate",
+- fw_stats->peer_stat[i].peer_rx_rate);
++ "Peer RX rate", peer->peer_rx_rate);
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
++
++unlock:
+ spin_unlock_bh(&ar->data_lock);
+
+- if (len > buf_len)
+- len = buf_len;
++ if (len >= buf_len)
++ buf[len - 1] = 0;
++ else
++ buf[len] = 0;
++}
+
+- ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
++{
++ struct ath10k *ar = inode->i_private;
++ void *buf = NULL;
++ int ret;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_ON) {
++ ret = -ENETDOWN;
++ goto err_unlock;
++ }
++
++ buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);
++ if (!buf) {
++ ret = -ENOMEM;
++ goto err_unlock;
++ }
++
++ ret = ath10k_debug_fw_stats_request(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
++ goto err_free;
++ }
++
++ ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
++ file->private_data = buf;
+
+-exit:
+ mutex_unlock(&ar->conf_mutex);
+- kfree(buf);
+- return ret_cnt;
++ return 0;
++
++err_free:
++ vfree(buf);
++
++err_unlock:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static int ath10k_fw_stats_release(struct inode *inode, struct file *file)
++{
++ vfree(file->private_data);
++
++ return 0;
++}
++
++static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ const char *buf = file->private_data;
++ unsigned int len = strlen(buf);
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ }
+
+ static const struct file_operations fops_fw_stats = {
+- .read = ath10k_read_fw_stats,
++ .open = ath10k_fw_stats_open,
++ .release = ath10k_fw_stats_release,
++ .read = ath10k_fw_stats_read,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ int ret, len, buf_len;
++ char *buf;
++
++ buf_len = 500;
++ buf = kmalloc(buf_len, GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ spin_lock_bh(&ar->data_lock);
++
++ len = 0;
++ len += scnprintf(buf + len, buf_len - len,
++ "fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);
++ len += scnprintf(buf + len, buf_len - len,
++ "fw_warm_reset_counter\t\t%d\n",
++ ar->stats.fw_warm_reset_counter);
++ len += scnprintf(buf + len, buf_len - len,
++ "fw_cold_reset_counter\t\t%d\n",
++ ar->stats.fw_cold_reset_counter);
++
++ spin_unlock_bh(&ar->data_lock);
++
++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++ kfree(buf);
++
++ return ret;
++}
++
++static const struct file_operations fops_fw_reset_stats = {
+ .open = simple_open,
++ .read = ath10k_debug_fw_reset_stats_read,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+ };
+
++/* This is a clean assert crash in firmware. */
++static int ath10k_debug_fw_assert(struct ath10k *ar)
++{
++ struct wmi_vdev_install_key_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + 16);
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
++ memset(cmd, 0, sizeof(*cmd));
++
++ /* big enough number so that firmware asserts */
++ cmd->vdev_id = __cpu_to_le32(0x7ffe);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->vdev_install_key_cmdid);
++}
++
+ static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+ {
+- const char buf[] = "To simulate firmware crash write one of the"
+- " keywords to this file:\n `soft` - this will send"
+- " WMI_FORCE_FW_HANG_ASSERT to firmware if FW"
+- " supports that command.\n `hard` - this will send"
+- " to firmware command with illegal parameters"
+- " causing firmware crash.\n";
++ const char buf[] =
++ "To simulate firmware crash write one of the keywords to this file:\n"
++ "`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"
++ "`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"
++ "`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"
++ "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+ }
+@@ -527,19 +827,30 @@ static ssize_t ath10k_write_simulate_fw_
+ }
+
+ if (!strcmp(buf, "soft")) {
+- ath10k_info("simulating soft firmware crash\n");
++ ath10k_info(ar, "simulating soft firmware crash\n");
+ ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+ } else if (!strcmp(buf, "hard")) {
+- ath10k_info("simulating hard firmware crash\n");
+- ret = ath10k_wmi_vdev_set_param(ar, TARGET_NUM_VDEVS + 1,
+- ar->wmi.vdev_param->rts_threshold, 0);
++ ath10k_info(ar, "simulating hard firmware crash\n");
++ /* 0x7fff is vdev id, and it is always out of range for all
++ * firmware variants in order to force a firmware crash.
++ */
++ ret = ath10k_wmi_vdev_set_param(ar, 0x7fff,
++ ar->wmi.vdev_param->rts_threshold,
++ 0);
++ } else if (!strcmp(buf, "assert")) {
++ ath10k_info(ar, "simulating firmware assert crash\n");
++ ret = ath10k_debug_fw_assert(ar);
++ } else if (!strcmp(buf, "hw-restart")) {
++ ath10k_info(ar, "user requested hw restart\n");
++ queue_work(ar->workqueue, &ar->restart_work);
++ ret = 0;
+ } else {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (ret) {
+- ath10k_warn("failed to simulate firmware crash: %d\n", ret);
++ ath10k_warn(ar, "failed to simulate firmware crash: %d\n", ret);
+ goto exit;
+ }
+
+@@ -565,13 +876,375 @@ static ssize_t ath10k_read_chip_id(struc
+ unsigned int len;
+ char buf[50];
+
+- len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id);
++ len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id);
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_chip_id = {
++ .read = ath10k_read_chip_id,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++struct ath10k_fw_crash_data *
++ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
++{
++ struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data;
++
++ lockdep_assert_held(&ar->data_lock);
++
++ crash_data->crashed_since_read = true;
++ uuid_le_gen(&crash_data->uuid);
++ getnstimeofday(&crash_data->timestamp);
++
++ return crash_data;
++}
++EXPORT_SYMBOL(ath10k_debug_get_new_fw_crash_data);
++
++static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar)
++{
++ struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data;
++ struct ath10k_dump_file_data *dump_data;
++ struct ath10k_tlv_dump_data *dump_tlv;
++ int hdr_len = sizeof(*dump_data);
++ unsigned int len, sofar = 0;
++ unsigned char *buf;
++
++ len = hdr_len;
++ len += sizeof(*dump_tlv) + sizeof(crash_data->registers);
++
++ sofar += hdr_len;
++
++ /* This is going to get big when we start dumping FW RAM and such,
++ * so go ahead and use vmalloc.
++ */
++ buf = vzalloc(len);
++ if (!buf)
++ return NULL;
++
++ spin_lock_bh(&ar->data_lock);
++
++ if (!crash_data->crashed_since_read) {
++ spin_unlock_bh(&ar->data_lock);
++ vfree(buf);
++ return NULL;
++ }
++
++ dump_data = (struct ath10k_dump_file_data *)(buf);
++ strlcpy(dump_data->df_magic, "ATH10K-FW-DUMP",
++ sizeof(dump_data->df_magic));
++ dump_data->len = cpu_to_le32(len);
++
++ dump_data->version = cpu_to_le32(ATH10K_FW_CRASH_DUMP_VERSION);
++
++ memcpy(dump_data->uuid, &crash_data->uuid, sizeof(dump_data->uuid));
++ dump_data->chip_id = cpu_to_le32(ar->chip_id);
++ dump_data->bus_type = cpu_to_le32(0);
++ dump_data->target_version = cpu_to_le32(ar->target_version);
++ dump_data->fw_version_major = cpu_to_le32(ar->fw_version_major);
++ dump_data->fw_version_minor = cpu_to_le32(ar->fw_version_minor);
++ dump_data->fw_version_release = cpu_to_le32(ar->fw_version_release);
++ dump_data->fw_version_build = cpu_to_le32(ar->fw_version_build);
++ dump_data->phy_capability = cpu_to_le32(ar->phy_capability);
++ dump_data->hw_min_tx_power = cpu_to_le32(ar->hw_min_tx_power);
++ dump_data->hw_max_tx_power = cpu_to_le32(ar->hw_max_tx_power);
++ dump_data->ht_cap_info = cpu_to_le32(ar->ht_cap_info);
++ dump_data->vht_cap_info = cpu_to_le32(ar->vht_cap_info);
++ dump_data->num_rf_chains = cpu_to_le32(ar->num_rf_chains);
++
++ strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version,
++ sizeof(dump_data->fw_ver));
++
++ dump_data->kernel_ver_code = 0;
++ strlcpy(dump_data->kernel_ver, init_utsname()->release,
++ sizeof(dump_data->kernel_ver));
++
++ dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec);
++ dump_data->tv_nsec = cpu_to_le64(crash_data->timestamp.tv_nsec);
++
++ /* Gather crash-dump */
++ dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar);
++ dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_REGISTERS);
++ dump_tlv->tlv_len = cpu_to_le32(sizeof(crash_data->registers));
++ memcpy(dump_tlv->tlv_data, &crash_data->registers,
++ sizeof(crash_data->registers));
++ sofar += sizeof(*dump_tlv) + sizeof(crash_data->registers);
++
++ ar->debug.fw_crash_data->crashed_since_read = false;
++
++ spin_unlock_bh(&ar->data_lock);
++
++ return dump_data;
++}
++
++static int ath10k_fw_crash_dump_open(struct inode *inode, struct file *file)
++{
++ struct ath10k *ar = inode->i_private;
++ struct ath10k_dump_file_data *dump;
++
++ dump = ath10k_build_dump_file(ar);
++ if (!dump)
++ return -ENODATA;
++
++ file->private_data = dump;
++
++ return 0;
++}
++
++static ssize_t ath10k_fw_crash_dump_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k_dump_file_data *dump_file = file->private_data;
++
++ return simple_read_from_buffer(user_buf, count, ppos,
++ dump_file,
++ le32_to_cpu(dump_file->len));
++}
++
++static int ath10k_fw_crash_dump_release(struct inode *inode,
++ struct file *file)
++{
++ vfree(file->private_data);
++
++ return 0;
++}
++
++static const struct file_operations fops_fw_crash_dump = {
++ .open = ath10k_fw_crash_dump_open,
++ .read = ath10k_fw_crash_dump_read,
++ .release = ath10k_fw_crash_dump_release,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_reg_addr_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u8 buf[32];
++ unsigned int len = 0;
++ u32 reg_addr;
++
++ mutex_lock(&ar->conf_mutex);
++ reg_addr = ar->debug.reg_addr;
++ mutex_unlock(&ar->conf_mutex);
++
++ len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t ath10k_reg_addr_write(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u32 reg_addr;
++ int ret;
++
++ ret = kstrtou32_from_user(user_buf, count, 0, &reg_addr);
++ if (ret)
++ return ret;
++
++ if (!IS_ALIGNED(reg_addr, 4))
++ return -EFAULT;
++
++ mutex_lock(&ar->conf_mutex);
++ ar->debug.reg_addr = reg_addr;
++ mutex_unlock(&ar->conf_mutex);
++
++ return count;
++}
++
++static const struct file_operations fops_reg_addr = {
++ .read = ath10k_reg_addr_read,
++ .write = ath10k_reg_addr_write,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_reg_value_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u8 buf[48];
++ unsigned int len;
++ u32 reg_addr, reg_val;
++ int ret;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_ON &&
++ ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto exit;
++ }
++
++ reg_addr = ar->debug.reg_addr;
++
++ reg_val = ath10k_hif_read32(ar, reg_addr);
++ len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);
++
++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
++
++exit:
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
++}
++
++static ssize_t ath10k_reg_value_write(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u32 reg_addr, reg_val;
++ int ret;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_ON &&
++ ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto exit;
++ }
++
++ reg_addr = ar->debug.reg_addr;
++
++ ret = kstrtou32_from_user(user_buf, count, 0, &reg_val);
++ if (ret)
++ goto exit;
++
++ ath10k_hif_write32(ar, reg_addr, reg_val);
++
++ ret = count;
++
++exit:
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
++}
++
++static const struct file_operations fops_reg_value = {
++ .read = ath10k_reg_value_read,
++ .write = ath10k_reg_value_write,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_mem_value_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u8 *buf;
++ int ret;
++
++ if (*ppos < 0)
++ return -EINVAL;
++
++ if (!count)
++ return 0;
++
++ mutex_lock(&ar->conf_mutex);
++
++ buf = vmalloc(count);
++ if (!buf) {
++ ret = -ENOMEM;
++ goto exit;
++ }
++
++ if (ar->state != ATH10K_STATE_ON &&
++ ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto exit;
++ }
++
++ ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
++ if (ret) {
++ ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
++ (u32)(*ppos), ret);
++ goto exit;
++ }
++
++ ret = copy_to_user(user_buf, buf, count);
++ if (ret) {
++ ret = -EFAULT;
++ goto exit;
++ }
++
++ count -= ret;
++ *ppos += count;
++ ret = count;
++
++exit:
++ vfree(buf);
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
++}
++
++static ssize_t ath10k_mem_value_write(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u8 *buf;
++ int ret;
++
++ if (*ppos < 0)
++ return -EINVAL;
++
++ if (!count)
++ return 0;
++
++ mutex_lock(&ar->conf_mutex);
++
++ buf = vmalloc(count);
++ if (!buf) {
++ ret = -ENOMEM;
++ goto exit;
++ }
++
++ if (ar->state != ATH10K_STATE_ON &&
++ ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto exit;
++ }
++
++ ret = copy_from_user(buf, user_buf, count);
++ if (ret) {
++ ret = -EFAULT;
++ goto exit;
++ }
++
++ ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
++ if (ret) {
++ ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
++ (u32)(*ppos), ret);
++ goto exit;
++ }
++
++ *ppos += count;
++ ret = count;
+
+- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++exit:
++ vfree(buf);
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
+ }
+
+-static const struct file_operations fops_chip_id = {
+- .read = ath10k_read_chip_id,
++static const struct file_operations fops_mem_value = {
++ .read = ath10k_mem_value_read,
++ .write = ath10k_mem_value_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+@@ -596,7 +1269,7 @@ static int ath10k_debug_htt_stats_req(st
+ ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,
+ cookie);
+ if (ret) {
+- ath10k_warn("failed to send htt stats request: %d\n", ret);
++ ath10k_warn(ar, "failed to send htt stats request: %d\n", ret);
+ return ret;
+ }
+
+@@ -619,8 +1292,8 @@ static void ath10k_debug_htt_stats_dwork
+ }
+
+ static ssize_t ath10k_read_htt_stats_mask(struct file *file,
+- char __user *user_buf,
+- size_t count, loff_t *ppos)
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
+ {
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+@@ -632,8 +1305,8 @@ static ssize_t ath10k_read_htt_stats_mas
+ }
+
+ static ssize_t ath10k_write_htt_stats_mask(struct file *file,
+- const char __user *user_buf,
+- size_t count, loff_t *ppos)
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
+ {
+ struct ath10k *ar = file->private_data;
+ unsigned long mask;
+@@ -671,16 +1344,82 @@ static const struct file_operations fops
+ .llseek = default_llseek,
+ };
+
++static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ char buf[64];
++ u8 amsdu = 3, ampdu = 64;
++ unsigned int len;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->debug.htt_max_amsdu)
++ amsdu = ar->debug.htt_max_amsdu;
++
++ if (ar->debug.htt_max_ampdu)
++ ampdu = ar->debug.htt_max_ampdu;
++
++ mutex_unlock(&ar->conf_mutex);
++
++ len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ int res;
++ char buf[64];
++ unsigned int amsdu, ampdu;
++
++ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
++
++ /* make sure that buf is null terminated */
++ buf[sizeof(buf) - 1] = 0;
++
++ res = sscanf(buf, "%u %u", &amsdu, &ampdu);
++
++ if (res != 2)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++
++ res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);
++ if (res)
++ goto out;
++
++ res = count;
++ ar->debug.htt_max_amsdu = amsdu;
++ ar->debug.htt_max_ampdu = ampdu;
++
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return res;
++}
++
++static const struct file_operations fops_htt_max_amsdu_ampdu = {
++ .read = ath10k_read_htt_max_amsdu_ampdu,
++ .write = ath10k_write_htt_max_amsdu_ampdu,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
+ static ssize_t ath10k_read_fw_dbglog(struct file *file,
+- char __user *user_buf,
+- size_t count, loff_t *ppos)
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
+ {
+ struct ath10k *ar = file->private_data;
+ unsigned int len;
+- char buf[32];
++ char buf[64];
+
+- len = scnprintf(buf, sizeof(buf), "0x%08x\n",
+- ar->debug.fw_dbglog_mask);
++ len = scnprintf(buf, sizeof(buf), "0x%08x %u\n",
++ ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ }
+@@ -690,21 +1429,34 @@ static ssize_t ath10k_write_fw_dbglog(st
+ size_t count, loff_t *ppos)
+ {
+ struct ath10k *ar = file->private_data;
+- unsigned long mask;
+ int ret;
++ char buf[64];
++ unsigned int log_level, mask;
+
+- ret = kstrtoul_from_user(user_buf, count, 0, &mask);
+- if (ret)
+- return ret;
++ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
++
++ /* make sure that buf is null terminated */
++ buf[sizeof(buf) - 1] = 0;
++
++ ret = sscanf(buf, "%x %u", &mask, &log_level);
++
++ if (!ret)
++ return -EINVAL;
++
++ if (ret == 1)
++ /* default if user did not specify */
++ log_level = ATH10K_DBGLOG_LEVEL_WARN;
+
+ mutex_lock(&ar->conf_mutex);
+
+ ar->debug.fw_dbglog_mask = mask;
++ ar->debug.fw_dbglog_level = log_level;
+
+ if (ar->state == ATH10K_STATE_ON) {
+- ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
++ ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
++ ar->debug.fw_dbglog_level);
+ if (ret) {
+- ath10k_warn("dbglog cfg failed from debugfs: %d\n",
++ ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n",
+ ret);
+ goto exit;
+ }
+@@ -718,6 +1470,166 @@ exit:
+ return ret;
+ }
+
++/* TODO: Would be nice to always support ethtool stats, would need to
++ * move the stats storage out of ath10k_debug, or always have ath10k_debug
++ * struct available..
++ */
++
++/* This generally cooresponds to the debugfs fw_stats file */
++static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = {
++ "tx_pkts_nic",
++ "tx_bytes_nic",
++ "rx_pkts_nic",
++ "rx_bytes_nic",
++ "d_noise_floor",
++ "d_cycle_count",
++ "d_phy_error",
++ "d_rts_bad",
++ "d_rts_good",
++ "d_tx_power", /* in .5 dbM I think */
++ "d_rx_crc_err", /* fcs_bad */
++ "d_no_beacon",
++ "d_tx_mpdus_queued",
++ "d_tx_msdu_queued",
++ "d_tx_msdu_dropped",
++ "d_local_enqued",
++ "d_local_freed",
++ "d_tx_ppdu_hw_queued",
++ "d_tx_ppdu_reaped",
++ "d_tx_fifo_underrun",
++ "d_tx_ppdu_abort",
++ "d_tx_mpdu_requed",
++ "d_tx_excessive_retries",
++ "d_tx_hw_rate",
++ "d_tx_dropped_sw_retries",
++ "d_tx_illegal_rate",
++ "d_tx_continuous_xretries",
++ "d_tx_timeout",
++ "d_tx_mpdu_txop_limit",
++ "d_pdev_resets",
++ "d_rx_mid_ppdu_route_change",
++ "d_rx_status",
++ "d_rx_extra_frags_ring0",
++ "d_rx_extra_frags_ring1",
++ "d_rx_extra_frags_ring2",
++ "d_rx_extra_frags_ring3",
++ "d_rx_msdu_htt",
++ "d_rx_mpdu_htt",
++ "d_rx_msdu_stack",
++ "d_rx_mpdu_stack",
++ "d_rx_phy_err",
++ "d_rx_phy_err_drops",
++ "d_rx_mpdu_errors", /* FCS, MIC, ENC */
++ "d_fw_crash_count",
++ "d_fw_warm_reset_count",
++ "d_fw_cold_reset_count",
++};
++
++#define ATH10K_SSTATS_LEN ARRAY_SIZE(ath10k_gstrings_stats)
++
++void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ u32 sset, u8 *data)
++{
++ if (sset == ETH_SS_STATS)
++ memcpy(data, *ath10k_gstrings_stats,
++ sizeof(ath10k_gstrings_stats));
++}
++
++int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif, int sset)
++{
++ if (sset == ETH_SS_STATS)
++ return ATH10K_SSTATS_LEN;
++
++ return 0;
++}
++
++void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct ethtool_stats *stats, u64 *data)
++{
++ struct ath10k *ar = hw->priv;
++ static const struct ath10k_fw_stats_pdev zero_stats = {};
++ const struct ath10k_fw_stats_pdev *pdev_stats;
++ int i = 0, ret;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state == ATH10K_STATE_ON) {
++ ret = ath10k_debug_fw_stats_request(ar);
++ if (ret) {
++ /* just print a warning and try to use older results */
++ ath10k_warn(ar,
++ "failed to get fw stats for ethtool: %d\n",
++ ret);
++ }
++ }
++
++ pdev_stats = list_first_entry_or_null(&ar->debug.fw_stats.pdevs,
++ struct ath10k_fw_stats_pdev,
++ list);
++ if (!pdev_stats) {
++ /* no results available so just return zeroes */
++ pdev_stats = &zero_stats;
++ }
++
++ spin_lock_bh(&ar->data_lock);
++
++ data[i++] = pdev_stats->hw_reaped; /* ppdu reaped */
++ data[i++] = 0; /* tx bytes */
++ data[i++] = pdev_stats->htt_mpdus;
++ data[i++] = 0; /* rx bytes */
++ data[i++] = pdev_stats->ch_noise_floor;
++ data[i++] = pdev_stats->cycle_count;
++ data[i++] = pdev_stats->phy_err_count;
++ data[i++] = pdev_stats->rts_bad;
++ data[i++] = pdev_stats->rts_good;
++ data[i++] = pdev_stats->chan_tx_power;
++ data[i++] = pdev_stats->fcs_bad;
++ data[i++] = pdev_stats->no_beacons;
++ data[i++] = pdev_stats->mpdu_enqued;
++ data[i++] = pdev_stats->msdu_enqued;
++ data[i++] = pdev_stats->wmm_drop;
++ data[i++] = pdev_stats->local_enqued;
++ data[i++] = pdev_stats->local_freed;
++ data[i++] = pdev_stats->hw_queued;
++ data[i++] = pdev_stats->hw_reaped;
++ data[i++] = pdev_stats->underrun;
++ data[i++] = pdev_stats->tx_abort;
++ data[i++] = pdev_stats->mpdus_requed;
++ data[i++] = pdev_stats->tx_ko;
++ data[i++] = pdev_stats->data_rc;
++ data[i++] = pdev_stats->sw_retry_failure;
++ data[i++] = pdev_stats->illgl_rate_phy_err;
++ data[i++] = pdev_stats->pdev_cont_xretry;
++ data[i++] = pdev_stats->pdev_tx_timeout;
++ data[i++] = pdev_stats->txop_ovf;
++ data[i++] = pdev_stats->pdev_resets;
++ data[i++] = pdev_stats->mid_ppdu_route_change;
++ data[i++] = pdev_stats->status_rcvd;
++ data[i++] = pdev_stats->r0_frags;
++ data[i++] = pdev_stats->r1_frags;
++ data[i++] = pdev_stats->r2_frags;
++ data[i++] = pdev_stats->r3_frags;
++ data[i++] = pdev_stats->htt_msdus;
++ data[i++] = pdev_stats->htt_mpdus;
++ data[i++] = pdev_stats->loc_msdus;
++ data[i++] = pdev_stats->loc_mpdus;
++ data[i++] = pdev_stats->phy_errs;
++ data[i++] = pdev_stats->phy_err_drop;
++ data[i++] = pdev_stats->mpdu_errs;
++ data[i++] = ar->stats.fw_crash_counter;
++ data[i++] = ar->stats.fw_warm_reset_counter;
++ data[i++] = ar->stats.fw_cold_reset_counter;
++
++ spin_unlock_bh(&ar->data_lock);
++
++ mutex_unlock(&ar->conf_mutex);
++
++ WARN_ON(i != ATH10K_SSTATS_LEN);
++}
++
+ static const struct file_operations fops_fw_dbglog = {
+ .read = ath10k_read_fw_dbglog,
+ .write = ath10k_write_fw_dbglog,
+@@ -726,6 +1638,151 @@ static const struct file_operations fops
+ .llseek = default_llseek,
+ };
+
++static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
++{
++ struct ath10k *ar = inode->i_private;
++ void *buf;
++ u32 hi_addr;
++ __le32 addr;
++ int ret;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_ON &&
++ ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto err;
++ }
++
++ buf = vmalloc(QCA988X_CAL_DATA_LEN);
++ if (!buf) {
++ ret = -ENOMEM;
++ goto err;
++ }
++
++ hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));
++
++ ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));
++ if (ret) {
++ ath10k_warn(ar, "failed to read hi_board_data address: %d\n", ret);
++ goto err_vfree;
++ }
++
++ ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), buf,
++ QCA988X_CAL_DATA_LEN);
++ if (ret) {
++ ath10k_warn(ar, "failed to read calibration data: %d\n", ret);
++ goto err_vfree;
++ }
++
++ file->private_data = buf;
++
++ mutex_unlock(&ar->conf_mutex);
++
++ return 0;
++
++err_vfree:
++ vfree(buf);
++
++err:
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
++}
++
++static ssize_t ath10k_debug_cal_data_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ void *buf = file->private_data;
++
++ return simple_read_from_buffer(user_buf, count, ppos,
++ buf, QCA988X_CAL_DATA_LEN);
++}
++
++static int ath10k_debug_cal_data_release(struct inode *inode,
++ struct file *file)
++{
++ vfree(file->private_data);
++
++ return 0;
++}
++
++static const struct file_operations fops_cal_data = {
++ .open = ath10k_debug_cal_data_open,
++ .read = ath10k_debug_cal_data_read,
++ .release = ath10k_debug_cal_data_release,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_read_nf_cal_period(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ unsigned int len;
++ char buf[32];
++
++ len = scnprintf(buf, sizeof(buf), "%d\n",
++ ar->debug.nf_cal_period);
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t ath10k_write_nf_cal_period(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ unsigned long period;
++ int ret;
++
++ ret = kstrtoul_from_user(user_buf, count, 0, &period);
++ if (ret)
++ return ret;
++
++ if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX)
++ return -EINVAL;
++
++ /* there's no way to switch back to the firmware default */
++ if (period == 0)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++
++ ar->debug.nf_cal_period = period;
++
++ if (ar->state != ATH10K_STATE_ON) {
++ /* firmware is not running, nothing else to do */
++ ret = count;
++ goto exit;
++ }
++
++ ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period,
++ ar->debug.nf_cal_period);
++ if (ret) {
++ ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n",
++ ret);
++ goto exit;
++ }
++
++ ret = count;
++
++exit:
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
++}
++
++static const struct file_operations fops_nf_cal_period = {
++ .read = ath10k_read_nf_cal_period,
++ .write = ath10k_write_nf_cal_period,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
+ int ath10k_debug_start(struct ath10k *ar)
+ {
+ int ret;
+@@ -735,17 +1792,44 @@ int ath10k_debug_start(struct ath10k *ar
+ ret = ath10k_debug_htt_stats_req(ar);
+ if (ret)
+ /* continue normally anyway, this isn't serious */
+- ath10k_warn("failed to start htt stats workqueue: %d\n", ret);
++ ath10k_warn(ar, "failed to start htt stats workqueue: %d\n",
++ ret);
+
+ if (ar->debug.fw_dbglog_mask) {
+- ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
++ ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,
++ ATH10K_DBGLOG_LEVEL_WARN);
+ if (ret)
+ /* not serious */
+- ath10k_warn("failed to enable dbglog during start: %d",
++ ath10k_warn(ar, "failed to enable dbglog during start: %d",
+ ret);
+ }
+
+- return 0;
++ if (ar->debug.pktlog_filter) {
++ ret = ath10k_wmi_pdev_pktlog_enable(ar,
++ ar->debug.pktlog_filter);
++ if (ret)
++ /* not serious */
++ ath10k_warn(ar,
++ "failed to enable pktlog filter %x: %d\n",
++ ar->debug.pktlog_filter, ret);
++ } else {
++ ret = ath10k_wmi_pdev_pktlog_disable(ar);
++ if (ret)
++ /* not serious */
++ ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
++ }
++
++ if (ar->debug.nf_cal_period) {
++ ret = ath10k_wmi_pdev_set_param(ar,
++ ar->wmi.pdev_param->cal_period,
++ ar->debug.nf_cal_period);
++ if (ret)
++ /* not serious */
++ ath10k_warn(ar, "cal period cfg failed from debug start: %d\n",
++ ret);
++ }
++
++ return ret;
+ }
+
+ void ath10k_debug_stop(struct ath10k *ar)
+@@ -757,6 +1841,11 @@ void ath10k_debug_stop(struct ath10k *ar
+ * warning from del_timer(). */
+ if (ar->debug.htt_stats_mask != 0)
+ cancel_delayed_work(&ar->debug.htt_stats_dwork);
++
++ ar->debug.htt_max_amsdu = 0;
++ ar->debug.htt_max_ampdu = 0;
++
++ ath10k_wmi_pdev_pktlog_disable(ar);
+ }
+
+ static ssize_t ath10k_write_simulate_radar(struct file *file,
+@@ -839,37 +1928,149 @@ static const struct file_operations fops
+ .llseek = default_llseek,
+ };
+
++static ssize_t ath10k_write_pktlog_filter(struct file *file,
++ const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ u32 filter;
++ int ret;
++
++ if (kstrtouint_from_user(ubuf, count, 0, &filter))
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_ON) {
++ ar->debug.pktlog_filter = filter;
++ ret = count;
++ goto out;
++ }
++
++ if (filter && (filter != ar->debug.pktlog_filter)) {
++ ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
++ if (ret) {
++ ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
++ ar->debug.pktlog_filter, ret);
++ goto out;
++ }
++ } else {
++ ret = ath10k_wmi_pdev_pktlog_disable(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
++ goto out;
++ }
++ }
++
++ ar->debug.pktlog_filter = filter;
++ ret = count;
++
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ char buf[32];
++ struct ath10k *ar = file->private_data;
++ int len = 0;
++
++ mutex_lock(&ar->conf_mutex);
++ len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
++ ar->debug.pktlog_filter);
++ mutex_unlock(&ar->conf_mutex);
++
++ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
++}
++
++static const struct file_operations fops_pktlog_filter = {
++ .read = ath10k_read_pktlog_filter,
++ .write = ath10k_write_pktlog_filter,
++ .open = simple_open
++};
++
+ int ath10k_debug_create(struct ath10k *ar)
+ {
++ ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data));
++ if (!ar->debug.fw_crash_data)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
++ INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
++ INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
++
++ return 0;
++}
++
++void ath10k_debug_destroy(struct ath10k *ar)
++{
++ vfree(ar->debug.fw_crash_data);
++ ar->debug.fw_crash_data = NULL;
++
++ ath10k_debug_fw_stats_reset(ar);
++}
++
++int ath10k_debug_register(struct ath10k *ar)
++{
+ ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
+ ar->hw->wiphy->debugfsdir);
++ if (IS_ERR_OR_NULL(ar->debug.debugfs_phy)) {
++ if (IS_ERR(ar->debug.debugfs_phy))
++ return PTR_ERR(ar->debug.debugfs_phy);
+
+- if (!ar->debug.debugfs_phy)
+ return -ENOMEM;
++ }
+
+ INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
+ ath10k_debug_htt_stats_dwork);
+
+- init_completion(&ar->debug.event_stats_compl);
++ init_completion(&ar->debug.fw_stats_complete);
+
+ debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
+ &fops_fw_stats);
+
++ debugfs_create_file("fw_reset_stats", S_IRUSR, ar->debug.debugfs_phy,
++ ar, &fops_fw_reset_stats);
++
+ debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
+ &fops_wmi_services);
+
+ debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_simulate_fw_crash);
+
++ debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
++ ar, &fops_fw_crash_dump);
++
++ debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar, &fops_reg_addr);
++
++ debugfs_create_file("reg_value", S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar, &fops_reg_value);
++
++ debugfs_create_file("mem_value", S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar, &fops_mem_value);
++
+ debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_chip_id);
+
+ debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_htt_stats_mask);
+
++ debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar,
++ &fops_htt_max_amsdu_ampdu);
++
+ debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_fw_dbglog);
+
++ debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
++ ar, &fops_cal_data);
++
++ debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar, &fops_nf_cal_period);
++
+ if (config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED)) {
+ debugfs_create_file("dfs_simulate_radar", S_IWUSR,
+ ar->debug.debugfs_phy, ar,
+@@ -884,10 +2085,13 @@ int ath10k_debug_create(struct ath10k *a
+ &fops_dfs_stats);
+ }
+
++ debugfs_create_file("pktlog_filter", S_IRUGO | S_IWUSR,
++ ar->debug.debugfs_phy, ar, &fops_pktlog_filter);
++
+ return 0;
+ }
+
+-void ath10k_debug_destroy(struct ath10k *ar)
++void ath10k_debug_unregister(struct ath10k *ar)
+ {
+ cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);
+ }
+@@ -895,7 +2099,8 @@ void ath10k_debug_destroy(struct ath10k
+ #endif /* CPTCFG_ATH10K_DEBUGFS */
+
+ #ifdef CPTCFG_ATH10K_DEBUG
+-void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...)
++void ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask,
++ const char *fmt, ...)
+ {
+ struct va_format vaf;
+ va_list args;
+@@ -906,27 +2111,43 @@ void ath10k_dbg(enum ath10k_debug_mask m
+ vaf.va = &args;
+
+ if (ath10k_debug_mask & mask)
+- ath10k_printk(KERN_DEBUG, "%pV", &vaf);
++ dev_printk(KERN_DEBUG, ar->dev, "%pV", &vaf);
+
+- trace_ath10k_log_dbg(mask, &vaf);
++ trace_ath10k_log_dbg(ar, mask, &vaf);
+
+ va_end(args);
+ }
+ EXPORT_SYMBOL(ath10k_dbg);
+
+-void ath10k_dbg_dump(enum ath10k_debug_mask mask,
++void ath10k_dbg_dump(struct ath10k *ar,
++ enum ath10k_debug_mask mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+ {
++ char linebuf[256];
++ unsigned int linebuflen;
++ const void *ptr;
++
+ if (ath10k_debug_mask & mask) {
+ if (msg)
+- ath10k_dbg(mask, "%s\n", msg);
++ ath10k_dbg(ar, mask, "%s\n", msg);
+
+- print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
++ for (ptr = buf; (ptr - buf) < len; ptr += 16) {
++ linebuflen = 0;
++ linebuflen += scnprintf(linebuf + linebuflen,
++ sizeof(linebuf) - linebuflen,
++ "%s%08x: ",
++ (prefix ? prefix : ""),
++ (unsigned int)(ptr - buf));
++ hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,
++ linebuf + linebuflen,
++ sizeof(linebuf) - linebuflen, true);
++ dev_printk(KERN_DEBUG, ar->dev, "%s\n", linebuf);
++ }
+ }
+
+ /* tracing code doesn't like null strings :/ */
+- trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
++ trace_ath10k_log_dbg_dump(ar, msg ? msg : "", prefix ? prefix : "",
+ buf, len);
+ }
+ EXPORT_SYMBOL(ath10k_dbg_dump);
+--- a/drivers/net/wireless/ath/ath10k/debug.h
++++ b/drivers/net/wireless/ath/ath10k/debug.h
+@@ -34,28 +34,55 @@ enum ath10k_debug_mask {
+ ATH10K_DBG_DATA = 0x00000200,
+ ATH10K_DBG_BMI = 0x00000400,
+ ATH10K_DBG_REGULATORY = 0x00000800,
++ ATH10K_DBG_TESTMODE = 0x00001000,
++ ATH10K_DBG_WMI_PRINT = 0x00002000,
+ ATH10K_DBG_ANY = 0xffffffff,
+ };
+
++enum ath10k_pktlog_filter {
++ ATH10K_PKTLOG_RX = 0x000000001,
++ ATH10K_PKTLOG_TX = 0x000000002,
++ ATH10K_PKTLOG_RCFIND = 0x000000004,
++ ATH10K_PKTLOG_RCUPDATE = 0x000000008,
++ ATH10K_PKTLOG_DBG_PRINT = 0x000000010,
++ ATH10K_PKTLOG_ANY = 0x00000001f,
++};
++
++enum ath10k_dbg_aggr_mode {
++ ATH10K_DBG_AGGR_MODE_AUTO,
++ ATH10K_DBG_AGGR_MODE_MANUAL,
++ ATH10K_DBG_AGGR_MODE_MAX,
++};
++
+ extern unsigned int ath10k_debug_mask;
+
+-__printf(1, 2) int ath10k_info(const char *fmt, ...);
+-__printf(1, 2) int ath10k_err(const char *fmt, ...);
+-__printf(1, 2) int ath10k_warn(const char *fmt, ...);
++__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
++__printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...);
++__printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...);
++void ath10k_print_driver_info(struct ath10k *ar);
+
+ #ifdef CPTCFG_ATH10K_DEBUGFS
+ int ath10k_debug_start(struct ath10k *ar);
+ void ath10k_debug_stop(struct ath10k *ar);
+ int ath10k_debug_create(struct ath10k *ar);
+ void ath10k_debug_destroy(struct ath10k *ar);
+-void ath10k_debug_read_service_map(struct ath10k *ar,
+- void *service_map,
+- size_t map_size);
+-void ath10k_debug_read_target_stats(struct ath10k *ar,
+- struct wmi_stats_event *ev);
++int ath10k_debug_register(struct ath10k *ar);
++void ath10k_debug_unregister(struct ath10k *ar);
++void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
++struct ath10k_fw_crash_data *
++ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
+
++void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
+ #define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
+
++void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ u32 sset, u8 *data);
++int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif, int sset);
++void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ struct ethtool_stats *stats, u64 *data);
+ #else
+ static inline int ath10k_debug_start(struct ath10k *ar)
+ {
+@@ -75,36 +102,62 @@ static inline void ath10k_debug_destroy(
+ {
+ }
+
+-static inline void ath10k_debug_read_service_map(struct ath10k *ar,
+- void *service_map,
+- size_t map_size)
++static inline int ath10k_debug_register(struct ath10k *ar)
++{
++ return 0;
++}
++
++static inline void ath10k_debug_unregister(struct ath10k *ar)
+ {
+ }
+
+-static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
+- struct wmi_stats_event *ev)
++static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
++ struct sk_buff *skb)
+ {
+ }
+
++static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
++ int len)
++{
++}
++
++static inline struct ath10k_fw_crash_data *
++ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
++{
++ return NULL;
++}
++
+ #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
+
++#define ath10k_debug_get_et_strings NULL
++#define ath10k_debug_get_et_sset_count NULL
++#define ath10k_debug_get_et_stats NULL
++
+ #endif /* CPTCFG_ATH10K_DEBUGFS */
++#ifdef CPTCFG_MAC80211_DEBUGFS
++void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, struct dentry *dir);
++#endif /* CPTCFG_MAC80211_DEBUGFS */
+
+ #ifdef CPTCFG_ATH10K_DEBUG
+-__printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
++__printf(3, 4) void ath10k_dbg(struct ath10k *ar,
++ enum ath10k_debug_mask mask,
+ const char *fmt, ...);
+-void ath10k_dbg_dump(enum ath10k_debug_mask mask,
++void ath10k_dbg_dump(struct ath10k *ar,
++ enum ath10k_debug_mask mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len);
+ #else /* CPTCFG_ATH10K_DEBUG */
+
+-static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask,
++static inline int ath10k_dbg(struct ath10k *ar,
++ enum ath10k_debug_mask dbg_mask,
+ const char *fmt, ...)
+ {
+ return 0;
+ }
+
+-static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask,
++static inline void ath10k_dbg_dump(struct ath10k *ar,
++ enum ath10k_debug_mask mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+ {
+--- a/drivers/net/wireless/ath/ath10k/hif.h
++++ b/drivers/net/wireless/ath/ath10k/hif.h
+@@ -20,6 +20,7 @@
+
+ #include <linux/kernel.h>
+ #include "core.h"
++#include "debug.h"
+
+ struct ath10k_hif_sg_item {
+ u16 transfer_id;
+@@ -31,11 +32,9 @@ struct ath10k_hif_sg_item {
+
+ struct ath10k_hif_cb {
+ int (*tx_completion)(struct ath10k *ar,
+- struct sk_buff *wbuf,
+- unsigned transfer_id);
++ struct sk_buff *wbuf);
+ int (*rx_completion)(struct ath10k *ar,
+- struct sk_buff *wbuf,
+- u8 pipe_id);
++ struct sk_buff *wbuf);
+ };
+
+ struct ath10k_hif_ops {
+@@ -43,6 +42,12 @@ struct ath10k_hif_ops {
+ int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items, int n_items);
+
++ /* read firmware memory through the diagnose interface */
++ int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
++ size_t buf_len);
++
++ int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
++ int nbytes);
+ /*
+ * API to handle HIF-specific BMI message exchanges, this API is
+ * synchronous and only allowed to be called from a context that
+@@ -80,6 +85,10 @@ struct ath10k_hif_ops {
+
+ u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
++ u32 (*read32)(struct ath10k *ar, u32 address);
++
++ void (*write32)(struct ath10k *ar, u32 address, u32 value);
++
+ /* Power up the device and enter BMI transfer mode for FW download */
+ int (*power_up)(struct ath10k *ar);
+
+@@ -91,7 +100,6 @@ struct ath10k_hif_ops {
+ int (*resume)(struct ath10k *ar);
+ };
+
+-
+ static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items,
+ int n_items)
+@@ -99,6 +107,21 @@ static inline int ath10k_hif_tx_sg(struc
+ return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items);
+ }
+
++static inline int ath10k_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
++ size_t buf_len)
++{
++ return ar->hif.ops->diag_read(ar, address, buf, buf_len);
++}
++
++static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
++ const void *data, int nbytes)
++{
++ if (!ar->hif.ops->diag_write)
++ return -EOPNOTSUPP;
++
++ return ar->hif.ops->diag_write(ar, address, data, nbytes);
++}
++
+ static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
+ void *request, u32 request_len,
+ void *response, u32 *response_len)
+@@ -178,4 +201,25 @@ static inline int ath10k_hif_resume(stru
+ return ar->hif.ops->resume(ar);
+ }
+
++static inline u32 ath10k_hif_read32(struct ath10k *ar, u32 address)
++{
++ if (!ar->hif.ops->read32) {
++ ath10k_warn(ar, "hif read32 not supported\n");
++ return 0xdeaddead;
++ }
++
++ return ar->hif.ops->read32(ar, address);
++}
++
++static inline void ath10k_hif_write32(struct ath10k *ar,
++ u32 address, u32 data)
++{
++ if (!ar->hif.ops->write32) {
++ ath10k_warn(ar, "hif write32 not supported\n");
++ return;
++ }
++
++ ar->hif.ops->write32(ar, address, data);
++}
++
+ #endif /* _HIF_H_ */
+--- a/drivers/net/wireless/ath/ath10k/htc.c
++++ b/drivers/net/wireless/ath/ath10k/htc.c
+@@ -45,10 +45,8 @@ static struct sk_buff *ath10k_htc_build_
+ struct ath10k_skb_cb *skb_cb;
+
+ skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
+- if (!skb) {
+- ath10k_warn("Unable to allocate ctrl skb\n");
++ if (!skb)
+ return NULL;
+- }
+
+ skb_reserve(skb, 20); /* FIXME: why 20 bytes? */
+ WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+@@ -56,7 +54,7 @@ static struct sk_buff *ath10k_htc_build_
+ skb_cb = ATH10K_SKB_CB(skb);
+ memset(skb_cb, 0, sizeof(*skb_cb));
+
+- ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
++ ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
+ return skb;
+ }
+
+@@ -72,13 +70,15 @@ static inline void ath10k_htc_restore_tx
+ static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
+ struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
++ struct ath10k *ar = ep->htc->ar;
++
++ ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+ ep->eid, skb);
+
+ ath10k_htc_restore_tx_skb(ep->htc, skb);
+
+ if (!ep->ep_ops.ep_tx_complete) {
+- ath10k_warn("no tx handler for eid %d\n", ep->eid);
++ ath10k_warn(ar, "no tx handler for eid %d\n", ep->eid);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+@@ -89,12 +89,14 @@ static void ath10k_htc_notify_tx_complet
+ /* assumes tx_lock is held */
+ static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep)
+ {
++ struct ath10k *ar = ep->htc->ar;
++
+ if (!ep->tx_credit_flow_enabled)
+ return false;
+ if (ep->tx_credits >= ep->tx_credits_per_max_message)
+ return false;
+
+- ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n",
+ ep->eid);
+ return true;
+ }
+@@ -123,6 +125,7 @@ int ath10k_htc_send(struct ath10k_htc *h
+ enum ath10k_htc_ep_id eid,
+ struct sk_buff *skb)
+ {
++ struct ath10k *ar = htc->ar;
+ struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+ struct ath10k_hif_sg_item sg_item;
+@@ -134,18 +137,10 @@ int ath10k_htc_send(struct ath10k_htc *h
+ return -ECOMM;
+
+ if (eid >= ATH10K_HTC_EP_COUNT) {
+- ath10k_warn("Invalid endpoint id: %d\n", eid);
++ ath10k_warn(ar, "Invalid endpoint id: %d\n", eid);
+ return -ENOENT;
+ }
+
+- /* FIXME: This looks ugly, can we fix it? */
+- spin_lock_bh(&htc->tx_lock);
+- if (htc->stopped) {
+- spin_unlock_bh(&htc->tx_lock);
+- return -ESHUTDOWN;
+- }
+- spin_unlock_bh(&htc->tx_lock);
+-
+ skb_push(skb, sizeof(struct ath10k_htc_hdr));
+
+ if (ep->tx_credit_flow_enabled) {
+@@ -157,7 +152,7 @@ int ath10k_htc_send(struct ath10k_htc *h
+ goto err_pull;
+ }
+ ep->tx_credits -= credits;
+- ath10k_dbg(ATH10K_DBG_HTC,
++ ath10k_dbg(ar, ATH10K_DBG_HTC,
+ "htc ep %d consumed %d credits (total %d)\n",
+ eid, credits, ep->tx_credits);
+ spin_unlock_bh(&htc->tx_lock);
+@@ -165,6 +160,7 @@ int ath10k_htc_send(struct ath10k_htc *h
+
+ ath10k_htc_prepare_tx_skb(ep, skb);
+
++ skb_cb->eid = eid;
+ skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(dev, skb_cb->paddr);
+ if (ret)
+@@ -188,7 +184,7 @@ err_credits:
+ if (ep->tx_credit_flow_enabled) {
+ spin_lock_bh(&htc->tx_lock);
+ ep->tx_credits += credits;
+- ath10k_dbg(ATH10K_DBG_HTC,
++ ath10k_dbg(ar, ATH10K_DBG_HTC,
+ "htc ep %d reverted %d credits back (total %d)\n",
+ eid, credits, ep->tx_credits);
+ spin_unlock_bh(&htc->tx_lock);
+@@ -202,15 +198,18 @@ err_pull:
+ }
+
+ static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
+- struct sk_buff *skb,
+- unsigned int eid)
++ struct sk_buff *skb)
+ {
+ struct ath10k_htc *htc = &ar->htc;
+- struct ath10k_htc_ep *ep = &htc->endpoint[eid];
++ struct ath10k_skb_cb *skb_cb;
++ struct ath10k_htc_ep *ep;
+
+ if (WARN_ON_ONCE(!skb))
+ return 0;
+
++ skb_cb = ATH10K_SKB_CB(skb);
++ ep = &htc->endpoint[skb_cb->eid];
++
+ ath10k_htc_notify_tx_completion(ep, skb);
+ /* the skb now belongs to the completion handler */
+
+@@ -227,11 +226,12 @@ ath10k_htc_process_credit_report(struct
+ int len,
+ enum ath10k_htc_ep_id eid)
+ {
++ struct ath10k *ar = htc->ar;
+ struct ath10k_htc_ep *ep;
+ int i, n_reports;
+
+ if (len % sizeof(*report))
+- ath10k_warn("Uneven credit report len %d", len);
++ ath10k_warn(ar, "Uneven credit report len %d", len);
+
+ n_reports = len / sizeof(*report);
+
+@@ -243,7 +243,7 @@ ath10k_htc_process_credit_report(struct
+ ep = &htc->endpoint[report->eid];
+ ep->tx_credits += report->credits;
+
+- ath10k_dbg(ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
+ report->eid, report->credits, ep->tx_credits);
+
+ if (ep->ep_ops.ep_tx_credits) {
+@@ -260,6 +260,7 @@ static int ath10k_htc_process_trailer(st
+ int length,
+ enum ath10k_htc_ep_id src_eid)
+ {
++ struct ath10k *ar = htc->ar;
+ int status = 0;
+ struct ath10k_htc_record *record;
+ u8 *orig_buffer;
+@@ -279,7 +280,7 @@ static int ath10k_htc_process_trailer(st
+
+ if (record->hdr.len > length) {
+ /* no room left in buffer for record */
+- ath10k_warn("Invalid record length: %d\n",
++ ath10k_warn(ar, "Invalid record length: %d\n",
+ record->hdr.len);
+ status = -EINVAL;
+ break;
+@@ -289,7 +290,7 @@ static int ath10k_htc_process_trailer(st
+ case ATH10K_HTC_RECORD_CREDITS:
+ len = sizeof(struct ath10k_htc_credit_report);
+ if (record->hdr.len < len) {
+- ath10k_warn("Credit report too long\n");
++ ath10k_warn(ar, "Credit report too long\n");
+ status = -EINVAL;
+ break;
+ }
+@@ -299,7 +300,7 @@ static int ath10k_htc_process_trailer(st
+ src_eid);
+ break;
+ default:
+- ath10k_warn("Unhandled record: id:%d length:%d\n",
++ ath10k_warn(ar, "Unhandled record: id:%d length:%d\n",
+ record->hdr.id, record->hdr.len);
+ break;
+ }
+@@ -313,15 +314,14 @@ static int ath10k_htc_process_trailer(st
+ }
+
+ if (status)
+- ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "",
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc rx bad trailer", "",
+ orig_buffer, orig_length);
+
+ return status;
+ }
+
+ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
+- struct sk_buff *skb,
+- u8 pipe_id)
++ struct sk_buff *skb)
+ {
+ int status = 0;
+ struct ath10k_htc *htc = &ar->htc;
+@@ -339,8 +339,8 @@ static int ath10k_htc_rx_completion_hand
+ eid = hdr->eid;
+
+ if (eid >= ATH10K_HTC_EP_COUNT) {
+- ath10k_warn("HTC Rx: invalid eid %d\n", eid);
+- ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "",
++ ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid);
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "",
+ hdr, sizeof(*hdr));
+ status = -EINVAL;
+ goto out;
+@@ -360,19 +360,19 @@ static int ath10k_htc_rx_completion_hand
+ payload_len = __le16_to_cpu(hdr->len);
+
+ if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
+- ath10k_warn("HTC rx frame too long, len: %zu\n",
++ ath10k_warn(ar, "HTC rx frame too long, len: %zu\n",
+ payload_len + sizeof(*hdr));
+- ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "",
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "",
+ hdr, sizeof(*hdr));
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (skb->len < payload_len) {
+- ath10k_dbg(ATH10K_DBG_HTC,
++ ath10k_dbg(ar, ATH10K_DBG_HTC,
+ "HTC Rx: insufficient length, got %d, expected %d\n",
+ skb->len, payload_len);
+- ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len",
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len",
+ "", hdr, sizeof(*hdr));
+ status = -EINVAL;
+ goto out;
+@@ -388,7 +388,7 @@ static int ath10k_htc_rx_completion_hand
+
+ if ((trailer_len < min_len) ||
+ (trailer_len > payload_len)) {
+- ath10k_warn("Invalid trailer length: %d\n",
++ ath10k_warn(ar, "Invalid trailer length: %d\n",
+ trailer_len);
+ status = -EPROTO;
+ goto out;
+@@ -421,7 +421,7 @@ static int ath10k_htc_rx_completion_hand
+ * this is a fatal error, target should not be
+ * sending unsolicited messages on the ep 0
+ */
+- ath10k_warn("HTC rx ctrl still processing\n");
++ ath10k_warn(ar, "HTC rx ctrl still processing\n");
+ status = -EINVAL;
+ complete(&htc->ctl_resp);
+ goto out;
+@@ -442,7 +442,7 @@ static int ath10k_htc_rx_completion_hand
+ goto out;
+ }
+
+- ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
+ eid, skb);
+ ep->ep_ops.ep_rx_complete(ar, skb);
+
+@@ -459,7 +459,7 @@ static void ath10k_htc_control_rx_comple
+ {
+ /* This is unexpected. FW is not supposed to send regular rx on this
+ * endpoint. */
+- ath10k_warn("unexpected htc rx\n");
++ ath10k_warn(ar, "unexpected htc rx\n");
+ kfree_skb(skb);
+ }
+
+@@ -546,7 +546,8 @@ static u8 ath10k_htc_get_credit_allocati
+
+ int ath10k_htc_wait_target(struct ath10k_htc *htc)
+ {
+- int status = 0;
++ struct ath10k *ar = htc->ar;
++ int i, status = 0;
+ struct ath10k_htc_svc_conn_req conn_req;
+ struct ath10k_htc_svc_conn_resp conn_resp;
+ struct ath10k_htc_msg *msg;
+@@ -556,16 +557,32 @@ int ath10k_htc_wait_target(struct ath10k
+
+ status = wait_for_completion_timeout(&htc->ctl_resp,
+ ATH10K_HTC_WAIT_TIMEOUT_HZ);
+- if (status <= 0) {
++ if (status == 0) {
++ /* Workaround: In some cases the PCI HIF doesn't
++ * receive interrupt for the control response message
++ * even if the buffer was completed. It is suspected
++ * iomap writes unmasking PCI CE irqs aren't propagated
++ * properly in KVM PCI-passthrough sometimes.
++ */
++ ath10k_warn(ar, "failed to receive control response completion, polling..\n");
++
++ for (i = 0; i < CE_COUNT; i++)
++ ath10k_hif_send_complete_check(htc->ar, i, 1);
++
++ status = wait_for_completion_timeout(&htc->ctl_resp,
++ ATH10K_HTC_WAIT_TIMEOUT_HZ);
++
+ if (status == 0)
+ status = -ETIMEDOUT;
++ }
+
+- ath10k_err("ctl_resp never came in (%d)\n", status);
++ if (status < 0) {
++ ath10k_err(ar, "ctl_resp never came in (%d)\n", status);
+ return status;
+ }
+
+ if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
+- ath10k_err("Invalid HTC ready msg len:%d\n",
++ ath10k_err(ar, "Invalid HTC ready msg len:%d\n",
+ htc->control_resp_len);
+ return -ECOMM;
+ }
+@@ -576,21 +593,21 @@ int ath10k_htc_wait_target(struct ath10k
+ credit_size = __le16_to_cpu(msg->ready.credit_size);
+
+ if (message_id != ATH10K_HTC_MSG_READY_ID) {
+- ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id);
++ ath10k_err(ar, "Invalid HTC ready msg: 0x%x\n", message_id);
+ return -ECOMM;
+ }
+
+ htc->total_transmit_credits = credit_count;
+ htc->target_credit_size = credit_size;
+
+- ath10k_dbg(ATH10K_DBG_HTC,
++ ath10k_dbg(ar, ATH10K_DBG_HTC,
+ "Target ready! transmit resources: %d size:%d\n",
+ htc->total_transmit_credits,
+ htc->target_credit_size);
+
+ if ((htc->total_transmit_credits == 0) ||
+ (htc->target_credit_size == 0)) {
+- ath10k_err("Invalid credit size received\n");
++ ath10k_err(ar, "Invalid credit size received\n");
+ return -ECOMM;
+ }
+
+@@ -607,7 +624,8 @@ int ath10k_htc_wait_target(struct ath10k
+ /* connect fake service */
+ status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
+ if (status) {
+- ath10k_err("could not connect to htc service (%d)\n", status);
++ ath10k_err(ar, "could not connect to htc service (%d)\n",
++ status);
+ return status;
+ }
+
+@@ -618,6 +636,7 @@ int ath10k_htc_connect_service(struct at
+ struct ath10k_htc_svc_conn_req *conn_req,
+ struct ath10k_htc_svc_conn_resp *conn_resp)
+ {
++ struct ath10k *ar = htc->ar;
+ struct ath10k_htc_msg *msg;
+ struct ath10k_htc_conn_svc *req_msg;
+ struct ath10k_htc_conn_svc_response resp_msg_dummy;
+@@ -643,13 +662,13 @@ int ath10k_htc_connect_service(struct at
+ tx_alloc = ath10k_htc_get_credit_allocation(htc,
+ conn_req->service_id);
+ if (!tx_alloc)
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot htc service %s does not allocate target credits\n",
+ htc_service_name(conn_req->service_id));
+
+ skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+ if (!skb) {
+- ath10k_err("Failed to allocate HTC packet\n");
++ ath10k_err(ar, "Failed to allocate HTC packet\n");
+ return -ENOMEM;
+ }
+
+@@ -684,11 +703,9 @@ int ath10k_htc_connect_service(struct at
+ /* wait for response */
+ status = wait_for_completion_timeout(&htc->ctl_resp,
+ ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
+- if (status <= 0) {
+- if (status == 0)
+- status = -ETIMEDOUT;
+- ath10k_err("Service connect timeout: %d\n", status);
+- return status;
++ if (status == 0) {
++ ath10k_err(ar, "Service connect timeout: %d\n", status);
++ return -ETIMEDOUT;
+ }
+
+ /* we controlled the buffer creation, it's aligned */
+@@ -700,11 +717,11 @@ int ath10k_htc_connect_service(struct at
+ if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
+ (htc->control_resp_len < sizeof(msg->hdr) +
+ sizeof(msg->connect_service_response))) {
+- ath10k_err("Invalid resp message ID 0x%x", message_id);
++ ath10k_err(ar, "Invalid resp message ID 0x%x", message_id);
+ return -EPROTO;
+ }
+
+- ath10k_dbg(ATH10K_DBG_HTC,
++ ath10k_dbg(ar, ATH10K_DBG_HTC,
+ "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n",
+ htc_service_name(service_id),
+ resp_msg->status, resp_msg->eid);
+@@ -713,7 +730,7 @@ int ath10k_htc_connect_service(struct at
+
+ /* check response status */
+ if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) {
+- ath10k_err("HTC Service %s connect request failed: 0x%x)\n",
++ ath10k_err(ar, "HTC Service %s connect request failed: 0x%x)\n",
+ htc_service_name(service_id),
+ resp_msg->status);
+ return -EPROTO;
+@@ -764,18 +781,18 @@ setup:
+ if (status)
+ return status;
+
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
+ htc_service_name(ep->service_id), ep->ul_pipe_id,
+ ep->dl_pipe_id, ep->eid);
+
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot htc ep %d ul polled %d dl polled %d\n",
+ ep->eid, ep->ul_is_polled, ep->dl_is_polled);
+
+ if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
+ ep->tx_credit_flow_enabled = false;
+- ath10k_dbg(ATH10K_DBG_BOOT,
++ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot htc service '%s' eid %d TX flow control disabled\n",
+ htc_service_name(ep->service_id), assigned_eid);
+ }
+@@ -783,27 +800,26 @@ setup:
+ return status;
+ }
+
+-struct sk_buff *ath10k_htc_alloc_skb(int size)
++struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size)
+ {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
+- if (!skb) {
+- ath10k_warn("could not allocate HTC tx skb\n");
++ if (!skb)
+ return NULL;
+- }
+
+ skb_reserve(skb, sizeof(struct ath10k_htc_hdr));
+
+ /* FW/HTC requires 4-byte aligned streams */
+ if (!IS_ALIGNED((unsigned long)skb->data, 4))
+- ath10k_warn("Unaligned HTC tx skb\n");
++ ath10k_warn(ar, "Unaligned HTC tx skb\n");
+
+ return skb;
+ }
+
+ int ath10k_htc_start(struct ath10k_htc *htc)
+ {
++ struct ath10k *ar = htc->ar;
+ struct sk_buff *skb;
+ int status = 0;
+ struct ath10k_htc_msg *msg;
+@@ -819,7 +835,7 @@ int ath10k_htc_start(struct ath10k_htc *
+ msg->hdr.message_id =
+ __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+- ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
++ ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
+
+ status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+ if (status) {
+@@ -830,19 +846,6 @@ int ath10k_htc_start(struct ath10k_htc *
+ return 0;
+ }
+
+-/*
+- * stop HTC communications, i.e. stop interrupt reception, and flush all
+- * queued buffers
+- */
+-void ath10k_htc_stop(struct ath10k_htc *htc)
+-{
+- spin_lock_bh(&htc->tx_lock);
+- htc->stopped = true;
+- spin_unlock_bh(&htc->tx_lock);
+-
+- ath10k_hif_stop(htc->ar);
+-}
+-
+ /* registered target arrival callback from the HIF layer */
+ int ath10k_htc_init(struct ath10k *ar)
+ {
+@@ -852,7 +855,6 @@ int ath10k_htc_init(struct ath10k *ar)
+
+ spin_lock_init(&htc->tx_lock);
+
+- htc->stopped = false;
+ ath10k_htc_reset_endpoint_states(htc);
+
+ /* setup HIF layer callbacks */
+--- a/drivers/net/wireless/ath/ath10k/htc.h
++++ b/drivers/net/wireless/ath/ath10k/htc.h
+@@ -214,7 +214,6 @@ struct ath10k_htc_frame {
+ struct ath10k_htc_record trailer[0];
+ } __packed __aligned(4);
+
+-
+ /*******************/
+ /* Host-side stuff */
+ /*******************/
+@@ -332,7 +331,7 @@ struct ath10k_htc {
+ struct ath10k *ar;
+ struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
+
+- /* protects endpoint and stopped fields */
++ /* protects endpoints */
+ spinlock_t tx_lock;
+
+ struct ath10k_htc_ops htc_ops;
+@@ -345,8 +344,6 @@ struct ath10k_htc {
+ int total_transmit_credits;
+ struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
+ int target_credit_size;
+-
+- bool stopped;
+ };
+
+ int ath10k_htc_init(struct ath10k *ar);
+@@ -357,7 +354,6 @@ int ath10k_htc_connect_service(struct at
+ struct ath10k_htc_svc_conn_resp *conn_resp);
+ int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
+ struct sk_buff *packet);
+-void ath10k_htc_stop(struct ath10k_htc *htc);
+-struct sk_buff *ath10k_htc_alloc_skb(int size);
++struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size);
+
+ #endif
+--- a/drivers/net/wireless/ath/ath10k/htt.c
++++ b/drivers/net/wireless/ath/ath10k/htt.c
+@@ -22,7 +22,7 @@
+ #include "core.h"
+ #include "debug.h"
+
+-static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
++int ath10k_htt_connect(struct ath10k_htt *htt)
+ {
+ struct ath10k_htc_svc_conn_req conn_req;
+ struct ath10k_htc_svc_conn_resp conn_resp;
+@@ -48,37 +48,11 @@ static int ath10k_htt_htc_attach(struct
+ return 0;
+ }
+
+-int ath10k_htt_attach(struct ath10k *ar)
++int ath10k_htt_init(struct ath10k *ar)
+ {
+ struct ath10k_htt *htt = &ar->htt;
+- int ret;
+
+ htt->ar = ar;
+- htt->max_throughput_mbps = 800;
+-
+- /*
+- * Connect to HTC service.
+- * This has to be done before calling ath10k_htt_rx_attach,
+- * since ath10k_htt_rx_attach involves sending a rx ring configure
+- * message to the target.
+- */
+- ret = ath10k_htt_htc_attach(htt);
+- if (ret) {
+- ath10k_err("could not attach htt htc (%d)\n", ret);
+- goto err_htc_attach;
+- }
+-
+- ret = ath10k_htt_tx_attach(htt);
+- if (ret) {
+- ath10k_err("could not attach htt tx (%d)\n", ret);
+- goto err_htc_attach;
+- }
+-
+- ret = ath10k_htt_rx_attach(htt);
+- if (ret) {
+- ath10k_err("could not attach htt rx (%d)\n", ret);
+- goto err_rx_attach;
+- }
+
+ /*
+ * Prefetch enough data to satisfy target
+@@ -93,23 +67,20 @@ int ath10k_htt_attach(struct ath10k *ar)
+ 2; /* ip4 dscp or ip6 priority */
+
+ return 0;
+-
+-err_rx_attach:
+- ath10k_htt_tx_detach(htt);
+-err_htc_attach:
+- return ret;
+ }
+
+ #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
+
+ static int ath10k_htt_verify_version(struct ath10k_htt *htt)
+ {
+- ath10k_dbg(ATH10K_DBG_BOOT, "htt target version %d.%d\n",
++ struct ath10k *ar = htt->ar;
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt target version %d.%d\n",
+ htt->target_version_major, htt->target_version_minor);
+
+ if (htt->target_version_major != 2 &&
+ htt->target_version_major != 3) {
+- ath10k_err("unsupported htt major version %d. supported versions are 2 and 3\n",
++ ath10k_err(ar, "unsupported htt major version %d. supported versions are 2 and 3\n",
+ htt->target_version_major);
+ return -ENOTSUPP;
+ }
+@@ -117,8 +88,9 @@ static int ath10k_htt_verify_version(str
+ return 0;
+ }
+
+-int ath10k_htt_attach_target(struct ath10k_htt *htt)
++int ath10k_htt_setup(struct ath10k_htt *htt)
+ {
++ struct ath10k *ar = htt->ar;
+ int status;
+
+ init_completion(&htt->target_version_received);
+@@ -128,9 +100,9 @@ int ath10k_htt_attach_target(struct ath1
+ return status;
+
+ status = wait_for_completion_timeout(&htt->target_version_received,
+- HTT_TARGET_VERSION_TIMEOUT_HZ);
+- if (status <= 0) {
+- ath10k_warn("htt version request timed out\n");
++ HTT_TARGET_VERSION_TIMEOUT_HZ);
++ if (status == 0) {
++ ath10k_warn(ar, "htt version request timed out\n");
+ return -ETIMEDOUT;
+ }
+
+@@ -140,9 +112,3 @@ int ath10k_htt_attach_target(struct ath1
+
+ return ath10k_htt_send_rx_ring_cfg_ll(htt);
+ }
+-
+-void ath10k_htt_detach(struct ath10k_htt *htt)
+-{
+- ath10k_htt_rx_detach(htt);
+- ath10k_htt_tx_detach(htt);
+-}
+--- a/drivers/net/wireless/ath/ath10k/htt.h
++++ b/drivers/net/wireless/ath/ath10k/htt.h
+@@ -21,6 +21,7 @@
+ #include <linux/bug.h>
+ #include <linux/interrupt.h>
+ #include <linux/dmapool.h>
++#include <linux/hashtable.h>
+ #include <net/mac80211.h>
+
+ #include "htc.h"
+@@ -126,6 +127,7 @@ enum htt_data_tx_ext_tid {
+ * (HL hosts manage queues on the host )
+ * more_in_batch: only for HL hosts. indicates if more packets are
+ * pending. this allows target to wait and aggregate
++ * freq: 0 means home channel of given vdev. intended for offchannel
+ */
+ struct htt_data_tx_desc {
+ u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
+@@ -133,7 +135,8 @@ struct htt_data_tx_desc {
+ __le16 len;
+ __le16 id;
+ __le32 frags_paddr;
+- __le32 peerid;
++ __le16 peerid;
++ __le16 freq;
+ u8 prefetch[0]; /* start of frame, for FW classification engine */
+ } __packed;
+
+@@ -156,6 +159,9 @@ enum htt_rx_ring_flags {
+ HTT_RX_RING_FLAGS_PHY_DATA_RX = 1 << 15
+ };
+
++#define HTT_RX_RING_SIZE_MIN 128
++#define HTT_RX_RING_SIZE_MAX 2048
++
+ struct htt_rx_ring_setup_ring {
+ __le32 fw_idx_shadow_reg_paddr;
+ __le32 rx_ring_base_paddr;
+@@ -240,16 +246,10 @@ struct htt_oob_sync_req {
+ __le16 rsvd0;
+ } __packed;
+
+-#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
+-#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB 0
+-
+ struct htt_aggr_conf {
+ u8 max_num_ampdu_subframes;
+- union {
+- /* dont use bitfields; undefined behaviour */
+- u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
+- u8 max_num_amsdu_subframes:5;
+- } __packed;
++ /* amsdu_subframes is limited by 0x1F mask */
++ u8 max_num_amsdu_subframes;
+ } __packed;
+
+ #define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
+@@ -271,7 +271,6 @@ enum htt_mgmt_tx_status {
+
+ /*=== target -> host messages ===============================================*/
+
+-
+ enum htt_t2h_msg_type {
+ HTT_T2H_MSG_TYPE_VERSION_CONF = 0x0,
+ HTT_T2H_MSG_TYPE_RX_IND = 0x1,
+@@ -288,7 +287,19 @@ enum htt_t2h_msg_type {
+ HTT_T2H_MSG_TYPE_RC_UPDATE_IND = 0xc,
+ HTT_T2H_MSG_TYPE_TX_INSPECT_IND = 0xd,
+ HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION = 0xe,
++ HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND = 0xf,
++ HTT_T2H_MSG_TYPE_RX_PN_IND = 0x10,
++ HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND = 0x11,
++ HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND = 0x12,
++ /* 0x13 reservd */
++ HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE = 0x14,
++
++ /* FIXME: Do not depend on this event id. Numbering of this event id is
++ * broken across different firmware revisions and HTT version fails to
++ * indicate this.
++ */
+ HTT_T2H_MSG_TYPE_TEST,
++
+ /* keep this last */
+ HTT_T2H_NUM_MSGS
+ };
+@@ -657,6 +668,53 @@ struct htt_rx_fragment_indication {
+ #define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK 0x00000FC0
+ #define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB 6
+
++struct htt_rx_pn_ind {
++ __le16 peer_id;
++ u8 tid;
++ u8 seqno_start;
++ u8 seqno_end;
++ u8 pn_ie_count;
++ u8 reserved;
++ u8 pn_ies[0];
++} __packed;
++
++struct htt_rx_offload_msdu {
++ __le16 msdu_len;
++ __le16 peer_id;
++ u8 vdev_id;
++ u8 tid;
++ u8 fw_desc;
++ u8 payload[0];
++} __packed;
++
++struct htt_rx_offload_ind {
++ u8 reserved;
++ __le16 msdu_count;
++} __packed;
++
++struct htt_rx_in_ord_msdu_desc {
++ __le32 msdu_paddr;
++ __le16 msdu_len;
++ u8 fw_desc;
++ u8 reserved;
++} __packed;
++
++struct htt_rx_in_ord_ind {
++ u8 info;
++ __le16 peer_id;
++ u8 vdev_id;
++ u8 reserved;
++ __le16 msdu_count;
++ struct htt_rx_in_ord_msdu_desc msdu_descs[0];
++} __packed;
++
++#define HTT_RX_IN_ORD_IND_INFO_TID_MASK 0x0000001f
++#define HTT_RX_IN_ORD_IND_INFO_TID_LSB 0
++#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK 0x00000020
++#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_LSB 5
++#define HTT_RX_IN_ORD_IND_INFO_FRAG_MASK 0x00000040
++#define HTT_RX_IN_ORD_IND_INFO_FRAG_LSB 6
++
+ /*
+ * target -> host test message definition
+ *
+@@ -732,7 +790,7 @@ static inline u8 *htt_rx_test_get_chars(
+ */
+ struct htt_pktlog_msg {
+ u8 pad[3];
+- __le32 payload[1 /* or more */];
++ u8 payload[0];
+ } __packed;
+
+ struct htt_dbg_stats_rx_reorder_stats {
+@@ -1038,6 +1096,7 @@ static inline struct htt_stats_conf_item
+ {
+ return (void *)item + sizeof(*item) + roundup(item->length, 4);
+ }
++
+ /*
+ * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank
+ *
+@@ -1151,10 +1210,12 @@ struct htt_resp {
+ struct htt_rx_test rx_test;
+ struct htt_pktlog_msg pktlog_msg;
+ struct htt_stats_conf stats_conf;
++ struct htt_rx_pn_ind rx_pn_ind;
++ struct htt_rx_offload_ind rx_offload_ind;
++ struct htt_rx_in_ord_ind rx_in_ord_ind;
+ };
+ } __packed;
+
+-
+ /*** host side structures follow ***/
+
+ struct htt_tx_done {
+@@ -1184,7 +1245,6 @@ struct ath10k_htt {
+ struct ath10k *ar;
+ enum ath10k_htc_ep_id eid;
+
+- int max_throughput_mbps;
+ u8 target_version_major;
+ u8 target_version_minor;
+ struct completion target_version_received;
+@@ -1200,6 +1260,20 @@ struct ath10k_htt {
+ * filled.
+ */
+ struct sk_buff **netbufs_ring;
++
++ /* This is used only with firmware supporting IN_ORD_IND.
++ *
++ * With Full Rx Reorder the HTT Rx Ring is more of a temporary
++ * buffer ring from which buffer addresses are copied by the
++ * firmware to MAC Rx ring. Firmware then delivers IN_ORD_IND
++ * pointing to specific (re-ordered) buffers.
++ *
++ * FIXME: With kernel generic hashing functions there's a lot
++ * of hash collisions for sk_buffs.
++ */
++ bool in_ord_rx;
++ DECLARE_HASHTABLE(skb_table, 4);
++
+ /*
+ * Ring of buffer addresses -
+ * This ring holds the "physical" device address of the
+@@ -1254,12 +1328,11 @@ struct ath10k_htt {
+
+ unsigned int prefetch_len;
+
+- /* Protects access to %pending_tx, %used_msdu_ids */
++ /* Protects access to pending_tx, num_pending_tx */
+ spinlock_t tx_lock;
+ int max_num_pending_tx;
+ int num_pending_tx;
+- struct sk_buff **pending_tx;
+- unsigned long *used_msdu_ids; /* bitmap */
++ struct idr pending_tx;
+ wait_queue_head_t empty_tx_wq;
+ struct dma_pool *tx_pool;
+
+@@ -1273,6 +1346,7 @@ struct ath10k_htt {
+ struct tasklet_struct txrx_compl_task;
+ struct sk_buff_head tx_compl_q;
+ struct sk_buff_head rx_compl_q;
++ struct sk_buff_head rx_in_ord_compl_q;
+
+ /* rx_status template */
+ struct ieee80211_rx_status rx_status;
+@@ -1328,22 +1402,28 @@ struct htt_rx_desc {
+ #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */
+ #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
+
+-int ath10k_htt_attach(struct ath10k *ar);
+-int ath10k_htt_attach_target(struct ath10k_htt *htt);
+-void ath10k_htt_detach(struct ath10k_htt *htt);
+-
+-int ath10k_htt_tx_attach(struct ath10k_htt *htt);
+-void ath10k_htt_tx_detach(struct ath10k_htt *htt);
+-int ath10k_htt_rx_attach(struct ath10k_htt *htt);
+-void ath10k_htt_rx_detach(struct ath10k_htt *htt);
++int ath10k_htt_connect(struct ath10k_htt *htt);
++int ath10k_htt_init(struct ath10k *ar);
++int ath10k_htt_setup(struct ath10k_htt *htt);
++
++int ath10k_htt_tx_alloc(struct ath10k_htt *htt);
++void ath10k_htt_tx_free(struct ath10k_htt *htt);
++
++int ath10k_htt_rx_alloc(struct ath10k_htt *htt);
++int ath10k_htt_rx_ring_refill(struct ath10k *ar);
++void ath10k_htt_rx_free(struct ath10k_htt *htt);
++
+ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
+ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
+ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
++int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
++ u8 max_subfrms_ampdu,
++ u8 max_subfrms_amsdu);
+
+ void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
+-int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
++int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
+ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
+ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
+ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
++++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
+@@ -21,118 +21,84 @@
+ #include "txrx.h"
+ #include "debug.h"
+ #include "trace.h"
++#include "mac.h"
+
+ #include <linux/log2.h>
+
+-/* slightly larger than one large A-MPDU */
+-#define HTT_RX_RING_SIZE_MIN 128
+-
+-/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
+-#define HTT_RX_RING_SIZE_MAX 2048
+-
+-#define HTT_RX_AVG_FRM_BYTES 1000
+-
+-/* ms, very conservative */
+-#define HTT_RX_HOST_LATENCY_MAX_MS 20
+-
+-/* ms, conservative */
+-#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
++#define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX
++#define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1)
+
+ /* when under memory pressure rx ring refill may fail and needs a retry */
+ #define HTT_RX_RING_REFILL_RETRY_MS 50
+
+-
+ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
+ static void ath10k_htt_txrx_compl_task(unsigned long ptr);
+
+-static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
+-{
+- int size;
+-
+- /*
+- * It is expected that the host CPU will typically be able to
+- * service the rx indication from one A-MPDU before the rx
+- * indication from the subsequent A-MPDU happens, roughly 1-2 ms
+- * later. However, the rx ring should be sized very conservatively,
+- * to accomodate the worst reasonable delay before the host CPU
+- * services a rx indication interrupt.
+- *
+- * The rx ring need not be kept full of empty buffers. In theory,
+- * the htt host SW can dynamically track the low-water mark in the
+- * rx ring, and dynamically adjust the level to which the rx ring
+- * is filled with empty buffers, to dynamically meet the desired
+- * low-water mark.
+- *
+- * In contrast, it's difficult to resize the rx ring itself, once
+- * it's in use. Thus, the ring itself should be sized very
+- * conservatively, while the degree to which the ring is filled
+- * with empty buffers should be sized moderately conservatively.
+- */
+-
+- /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+- size =
+- htt->max_throughput_mbps +
+- 1000 /
+- (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
+-
+- if (size < HTT_RX_RING_SIZE_MIN)
+- size = HTT_RX_RING_SIZE_MIN;
+-
+- if (size > HTT_RX_RING_SIZE_MAX)
+- size = HTT_RX_RING_SIZE_MAX;
+-
+- size = roundup_pow_of_two(size);
+-
+- return size;
+-}
+-
+-static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
++static struct sk_buff *
++ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u32 paddr)
+ {
+- int size;
++ struct ath10k_skb_rxcb *rxcb;
+
+- /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+- size =
+- htt->max_throughput_mbps *
+- 1000 /
+- (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
+-
+- /*
+- * Make sure the fill level is at least 1 less than the ring size.
+- * Leaving 1 element empty allows the SW to easily distinguish
+- * between a full ring vs. an empty ring.
+- */
+- if (size >= htt->rx_ring.size)
+- size = htt->rx_ring.size - 1;
++ hash_for_each_possible(ar->htt.rx_ring.skb_table, rxcb, hlist, paddr)
++ if (rxcb->paddr == paddr)
++ return ATH10K_RXCB_SKB(rxcb);
+
+- return size;
++ WARN_ON_ONCE(1);
++ return NULL;
+ }
+
+ static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
+ {
+ struct sk_buff *skb;
+- struct ath10k_skb_cb *cb;
++ struct ath10k_skb_rxcb *rxcb;
++ struct hlist_node *n;
+ int i;
+
+- for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
+- skb = htt->rx_ring.netbufs_ring[i];
+- cb = ATH10K_SKB_CB(skb);
+- dma_unmap_single(htt->ar->dev, cb->paddr,
+- skb->len + skb_tailroom(skb),
+- DMA_FROM_DEVICE);
+- dev_kfree_skb_any(skb);
++ if (htt->rx_ring.in_ord_rx) {
++ hash_for_each_safe(htt->rx_ring.skb_table, i, n, rxcb, hlist) {
++ skb = ATH10K_RXCB_SKB(rxcb);
++ dma_unmap_single(htt->ar->dev, rxcb->paddr,
++ skb->len + skb_tailroom(skb),
++ DMA_FROM_DEVICE);
++ hash_del(&rxcb->hlist);
++ dev_kfree_skb_any(skb);
++ }
++ } else {
++ for (i = 0; i < htt->rx_ring.size; i++) {
++ skb = htt->rx_ring.netbufs_ring[i];
++ if (!skb)
++ continue;
++
++ rxcb = ATH10K_SKB_RXCB(skb);
++ dma_unmap_single(htt->ar->dev, rxcb->paddr,
++ skb->len + skb_tailroom(skb),
++ DMA_FROM_DEVICE);
++ dev_kfree_skb_any(skb);
++ }
+ }
+
+ htt->rx_ring.fill_cnt = 0;
++ hash_init(htt->rx_ring.skb_table);
++ memset(htt->rx_ring.netbufs_ring, 0,
++ htt->rx_ring.size * sizeof(htt->rx_ring.netbufs_ring[0]));
+ }
+
+ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+ {
+ struct htt_rx_desc *rx_desc;
++ struct ath10k_skb_rxcb *rxcb;
+ struct sk_buff *skb;
+ dma_addr_t paddr;
+ int ret = 0, idx;
+
+- idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr));
++ /* The Full Rx Reorder firmware has no way of telling the host
++ * implicitly when it copied HTT Rx Ring buffers to MAC Rx Ring.
++ * To keep things simple make sure ring is always half empty. This
++ * guarantees there'll be no replenishment overruns possible.
++ */
++ BUILD_BUG_ON(HTT_RX_RING_FILL_LEVEL >= HTT_RX_RING_SIZE / 2);
++
++ idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
+ while (num > 0) {
+ skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+ if (!skb) {
+@@ -159,18 +125,30 @@ static int __ath10k_htt_rx_ring_fill_n(s
+ goto fail;
+ }
+
+- ATH10K_SKB_CB(skb)->paddr = paddr;
++ rxcb = ATH10K_SKB_RXCB(skb);
++ rxcb->paddr = paddr;
+ htt->rx_ring.netbufs_ring[idx] = skb;
+ htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
+ htt->rx_ring.fill_cnt++;
+
++ if (htt->rx_ring.in_ord_rx) {
++ hash_add(htt->rx_ring.skb_table,
++ &ATH10K_SKB_RXCB(skb)->hlist,
++ (u32)paddr);
++ }
++
+ num--;
+ idx++;
+ idx &= htt->rx_ring.size_mask;
+ }
+
+ fail:
+- *(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx);
++ /*
++ * Make sure the rx buffer is updated before available buffer
++ * index to avoid any potential rx ring corruption.
++ */
++ mb();
++ *htt->rx_ring.alloc_idx.vaddr = __cpu_to_le32(idx);
+ return ret;
+ }
+
+@@ -198,7 +176,7 @@ static void ath10k_htt_rx_msdu_buff_repl
+ * automatically balances load wrt to CPU power.
+ *
+ * This probably comes at a cost of lower maximum throughput but
+- * improves the avarage and stability. */
++ * improves the average and stability. */
+ spin_lock_bh(&htt->rx_ring.lock);
+ num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+ num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
+@@ -222,32 +200,37 @@ static void ath10k_htt_rx_msdu_buff_repl
+ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
+ {
+ struct ath10k_htt *htt = (struct ath10k_htt *)arg;
++
+ ath10k_htt_rx_msdu_buff_replenish(htt);
+ }
+
+-void ath10k_htt_rx_detach(struct ath10k_htt *htt)
++int ath10k_htt_rx_ring_refill(struct ath10k *ar)
+ {
+- int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
++ struct ath10k_htt *htt = &ar->htt;
++ int ret;
++
++ spin_lock_bh(&htt->rx_ring.lock);
++ ret = ath10k_htt_rx_ring_fill_n(htt, (htt->rx_ring.fill_level -
++ htt->rx_ring.fill_cnt));
++ spin_unlock_bh(&htt->rx_ring.lock);
++
++ if (ret)
++ ath10k_htt_rx_ring_free(htt);
++
++ return ret;
++}
+
++void ath10k_htt_rx_free(struct ath10k_htt *htt)
++{
+ del_timer_sync(&htt->rx_ring.refill_retry_timer);
+ tasklet_kill(&htt->rx_replenish_task);
+ tasklet_kill(&htt->txrx_compl_task);
+
+ skb_queue_purge(&htt->tx_compl_q);
+ skb_queue_purge(&htt->rx_compl_q);
++ skb_queue_purge(&htt->rx_in_ord_compl_q);
+
+- while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
+- struct sk_buff *skb =
+- htt->rx_ring.netbufs_ring[sw_rd_idx];
+- struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+-
+- dma_unmap_single(htt->ar->dev, cb->paddr,
+- skb->len + skb_tailroom(skb),
+- DMA_FROM_DEVICE);
+- dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]);
+- sw_rd_idx++;
+- sw_rd_idx &= htt->rx_ring.size_mask;
+- }
++ ath10k_htt_rx_ring_free(htt);
+
+ dma_free_coherent(htt->ar->dev,
+ (htt->rx_ring.size *
+@@ -265,66 +248,59 @@ void ath10k_htt_rx_detach(struct ath10k_
+
+ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
+ {
++ struct ath10k *ar = htt->ar;
+ int idx;
+ struct sk_buff *msdu;
+
+ lockdep_assert_held(&htt->rx_ring.lock);
+
+ if (htt->rx_ring.fill_cnt == 0) {
+- ath10k_warn("tried to pop sk_buff from an empty rx ring\n");
++ ath10k_warn(ar, "tried to pop sk_buff from an empty rx ring\n");
+ return NULL;
+ }
+
+ idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+ msdu = htt->rx_ring.netbufs_ring[idx];
++ htt->rx_ring.netbufs_ring[idx] = NULL;
++ htt->rx_ring.paddrs_ring[idx] = 0;
+
+ idx++;
+ idx &= htt->rx_ring.size_mask;
+ htt->rx_ring.sw_rd_idx.msdu_payld = idx;
+ htt->rx_ring.fill_cnt--;
+
+- return msdu;
+-}
+-
+-static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
+-{
+- struct sk_buff *next;
++ dma_unmap_single(htt->ar->dev,
++ ATH10K_SKB_RXCB(msdu)->paddr,
++ msdu->len + skb_tailroom(msdu),
++ DMA_FROM_DEVICE);
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
++ msdu->data, msdu->len + skb_tailroom(msdu));
+
+- while (skb) {
+- next = skb->next;
+- dev_kfree_skb_any(skb);
+- skb = next;
+- }
++ return msdu;
+ }
+
+ /* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
+ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
+ u8 **fw_desc, int *fw_desc_len,
+- struct sk_buff **head_msdu,
+- struct sk_buff **tail_msdu)
++ struct sk_buff_head *amsdu)
+ {
++ struct ath10k *ar = htt->ar;
+ int msdu_len, msdu_chaining = 0;
+ struct sk_buff *msdu;
+ struct htt_rx_desc *rx_desc;
+
+ lockdep_assert_held(&htt->rx_ring.lock);
+
+- if (htt->rx_confused) {
+- ath10k_warn("htt is confused. refusing rx\n");
+- return -1;
+- }
+-
+- msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
+- while (msdu) {
++ for (;;) {
+ int last_msdu, msdu_len_invalid, msdu_chained;
+
+- dma_unmap_single(htt->ar->dev,
+- ATH10K_SKB_CB(msdu)->paddr,
+- msdu->len + skb_tailroom(msdu),
+- DMA_FROM_DEVICE);
++ msdu = ath10k_htt_rx_netbuf_pop(htt);
++ if (!msdu) {
++ __skb_queue_purge(amsdu);
++ return -ENOENT;
++ }
+
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ",
+- msdu->data, msdu->len + skb_tailroom(msdu));
++ __skb_queue_tail(amsdu, msdu);
+
+ rx_desc = (struct htt_rx_desc *)msdu->data;
+
+@@ -343,12 +319,8 @@ static int ath10k_htt_rx_amsdu_pop(struc
+ */
+ if (!(__le32_to_cpu(rx_desc->attention.flags)
+ & RX_ATTENTION_FLAGS_MSDU_DONE)) {
+- ath10k_htt_rx_free_msdu_chain(*head_msdu);
+- *head_msdu = NULL;
+- msdu = NULL;
+- ath10k_err("htt rx stopped. cannot recover\n");
+- htt->rx_confused = true;
+- break;
++ __skb_queue_purge(amsdu);
++ return -EIO;
+ }
+
+ /*
+@@ -399,7 +371,6 @@ static int ath10k_htt_rx_amsdu_pop(struc
+ msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
+ RX_MSDU_START_INFO0_MSDU_LENGTH);
+ msdu_chained = rx_desc->frag_info.ring2_more_count;
+- msdu_chaining = msdu_chained;
+
+ if (msdu_len_invalid)
+ msdu_len = 0;
+@@ -408,42 +379,32 @@ static int ath10k_htt_rx_amsdu_pop(struc
+ skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
+ msdu_len -= msdu->len;
+
+- /* FIXME: Do chained buffers include htt_rx_desc or not? */
++ /* Note: Chained buffers do not contain rx descriptor */
+ while (msdu_chained--) {
+- struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+-
+- dma_unmap_single(htt->ar->dev,
+- ATH10K_SKB_CB(next)->paddr,
+- next->len + skb_tailroom(next),
+- DMA_FROM_DEVICE);
+-
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL,
+- "htt rx chained: ", next->data,
+- next->len + skb_tailroom(next));
+-
+- skb_trim(next, 0);
+- skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
+- msdu_len -= next->len;
++ msdu = ath10k_htt_rx_netbuf_pop(htt);
++ if (!msdu) {
++ __skb_queue_purge(amsdu);
++ return -ENOENT;
++ }
+
+- msdu->next = next;
+- msdu = next;
++ __skb_queue_tail(amsdu, msdu);
++ skb_trim(msdu, 0);
++ skb_put(msdu, min(msdu_len, HTT_RX_BUF_SIZE));
++ msdu_len -= msdu->len;
++ msdu_chaining = 1;
+ }
+
+ last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
+ RX_MSDU_END_INFO0_LAST_MSDU;
+
+- if (last_msdu) {
+- msdu->next = NULL;
++ trace_ath10k_htt_rx_desc(ar, &rx_desc->attention,
++ sizeof(*rx_desc) - sizeof(u32));
++
++ if (last_msdu)
+ break;
+- } else {
+- struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+- msdu->next = next;
+- msdu = next;
+- }
+ }
+- *tail_msdu = msdu;
+
+- if (*head_msdu == NULL)
++ if (skb_queue_empty(amsdu))
+ msdu_chaining = -1;
+
+ /*
+@@ -465,43 +426,117 @@ static int ath10k_htt_rx_amsdu_pop(struc
+ static void ath10k_htt_rx_replenish_task(unsigned long ptr)
+ {
+ struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
++
+ ath10k_htt_rx_msdu_buff_replenish(htt);
+ }
+
+-int ath10k_htt_rx_attach(struct ath10k_htt *htt)
++static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
++ u32 paddr)
++{
++ struct ath10k *ar = htt->ar;
++ struct ath10k_skb_rxcb *rxcb;
++ struct sk_buff *msdu;
++
++ lockdep_assert_held(&htt->rx_ring.lock);
++
++ msdu = ath10k_htt_rx_find_skb_paddr(ar, paddr);
++ if (!msdu)
++ return NULL;
++
++ rxcb = ATH10K_SKB_RXCB(msdu);
++ hash_del(&rxcb->hlist);
++ htt->rx_ring.fill_cnt--;
++
++ dma_unmap_single(htt->ar->dev, rxcb->paddr,
++ msdu->len + skb_tailroom(msdu),
++ DMA_FROM_DEVICE);
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
++ msdu->data, msdu->len + skb_tailroom(msdu));
++
++ return msdu;
++}
++
++static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt,
++ struct htt_rx_in_ord_ind *ev,
++ struct sk_buff_head *list)
+ {
++ struct ath10k *ar = htt->ar;
++ struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs;
++ struct htt_rx_desc *rxd;
++ struct sk_buff *msdu;
++ int msdu_count;
++ bool is_offload;
++ u32 paddr;
++
++ lockdep_assert_held(&htt->rx_ring.lock);
++
++ msdu_count = __le16_to_cpu(ev->msdu_count);
++ is_offload = !!(ev->info & HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
++
++ while (msdu_count--) {
++ paddr = __le32_to_cpu(msdu_desc->msdu_paddr);
++
++ msdu = ath10k_htt_rx_pop_paddr(htt, paddr);
++ if (!msdu) {
++ __skb_queue_purge(list);
++ return -ENOENT;
++ }
++
++ __skb_queue_tail(list, msdu);
++
++ if (!is_offload) {
++ rxd = (void *)msdu->data;
++
++ trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));
++
++ skb_put(msdu, sizeof(*rxd));
++ skb_pull(msdu, sizeof(*rxd));
++ skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len));
++
++ if (!(__le32_to_cpu(rxd->attention.flags) &
++ RX_ATTENTION_FLAGS_MSDU_DONE)) {
++ ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
++ return -EIO;
++ }
++ }
++
++ msdu_desc++;
++ }
++
++ return 0;
++}
++
++int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
++{
++ struct ath10k *ar = htt->ar;
+ dma_addr_t paddr;
+ void *vaddr;
++ size_t size;
+ struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
+
+- htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+- if (!is_power_of_2(htt->rx_ring.size)) {
+- ath10k_warn("htt rx ring size is not power of 2\n");
+- return -EINVAL;
+- }
++ htt->rx_confused = false;
+
++ /* XXX: The fill level could be changed during runtime in response to
++ * the host processing latency. Is this really worth it?
++ */
++ htt->rx_ring.size = HTT_RX_RING_SIZE;
+ htt->rx_ring.size_mask = htt->rx_ring.size - 1;
++ htt->rx_ring.fill_level = HTT_RX_RING_FILL_LEVEL;
+
+- /*
+- * Set the initial value for the level to which the rx ring
+- * should be filled, based on the max throughput and the
+- * worst likely latency for the host to fill the rx ring
+- * with new buffers. In theory, this fill level can be
+- * dynamically adjusted from the initial value set here, to
+- * reflect the actual host latency rather than a
+- * conservative assumption about the host latency.
+- */
+- htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
++ if (!is_power_of_2(htt->rx_ring.size)) {
++ ath10k_warn(ar, "htt rx ring size is not power of 2\n");
++ return -EINVAL;
++ }
+
+ htt->rx_ring.netbufs_ring =
+- kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
++ kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!htt->rx_ring.netbufs_ring)
+ goto err_netbuf;
+
+- vaddr = dma_alloc_coherent(htt->ar->dev,
+- (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)),
+- &paddr, GFP_DMA);
++ size = htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring);
++
++ vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_DMA);
+ if (!vaddr)
+ goto err_dma_ring;
+
+@@ -516,7 +551,7 @@ int ath10k_htt_rx_attach(struct ath10k_h
+
+ htt->rx_ring.alloc_idx.vaddr = vaddr;
+ htt->rx_ring.alloc_idx.paddr = paddr;
+- htt->rx_ring.sw_rd_idx.msdu_payld = 0;
++ htt->rx_ring.sw_rd_idx.msdu_payld = htt->rx_ring.size_mask;
+ *htt->rx_ring.alloc_idx.vaddr = 0;
+
+ /* Initialize the Rx refill retry timer */
+@@ -525,28 +560,23 @@ int ath10k_htt_rx_attach(struct ath10k_h
+ spin_lock_init(&htt->rx_ring.lock);
+
+ htt->rx_ring.fill_cnt = 0;
+- if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
+- goto err_fill_ring;
++ htt->rx_ring.sw_rd_idx.msdu_payld = 0;
++ hash_init(htt->rx_ring.skb_table);
+
+ tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
+ (unsigned long)htt);
+
+ skb_queue_head_init(&htt->tx_compl_q);
+ skb_queue_head_init(&htt->rx_compl_q);
++ skb_queue_head_init(&htt->rx_in_ord_compl_q);
+
+ tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
+ (unsigned long)htt);
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
+ htt->rx_ring.size, htt->rx_ring.fill_level);
+ return 0;
+
+-err_fill_ring:
+- ath10k_htt_rx_ring_free(htt);
+- dma_free_coherent(htt->ar->dev,
+- sizeof(*htt->rx_ring.alloc_idx.vaddr),
+- htt->rx_ring.alloc_idx.vaddr,
+- htt->rx_ring.alloc_idx.paddr);
+ err_dma_idx:
+ dma_free_coherent(htt->ar->dev,
+ (htt->rx_ring.size *
+@@ -559,73 +589,54 @@ err_netbuf:
+ return -ENOMEM;
+ }
+
+-static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type)
++static int ath10k_htt_rx_crypto_param_len(struct ath10k *ar,
++ enum htt_rx_mpdu_encrypt_type type)
+ {
+ switch (type) {
++ case HTT_RX_MPDU_ENCRYPT_NONE:
++ return 0;
+ case HTT_RX_MPDU_ENCRYPT_WEP40:
+ case HTT_RX_MPDU_ENCRYPT_WEP104:
+- return 4;
++ return IEEE80211_WEP_IV_LEN;
+ case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+- case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
+ case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+- case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
++ return IEEE80211_TKIP_IV_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+- return 8;
+- case HTT_RX_MPDU_ENCRYPT_NONE:
+- return 0;
++ return IEEE80211_CCMP_HDR_LEN;
++ case HTT_RX_MPDU_ENCRYPT_WEP128:
++ case HTT_RX_MPDU_ENCRYPT_WAPI:
++ break;
+ }
+
+- ath10k_warn("unknown encryption type %d\n", type);
++ ath10k_warn(ar, "unsupported encryption type %d\n", type);
+ return 0;
+ }
+
+-static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type)
++#define MICHAEL_MIC_LEN 8
++
++static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
++ enum htt_rx_mpdu_encrypt_type type)
+ {
+ switch (type) {
+ case HTT_RX_MPDU_ENCRYPT_NONE:
++ return 0;
+ case HTT_RX_MPDU_ENCRYPT_WEP40:
+ case HTT_RX_MPDU_ENCRYPT_WEP104:
+- case HTT_RX_MPDU_ENCRYPT_WEP128:
+- case HTT_RX_MPDU_ENCRYPT_WAPI:
+- return 0;
++ return IEEE80211_WEP_ICV_LEN;
+ case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+ case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+- return 4;
++ return IEEE80211_TKIP_ICV_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+- return 8;
++ return IEEE80211_CCMP_MIC_LEN;
++ case HTT_RX_MPDU_ENCRYPT_WEP128:
++ case HTT_RX_MPDU_ENCRYPT_WAPI:
++ break;
+ }
+
+- ath10k_warn("unknown encryption type %d\n", type);
++ ath10k_warn(ar, "unsupported encryption type %d\n", type);
+ return 0;
+ }
+
+-/* Applies for first msdu in chain, before altering it. */
+-static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
+-{
+- struct htt_rx_desc *rxd;
+- enum rx_msdu_decap_format fmt;
+-
+- rxd = (void *)skb->data - sizeof(*rxd);
+- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+- RX_MSDU_START_INFO1_DECAP_FORMAT);
+-
+- if (fmt == RX_MSDU_DECAP_RAW)
+- return (void *)skb->data;
+- else
+- return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+-}
+-
+-/* This function only applies for first msdu in an msdu chain */
+-static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
+-{
+- if (ieee80211_is_data_qos(hdr->frame_control)) {
+- u8 *qc = ieee80211_get_qos_ctl(hdr);
+- if (qc[0] & 0x80)
+- return true;
+- }
+- return false;
+-}
+-
+ struct rfc1042_hdr {
+ u8 llc_dsap;
+ u8 llc_ssap;
+@@ -660,23 +671,34 @@ static const u8 rx_legacy_rate_idx[] = {
+ };
+
+ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
+- enum ieee80211_band band,
+- u8 info0, u32 info1, u32 info2,
+- struct ieee80211_rx_status *status)
++ struct ieee80211_rx_status *status,
++ struct htt_rx_desc *rxd)
+ {
++ enum ieee80211_band band;
+ u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
+ u8 preamble = 0;
++ u32 info1, info2, info3;
+
+- /* Check if valid fields */
+- if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
++ /* Band value can't be set as undefined but freq can be 0 - use that to
++ * determine whether band is provided.
++ *
++ * FIXME: Perhaps this can go away if CCK rate reporting is a little
++ * reworked?
++ */
++ if (!status->freq)
+ return;
+
+- preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
++ band = status->band;
++ info1 = __le32_to_cpu(rxd->ppdu_start.info1);
++ info2 = __le32_to_cpu(rxd->ppdu_start.info2);
++ info3 = __le32_to_cpu(rxd->ppdu_start.info3);
++
++ preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE);
+
+ switch (preamble) {
+ case HTT_RX_LEGACY:
+- cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
+- rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
++ cck = info1 & RX_PPDU_START_INFO1_L_SIG_RATE_SELECT;
++ rate = MS(info1, RX_PPDU_START_INFO1_L_SIG_RATE);
+ rate_idx = 0;
+
+ if (rate < 0x08 || rate > 0x0F)
+@@ -703,11 +725,11 @@ static void ath10k_htt_rx_h_rates(struct
+ break;
+ case HTT_RX_HT:
+ case HTT_RX_HT_WITH_TXBF:
+- /* HT-SIG - Table 20-11 in info1 and info2 */
+- mcs = info1 & 0x1F;
++ /* HT-SIG - Table 20-11 in info2 and info3 */
++ mcs = info2 & 0x1F;
+ nss = mcs >> 3;
+- bw = (info1 >> 7) & 1;
+- sgi = (info2 >> 7) & 1;
++ bw = (info2 >> 7) & 1;
++ sgi = (info3 >> 7) & 1;
+
+ status->rate_idx = mcs;
+ status->flag |= RX_FLAG_HT;
+@@ -718,12 +740,12 @@ static void ath10k_htt_rx_h_rates(struct
+ break;
+ case HTT_RX_VHT:
+ case HTT_RX_VHT_WITH_TXBF:
+- /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
++ /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
+ TODO check this */
+- mcs = (info2 >> 4) & 0x0F;
+- nss = ((info1 >> 10) & 0x07) + 1;
+- bw = info1 & 3;
+- sgi = info2 & 1;
++ mcs = (info3 >> 4) & 0x0F;
++ nss = ((info2 >> 10) & 0x07) + 1;
++ bw = info2 & 3;
++ sgi = info3 & 1;
+
+ status->rate_idx = mcs;
+ status->vht_nss = nss;
+@@ -751,28 +773,6 @@ static void ath10k_htt_rx_h_rates(struct
+ }
+ }
+
+-static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
+- struct ieee80211_rx_status *rx_status,
+- struct sk_buff *skb,
+- enum htt_rx_mpdu_encrypt_type enctype)
+-{
+- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+-
+-
+- if (enctype == HTT_RX_MPDU_ENCRYPT_NONE) {
+- rx_status->flag &= ~(RX_FLAG_DECRYPTED |
+- RX_FLAG_IV_STRIPPED |
+- RX_FLAG_MMIC_STRIPPED);
+- return;
+- }
+-
+- rx_status->flag |= RX_FLAG_DECRYPTED |
+- RX_FLAG_IV_STRIPPED |
+- RX_FLAG_MMIC_STRIPPED;
+- hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) &
+- ~IEEE80211_FCTL_PROTECTED);
+-}
+-
+ static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
+ struct ieee80211_rx_status *status)
+ {
+@@ -793,19 +793,121 @@ static bool ath10k_htt_rx_h_channel(stru
+ return true;
+ }
+
++static void ath10k_htt_rx_h_signal(struct ath10k *ar,
++ struct ieee80211_rx_status *status,
++ struct htt_rx_desc *rxd)
++{
++ /* FIXME: Get real NF */
++ status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
++ rxd->ppdu_start.rssi_comb;
++ status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
++}
++
++static void ath10k_htt_rx_h_mactime(struct ath10k *ar,
++ struct ieee80211_rx_status *status,
++ struct htt_rx_desc *rxd)
++{
++ /* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This
++ * means all prior MSDUs in a PPDU are reported to mac80211 without the
++ * TSF. Is it worth holding frames until end of PPDU is known?
++ *
++ * FIXME: Can we get/compute 64bit TSF?
++ */
++ status->mactime = __le32_to_cpu(rxd->ppdu_end.common.tsf_timestamp);
++ status->flag |= RX_FLAG_MACTIME_END;
++}
++
++static void ath10k_htt_rx_h_ppdu(struct ath10k *ar,
++ struct sk_buff_head *amsdu,
++ struct ieee80211_rx_status *status)
++{
++ struct sk_buff *first;
++ struct htt_rx_desc *rxd;
++ bool is_first_ppdu;
++ bool is_last_ppdu;
++
++ if (skb_queue_empty(amsdu))
++ return;
++
++ first = skb_peek(amsdu);
++ rxd = (void *)first->data - sizeof(*rxd);
++
++ is_first_ppdu = !!(rxd->attention.flags &
++ __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU));
++ is_last_ppdu = !!(rxd->attention.flags &
++ __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU));
++
++ if (is_first_ppdu) {
++ /* New PPDU starts so clear out the old per-PPDU status. */
++ status->freq = 0;
++ status->rate_idx = 0;
++ status->vht_nss = 0;
++ status->vht_flag &= ~RX_VHT_FLAG_80MHZ;
++ status->flag &= ~(RX_FLAG_HT |
++ RX_FLAG_VHT |
++ RX_FLAG_SHORT_GI |
++ RX_FLAG_40MHZ |
++ RX_FLAG_MACTIME_END);
++ status->flag |= RX_FLAG_NO_SIGNAL_VAL;
++
++ ath10k_htt_rx_h_signal(ar, status, rxd);
++ ath10k_htt_rx_h_channel(ar, status);
++ ath10k_htt_rx_h_rates(ar, status, rxd);
++ }
++
++ if (is_last_ppdu)
++ ath10k_htt_rx_h_mactime(ar, status, rxd);
++}
++
++static const char * const tid_to_ac[] = {
++ "BE",
++ "BK",
++ "BK",
++ "BE",
++ "VI",
++ "VI",
++ "VO",
++ "VO",
++};
++
++static char *ath10k_get_tid(struct ieee80211_hdr *hdr, char *out, size_t size)
++{
++ u8 *qc;
++ int tid;
++
++ if (!ieee80211_is_data_qos(hdr->frame_control))
++ return "";
++
++ qc = ieee80211_get_qos_ctl(hdr);
++ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
++ if (tid < 8)
++ snprintf(out, size, "tid %d (%s)", tid, tid_to_ac[tid]);
++ else
++ snprintf(out, size, "tid %d", tid);
++
++ return out;
++}
++
+ static void ath10k_process_rx(struct ath10k *ar,
+ struct ieee80211_rx_status *rx_status,
+ struct sk_buff *skb)
+ {
+ struct ieee80211_rx_status *status;
++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
++ char tid[32];
+
+ status = IEEE80211_SKB_RXCB(skb);
+ *status = *rx_status;
+
+- ath10k_dbg(ATH10K_DBG_DATA,
+- "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %imic-err %i\n",
++ ath10k_dbg(ar, ATH10K_DBG_DATA,
++ "rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
+ skb,
+ skb->len,
++ ieee80211_get_SA(hdr),
++ ath10k_get_tid(hdr, tid, sizeof(tid)),
++ is_multicast_ether_addr(ieee80211_get_DA(hdr)) ?
++ "mcast" : "ucast",
++ (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4,
+ status->flag == 0 ? "legacy" : "",
+ status->flag & RX_FLAG_HT ? "ht" : "",
+ status->flag & RX_FLAG_VHT ? "vht" : "",
+@@ -817,9 +919,12 @@ static void ath10k_process_rx(struct ath
+ status->freq,
+ status->band, status->flag,
+ !!(status->flag & RX_FLAG_FAILED_FCS_CRC),
+- !!(status->flag & RX_FLAG_MMIC_ERROR));
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
++ !!(status->flag & RX_FLAG_MMIC_ERROR),
++ !!(status->flag & RX_FLAG_AMSDU_MORE));
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
+ skb->data, skb->len);
++ trace_ath10k_rx_hdr(ar, skb->data, skb->len);
++ trace_ath10k_rx_payload(ar, skb->data, skb->len);
+
+ ieee80211_rx(ar->hw, skb);
+ }
+@@ -830,179 +935,263 @@ static int ath10k_htt_rx_nwifi_hdrlen(st
+ return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+ }
+
+-static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+- struct ieee80211_rx_status *rx_status,
+- struct sk_buff *skb_in)
++static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
++ struct sk_buff *msdu,
++ struct ieee80211_rx_status *status,
++ enum htt_rx_mpdu_encrypt_type enctype,
++ bool is_decrypted)
+ {
++ struct ieee80211_hdr *hdr;
+ struct htt_rx_desc *rxd;
+- struct sk_buff *skb = skb_in;
+- struct sk_buff *first;
+- enum rx_msdu_decap_format fmt;
+- enum htt_rx_mpdu_encrypt_type enctype;
++ size_t hdr_len;
++ size_t crypto_len;
++ bool is_first;
++ bool is_last;
++
++ rxd = (void *)msdu->data - sizeof(*rxd);
++ is_first = !!(rxd->msdu_end.info0 &
++ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
++ is_last = !!(rxd->msdu_end.info0 &
++ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
++
++ /* Delivered decapped frame:
++ * [802.11 header]
++ * [crypto param] <-- can be trimmed if !fcs_err &&
++ * !decrypt_err && !peer_idx_invalid
++ * [amsdu header] <-- only if A-MSDU
++ * [rfc1042/llc]
++ * [payload]
++ * [FCS] <-- at end, needs to be trimmed
++ */
++
++ /* This probably shouldn't happen but warn just in case */
++ if (unlikely(WARN_ON_ONCE(!is_first)))
++ return;
++
++ /* This probably shouldn't happen but warn just in case */
++ if (unlikely(WARN_ON_ONCE(!(is_first && is_last))))
++ return;
++
++ skb_trim(msdu, msdu->len - FCS_LEN);
++
++ /* In most cases this will be true for sniffed frames. It makes sense
++ * to deliver them as-is without stripping the crypto param. This would
++ * also make sense for software based decryption (which is not
++ * implemented in ath10k).
++ *
++ * If there's no error then the frame is decrypted. At least that is
++ * the case for frames that come in via fragmented rx indication.
++ */
++ if (!is_decrypted)
++ return;
++
++ /* The payload is decrypted so strip crypto params. Start from tail
++ * since hdr is used to compute some stuff.
++ */
++
++ hdr = (void *)msdu->data;
++
++ /* Tail */
++ skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
++
++ /* MMIC */
++ if (!ieee80211_has_morefrags(hdr->frame_control) &&
++ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
++ skb_trim(msdu, msdu->len - 8);
++
++ /* Head */
++ hdr_len = ieee80211_hdrlen(hdr->frame_control);
++ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
++
++ memmove((void *)msdu->data + crypto_len,
++ (void *)msdu->data, hdr_len);
++ skb_pull(msdu, crypto_len);
++}
++
++static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
++ struct sk_buff *msdu,
++ struct ieee80211_rx_status *status,
++ const u8 first_hdr[64])
++{
+ struct ieee80211_hdr *hdr;
+- u8 hdr_buf[64], addr[ETH_ALEN], *qos;
+- unsigned int hdr_len;
++ size_t hdr_len;
++ u8 da[ETH_ALEN];
++ u8 sa[ETH_ALEN];
++
++ /* Delivered decapped frame:
++ * [nwifi 802.11 header] <-- replaced with 802.11 hdr
++ * [rfc1042/llc]
++ *
++ * Note: The nwifi header doesn't have QoS Control and is
++ * (always?) a 3addr frame.
++ *
++ * Note2: There's no A-MSDU subframe header. Even if it's part
++ * of an A-MSDU.
++ */
+
+- rxd = (void *)skb->data - sizeof(*rxd);
+- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
++ /* pull decapped header and copy SA & DA */
++ hdr = (struct ieee80211_hdr *)msdu->data;
++ hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
++ ether_addr_copy(da, ieee80211_get_DA(hdr));
++ ether_addr_copy(sa, ieee80211_get_SA(hdr));
++ skb_pull(msdu, hdr_len);
+
+- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
++ /* push original 802.11 header */
++ hdr = (struct ieee80211_hdr *)first_hdr;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+- memcpy(hdr_buf, hdr, hdr_len);
+- hdr = (struct ieee80211_hdr *)hdr_buf;
++ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+
+- first = skb;
+- while (skb) {
+- void *decap_hdr;
+- int len;
+-
+- rxd = (void *)skb->data - sizeof(*rxd);
+- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+- RX_MSDU_START_INFO1_DECAP_FORMAT);
+- decap_hdr = (void *)rxd->rx_hdr_status;
+-
+- skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
+-
+- /* First frame in an A-MSDU chain has more decapped data. */
+- if (skb == first) {
+- len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
+- len += round_up(ath10k_htt_rx_crypto_param_len(enctype),
+- 4);
+- decap_hdr += len;
+- }
++ /* original 802.11 header has a different DA and in
++ * case of 4addr it may also have different SA
++ */
++ hdr = (struct ieee80211_hdr *)msdu->data;
++ ether_addr_copy(ieee80211_get_DA(hdr), da);
++ ether_addr_copy(ieee80211_get_SA(hdr), sa);
++}
+
+- switch (fmt) {
+- case RX_MSDU_DECAP_RAW:
+- /* remove trailing FCS */
+- skb_trim(skb, skb->len - FCS_LEN);
+- break;
+- case RX_MSDU_DECAP_NATIVE_WIFI:
+- /* pull decapped header and copy DA */
+- hdr = (struct ieee80211_hdr *)skb->data;
+- hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+- memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN);
+- skb_pull(skb, hdr_len);
+-
+- /* push original 802.11 header */
+- hdr = (struct ieee80211_hdr *)hdr_buf;
+- hdr_len = ieee80211_hdrlen(hdr->frame_control);
+- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+-
+- /* original A-MSDU header has the bit set but we're
+- * not including A-MSDU subframe header */
+- hdr = (struct ieee80211_hdr *)skb->data;
+- qos = ieee80211_get_qos_ctl(hdr);
+- qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
++static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
++ struct sk_buff *msdu,
++ enum htt_rx_mpdu_encrypt_type enctype)
++{
++ struct ieee80211_hdr *hdr;
++ struct htt_rx_desc *rxd;
++ size_t hdr_len, crypto_len;
++ void *rfc1042;
++ bool is_first, is_last, is_amsdu;
+
+- /* original 802.11 header has a different DA */
+- memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN);
+- break;
+- case RX_MSDU_DECAP_ETHERNET2_DIX:
+- /* strip ethernet header and insert decapped 802.11
+- * header, amsdu subframe header and rfc1042 header */
+-
+- len = 0;
+- len += sizeof(struct rfc1042_hdr);
+- len += sizeof(struct amsdu_subframe_hdr);
+-
+- skb_pull(skb, sizeof(struct ethhdr));
+- memcpy(skb_push(skb, len), decap_hdr, len);
+- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+- break;
+- case RX_MSDU_DECAP_8023_SNAP_LLC:
+- /* insert decapped 802.11 header making a singly
+- * A-MSDU */
+- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+- break;
+- }
++ rxd = (void *)msdu->data - sizeof(*rxd);
++ hdr = (void *)rxd->rx_hdr_status;
+
+- skb_in = skb;
+- ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype);
+- skb = skb->next;
+- skb_in->next = NULL;
++ is_first = !!(rxd->msdu_end.info0 &
++ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
++ is_last = !!(rxd->msdu_end.info0 &
++ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
++ is_amsdu = !(is_first && is_last);
+
+- if (skb)
+- rx_status->flag |= RX_FLAG_AMSDU_MORE;
+- else
+- rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
++ rfc1042 = hdr;
++
++ if (is_first) {
++ hdr_len = ieee80211_hdrlen(hdr->frame_control);
++ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
+
+- ath10k_process_rx(htt->ar, rx_status, skb_in);
++ rfc1042 += round_up(hdr_len, 4) +
++ round_up(crypto_len, 4);
+ }
+
+- /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
+- * monitor interface active for sniffing purposes. */
++ if (is_amsdu)
++ rfc1042 += sizeof(struct amsdu_subframe_hdr);
++
++ return rfc1042;
+ }
+
+-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
+- struct ieee80211_rx_status *rx_status,
+- struct sk_buff *skb)
++static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
++ struct sk_buff *msdu,
++ struct ieee80211_rx_status *status,
++ const u8 first_hdr[64],
++ enum htt_rx_mpdu_encrypt_type enctype)
+ {
+- struct htt_rx_desc *rxd;
+ struct ieee80211_hdr *hdr;
+- enum rx_msdu_decap_format fmt;
+- enum htt_rx_mpdu_encrypt_type enctype;
+- int hdr_len;
++ struct ethhdr *eth;
++ size_t hdr_len;
+ void *rfc1042;
++ u8 da[ETH_ALEN];
++ u8 sa[ETH_ALEN];
+
+- /* This shouldn't happen. If it does than it may be a FW bug. */
+- if (skb->next) {
+- ath10k_warn("htt rx received chained non A-MSDU frame\n");
+- ath10k_htt_rx_free_msdu_chain(skb->next);
+- skb->next = NULL;
+- }
++ /* Delivered decapped frame:
++ * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
++ * [payload]
++ */
+
+- rxd = (void *)skb->data - sizeof(*rxd);
+- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+- RX_MSDU_START_INFO1_DECAP_FORMAT);
+- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
++ rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
++ if (WARN_ON_ONCE(!rfc1042))
++ return;
++
++ /* pull decapped header and copy SA & DA */
++ eth = (struct ethhdr *)msdu->data;
++ ether_addr_copy(da, eth->h_dest);
++ ether_addr_copy(sa, eth->h_source);
++ skb_pull(msdu, sizeof(struct ethhdr));
++
++ /* push rfc1042/llc/snap */
++ memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
++ sizeof(struct rfc1042_hdr));
++
++ /* push original 802.11 header */
++ hdr = (struct ieee80211_hdr *)first_hdr;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
++ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
++
++ /* original 802.11 header has a different DA and in
++ * case of 4addr it may also have different SA
++ */
++ hdr = (struct ieee80211_hdr *)msdu->data;
++ ether_addr_copy(ieee80211_get_DA(hdr), da);
++ ether_addr_copy(ieee80211_get_SA(hdr), sa);
++}
++
++static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
++ struct sk_buff *msdu,
++ struct ieee80211_rx_status *status,
++ const u8 first_hdr[64])
++{
++ struct ieee80211_hdr *hdr;
++ size_t hdr_len;
++
++ /* Delivered decapped frame:
++ * [amsdu header] <-- replaced with 802.11 hdr
++ * [rfc1042/llc]
++ * [payload]
++ */
++
++ skb_pull(msdu, sizeof(struct amsdu_subframe_hdr));
++
++ hdr = (struct ieee80211_hdr *)first_hdr;
++ hdr_len = ieee80211_hdrlen(hdr->frame_control);
++ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
++}
++
++static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
++ struct sk_buff *msdu,
++ struct ieee80211_rx_status *status,
++ u8 first_hdr[64],
++ enum htt_rx_mpdu_encrypt_type enctype,
++ bool is_decrypted)
++{
++ struct htt_rx_desc *rxd;
++ enum rx_msdu_decap_format decap;
++ struct ieee80211_hdr *hdr;
++
++ /* First msdu's decapped header:
++ * [802.11 header] <-- padded to 4 bytes long
++ * [crypto param] <-- padded to 4 bytes long
++ * [amsdu header] <-- only if A-MSDU
++ * [rfc1042/llc]
++ *
++ * Other (2nd, 3rd, ..) msdu's decapped header:
++ * [amsdu header] <-- only if A-MSDU
++ * [rfc1042/llc]
++ */
+
+- skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
++ rxd = (void *)msdu->data - sizeof(*rxd);
++ hdr = (void *)rxd->rx_hdr_status;
++ decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
++ RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+- switch (fmt) {
++ switch (decap) {
+ case RX_MSDU_DECAP_RAW:
+- /* remove trailing FCS */
+- skb_trim(skb, skb->len - FCS_LEN);
++ ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
++ is_decrypted);
+ break;
+ case RX_MSDU_DECAP_NATIVE_WIFI:
+- /* Pull decapped header */
+- hdr = (struct ieee80211_hdr *)skb->data;
+- hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+- skb_pull(skb, hdr_len);
+-
+- /* Push original header */
+- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+- hdr_len = ieee80211_hdrlen(hdr->frame_control);
+- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
++ ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
+ break;
+ case RX_MSDU_DECAP_ETHERNET2_DIX:
+- /* strip ethernet header and insert decapped 802.11 header and
+- * rfc1042 header */
+-
+- rfc1042 = hdr;
+- rfc1042 += roundup(hdr_len, 4);
+- rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+-
+- skb_pull(skb, sizeof(struct ethhdr));
+- memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
+- rfc1042, sizeof(struct rfc1042_hdr));
+- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
++ ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
+ break;
+ case RX_MSDU_DECAP_8023_SNAP_LLC:
+- /* remove A-MSDU subframe header and insert
+- * decapped 802.11 header. rfc1042 header is already there */
+-
+- skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
+- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
++ ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
+ break;
+ }
+-
+- ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype);
+-
+- ath10k_process_rx(htt->ar, rx_status, skb);
+ }
+
+ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
+@@ -1036,10 +1225,128 @@ static int ath10k_htt_rx_get_csum_state(
+ return CHECKSUM_UNNECESSARY;
+ }
+
+-static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
++static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
++{
++ msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
++}
++
++static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
++ struct sk_buff_head *amsdu,
++ struct ieee80211_rx_status *status)
++{
++ struct sk_buff *first;
++ struct sk_buff *last;
++ struct sk_buff *msdu;
++ struct htt_rx_desc *rxd;
++ struct ieee80211_hdr *hdr;
++ enum htt_rx_mpdu_encrypt_type enctype;
++ u8 first_hdr[64];
++ u8 *qos;
++ size_t hdr_len;
++ bool has_fcs_err;
++ bool has_crypto_err;
++ bool has_tkip_err;
++ bool has_peer_idx_invalid;
++ bool is_decrypted;
++ u32 attention;
++
++ if (skb_queue_empty(amsdu))
++ return;
++
++ first = skb_peek(amsdu);
++ rxd = (void *)first->data - sizeof(*rxd);
++
++ enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
++ RX_MPDU_START_INFO0_ENCRYPT_TYPE);
++
++ /* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
++ * decapped header. It'll be used for undecapping of each MSDU.
++ */
++ hdr = (void *)rxd->rx_hdr_status;
++ hdr_len = ieee80211_hdrlen(hdr->frame_control);
++ memcpy(first_hdr, hdr, hdr_len);
++
++ /* Each A-MSDU subframe will use the original header as the base and be
++ * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
++ */
++ hdr = (void *)first_hdr;
++ qos = ieee80211_get_qos_ctl(hdr);
++ qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
++
++ /* Some attention flags are valid only in the last MSDU. */
++ last = skb_peek_tail(amsdu);
++ rxd = (void *)last->data - sizeof(*rxd);
++ attention = __le32_to_cpu(rxd->attention.flags);
++
++ has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
++ has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
++ has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
++ has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
++
++ /* Note: If hardware captures an encrypted frame that it can't decrypt,
++ * e.g. due to fcs error, missing peer or invalid key data it will
++ * report the frame as raw.
++ */
++ is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
++ !has_fcs_err &&
++ !has_crypto_err &&
++ !has_peer_idx_invalid);
++
++ /* Clear per-MPDU flags while leaving per-PPDU flags intact. */
++ status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
++ RX_FLAG_MMIC_ERROR |
++ RX_FLAG_DECRYPTED |
++ RX_FLAG_IV_STRIPPED |
++ RX_FLAG_MMIC_STRIPPED);
++
++ if (has_fcs_err)
++ status->flag |= RX_FLAG_FAILED_FCS_CRC;
++
++ if (has_tkip_err)
++ status->flag |= RX_FLAG_MMIC_ERROR;
++
++ if (is_decrypted)
++ status->flag |= RX_FLAG_DECRYPTED |
++ RX_FLAG_IV_STRIPPED |
++ RX_FLAG_MMIC_STRIPPED;
++
++ skb_queue_walk(amsdu, msdu) {
++ ath10k_htt_rx_h_csum_offload(msdu);
++ ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
++ is_decrypted);
++
++ /* Undecapping involves copying the original 802.11 header back
++ * to sk_buff. If frame is protected and hardware has decrypted
++ * it then remove the protected bit.
++ */
++ if (!is_decrypted)
++ continue;
++
++ hdr = (void *)msdu->data;
++ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
++ }
++}
++
++static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
++ struct sk_buff_head *amsdu,
++ struct ieee80211_rx_status *status)
++{
++ struct sk_buff *msdu;
++
++ while ((msdu = __skb_dequeue(amsdu))) {
++ /* Setup per-MSDU flags */
++ if (skb_queue_empty(amsdu))
++ status->flag &= ~RX_FLAG_AMSDU_MORE;
++ else
++ status->flag |= RX_FLAG_AMSDU_MORE;
++
++ ath10k_process_rx(ar, status, msdu);
++ }
++}
++
++static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
+ {
+- struct sk_buff *next = msdu_head->next;
+- struct sk_buff *to_free = next;
++ struct sk_buff *skb, *first;
+ int space;
+ int total_len = 0;
+
+@@ -1050,110 +1357,142 @@ static int ath10k_unchain_msdu(struct sk
+ * skb?
+ */
+
+- msdu_head->next = NULL;
++ first = __skb_dequeue(amsdu);
+
+ /* Allocate total length all at once. */
+- while (next) {
+- total_len += next->len;
+- next = next->next;
+- }
++ skb_queue_walk(amsdu, skb)
++ total_len += skb->len;
+
+- space = total_len - skb_tailroom(msdu_head);
++ space = total_len - skb_tailroom(first);
+ if ((space > 0) &&
+- (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) {
++ (pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0)) {
+ /* TODO: bump some rx-oom error stat */
+ /* put it back together so we can free the
+ * whole list at once.
+ */
+- msdu_head->next = to_free;
++ __skb_queue_head(amsdu, first);
+ return -1;
+ }
+
+ /* Walk list again, copying contents into
+ * msdu_head
+ */
+- next = to_free;
+- while (next) {
+- skb_copy_from_linear_data(next, skb_put(msdu_head, next->len),
+- next->len);
+- next = next->next;
++ while ((skb = __skb_dequeue(amsdu))) {
++ skb_copy_from_linear_data(skb, skb_put(first, skb->len),
++ skb->len);
++ dev_kfree_skb_any(skb);
+ }
+
+- /* If here, we have consolidated skb. Free the
+- * fragments and pass the main skb on up the
+- * stack.
+- */
+- ath10k_htt_rx_free_msdu_chain(to_free);
++ __skb_queue_head(amsdu, first);
+ return 0;
+ }
+
+-static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
+- struct sk_buff *head,
+- enum htt_rx_mpdu_status status,
+- bool channel_set,
+- u32 attention)
+-{
+- if (head->len == 0) {
+- ath10k_dbg(ATH10K_DBG_HTT,
+- "htt rx dropping due to zero-len\n");
+- return false;
+- }
++static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
++ struct sk_buff_head *amsdu,
++ bool chained)
++{
++ struct sk_buff *first;
++ struct htt_rx_desc *rxd;
++ enum rx_msdu_decap_format decap;
+
+- if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
+- ath10k_dbg(ATH10K_DBG_HTT,
+- "htt rx dropping due to decrypt-err\n");
+- return false;
+- }
++ first = skb_peek(amsdu);
++ rxd = (void *)first->data - sizeof(*rxd);
++ decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
++ RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+- if (!channel_set) {
+- ath10k_warn("no channel configured; ignoring frame!\n");
+- return false;
++ if (!chained)
++ return;
++
++ /* FIXME: Current unchaining logic can only handle simple case of raw
++ * msdu chaining. If decapping is other than raw the chaining may be
++ * more complex and this isn't handled by the current code. Don't even
++ * try re-constructing such frames - it'll be pretty much garbage.
++ */
++ if (decap != RX_MSDU_DECAP_RAW ||
++ skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
++ __skb_queue_purge(amsdu);
++ return;
+ }
+
+- /* Skip mgmt frames while we handle this in WMI */
+- if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
+- attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
+- ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
++ ath10k_unchain_msdu(amsdu);
++}
++
++static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
++ struct sk_buff_head *amsdu,
++ struct ieee80211_rx_status *rx_status)
++{
++ struct sk_buff *msdu;
++ struct htt_rx_desc *rxd;
++ bool is_mgmt;
++ bool has_fcs_err;
++
++ msdu = skb_peek(amsdu);
++ rxd = (void *)msdu->data - sizeof(*rxd);
++
++ /* FIXME: It might be a good idea to do some fuzzy-testing to drop
++ * invalid/dangerous frames.
++ */
++
++ if (!rx_status->freq) {
++ ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n");
+ return false;
+ }
+
+- if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+- status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+- status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
+- !htt->ar->monitor_started) {
+- ath10k_dbg(ATH10K_DBG_HTT,
+- "htt rx ignoring frame w/ status %d\n",
+- status);
++ is_mgmt = !!(rxd->attention.flags &
++ __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
++ has_fcs_err = !!(rxd->attention.flags &
++ __cpu_to_le32(RX_ATTENTION_FLAGS_FCS_ERR));
++
++ /* Management frames are handled via WMI events. The pros of such
++ * approach is that channel is explicitly provided in WMI events
++ * whereas HTT doesn't provide channel information for Rxed frames.
++ *
++ * However some firmware revisions don't report corrupted frames via
++ * WMI so don't drop them.
++ */
++ if (is_mgmt && !has_fcs_err) {
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
+ return false;
+ }
+
+- if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+- ath10k_dbg(ATH10K_DBG_HTT,
+- "htt rx CAC running\n");
++ if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
+ return false;
+ }
+
+ return true;
+ }
+
++static void ath10k_htt_rx_h_filter(struct ath10k *ar,
++ struct sk_buff_head *amsdu,
++ struct ieee80211_rx_status *rx_status)
++{
++ if (skb_queue_empty(amsdu))
++ return;
++
++ if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
++ return;
++
++ __skb_queue_purge(amsdu);
++}
++
+ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
+ struct htt_rx_indication *rx)
+ {
++ struct ath10k *ar = htt->ar;
+ struct ieee80211_rx_status *rx_status = &htt->rx_status;
+ struct htt_rx_indication_mpdu_range *mpdu_ranges;
+- struct htt_rx_desc *rxd;
+- enum htt_rx_mpdu_status status;
+- struct ieee80211_hdr *hdr;
++ struct sk_buff_head amsdu;
+ int num_mpdu_ranges;
+- u32 attention;
+ int fw_desc_len;
+ u8 *fw_desc;
+- bool channel_set;
+- int i, j;
+- int ret;
++ int i, ret, mpdu_count = 0;
+
+ lockdep_assert_held(&htt->rx_ring.lock);
+
++ if (htt->rx_confused)
++ return;
++
+ fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
+ fw_desc = (u8 *)&rx->fw_desc;
+
+@@ -1161,201 +1500,82 @@ static void ath10k_htt_rx_handler(struct
+ HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+ mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
+
+- /* Fill this once, while this is per-ppdu */
+- if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
+- memset(rx_status, 0, sizeof(*rx_status));
+- rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
+- rx->ppdu.combined_rssi;
+- }
+-
+- if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
+- /* TSF available only in 32-bit */
+- rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
+- rx_status->flag |= RX_FLAG_MACTIME_END;
+- }
+-
+- channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
+-
+- if (channel_set) {
+- ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
+- rx->ppdu.info0,
+- __le32_to_cpu(rx->ppdu.info1),
+- __le32_to_cpu(rx->ppdu.info2),
+- rx_status);
+- }
+-
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
+ rx, sizeof(*rx) +
+ (sizeof(struct htt_rx_indication_mpdu_range) *
+ num_mpdu_ranges));
+
+- for (i = 0; i < num_mpdu_ranges; i++) {
+- status = mpdu_ranges[i].mpdu_range_status;
+-
+- for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
+- struct sk_buff *msdu_head, *msdu_tail;
++ for (i = 0; i < num_mpdu_ranges; i++)
++ mpdu_count += mpdu_ranges[i].mpdu_count;
+
+- msdu_head = NULL;
+- msdu_tail = NULL;
+- ret = ath10k_htt_rx_amsdu_pop(htt,
+- &fw_desc,
+- &fw_desc_len,
+- &msdu_head,
+- &msdu_tail);
+-
+- if (ret < 0) {
+- ath10k_warn("failed to pop amsdu from htt rx ring %d\n",
+- ret);
+- ath10k_htt_rx_free_msdu_chain(msdu_head);
+- continue;
+- }
+-
+- rxd = container_of((void *)msdu_head->data,
+- struct htt_rx_desc,
+- msdu_payload);
+- attention = __le32_to_cpu(rxd->attention.flags);
+-
+- if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
+- status,
+- channel_set,
+- attention)) {
+- ath10k_htt_rx_free_msdu_chain(msdu_head);
+- continue;
+- }
+-
+- if (ret > 0 &&
+- ath10k_unchain_msdu(msdu_head) < 0) {
+- ath10k_htt_rx_free_msdu_chain(msdu_head);
+- continue;
+- }
+-
+- if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
+- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+- else
+- rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
+-
+- if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
+- rx_status->flag |= RX_FLAG_MMIC_ERROR;
+- else
+- rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
+-
+- hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
+-
+- if (ath10k_htt_rx_hdr_is_amsdu(hdr))
+- ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
+- else
+- ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
++ while (mpdu_count--) {
++ __skb_queue_head_init(&amsdu);
++ ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc,
++ &fw_desc_len, &amsdu);
++ if (ret < 0) {
++ ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
++ __skb_queue_purge(&amsdu);
++ /* FIXME: It's probably a good idea to reboot the
++ * device instead of leaving it inoperable.
++ */
++ htt->rx_confused = true;
++ break;
+ }
++
++ ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
++ ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
++ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
++ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
++ ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
+ }
+
+ tasklet_schedule(&htt->rx_replenish_task);
+ }
+
+ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
+- struct htt_rx_fragment_indication *frag)
++ struct htt_rx_fragment_indication *frag)
+ {
+- struct sk_buff *msdu_head, *msdu_tail;
+- enum htt_rx_mpdu_encrypt_type enctype;
+- struct htt_rx_desc *rxd;
+- enum rx_msdu_decap_format fmt;
++ struct ath10k *ar = htt->ar;
+ struct ieee80211_rx_status *rx_status = &htt->rx_status;
+- struct ieee80211_hdr *hdr;
++ struct sk_buff_head amsdu;
+ int ret;
+- bool tkip_mic_err;
+- bool decrypt_err;
+ u8 *fw_desc;
+- int fw_desc_len, hdrlen, paramlen;
+- int trim;
++ int fw_desc_len;
+
+ fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
+ fw_desc = (u8 *)frag->fw_msdu_rx_desc;
+
+- msdu_head = NULL;
+- msdu_tail = NULL;
++ __skb_queue_head_init(&amsdu);
+
+ spin_lock_bh(&htt->rx_ring.lock);
+ ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+- &msdu_head, &msdu_tail);
++ &amsdu);
+ spin_unlock_bh(&htt->rx_ring.lock);
+
+- ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
++ tasklet_schedule(&htt->rx_replenish_task);
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
+
+ if (ret) {
+- ath10k_warn("failed to pop amsdu from httr rx ring for fragmented rx %d\n",
++ ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n",
+ ret);
+- ath10k_htt_rx_free_msdu_chain(msdu_head);
++ __skb_queue_purge(&amsdu);
+ return;
+ }
+
+- /* FIXME: implement signal strength */
+-
+- hdr = (struct ieee80211_hdr *)msdu_head->data;
+- rxd = (void *)msdu_head->data - sizeof(*rxd);
+- tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) &
+- RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+- decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) &
+- RX_ATTENTION_FLAGS_DECRYPT_ERR);
+- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+- RX_MSDU_START_INFO1_DECAP_FORMAT);
+-
+- if (fmt != RX_MSDU_DECAP_RAW) {
+- ath10k_warn("we dont support non-raw fragmented rx yet\n");
+- dev_kfree_skb_any(msdu_head);
+- goto end;
+- }
+-
+- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+- ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype);
+- msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
+-
+- if (tkip_mic_err)
+- ath10k_warn("tkip mic error\n");
+-
+- if (decrypt_err) {
+- ath10k_warn("decryption err in fragmented rx\n");
+- dev_kfree_skb_any(msdu_head);
+- goto end;
+- }
+-
+- if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
+- hdrlen = ieee80211_hdrlen(hdr->frame_control);
+- paramlen = ath10k_htt_rx_crypto_param_len(enctype);
+-
+- /* It is more efficient to move the header than the payload */
+- memmove((void *)msdu_head->data + paramlen,
+- (void *)msdu_head->data,
+- hdrlen);
+- skb_pull(msdu_head, paramlen);
+- hdr = (struct ieee80211_hdr *)msdu_head->data;
+- }
+-
+- /* remove trailing FCS */
+- trim = 4;
+-
+- /* remove crypto trailer */
+- trim += ath10k_htt_rx_crypto_tail_len(enctype);
+-
+- /* last fragment of TKIP frags has MIC */
+- if (!ieee80211_has_morefrags(hdr->frame_control) &&
+- enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+- trim += 8;
+-
+- if (trim > msdu_head->len) {
+- ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
+- dev_kfree_skb_any(msdu_head);
+- goto end;
++ if (skb_queue_len(&amsdu) != 1) {
++ ath10k_warn(ar, "failed to pop frag amsdu: too many msdus\n");
++ __skb_queue_purge(&amsdu);
++ return;
+ }
+
+- skb_trim(msdu_head, msdu_head->len - trim);
+-
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
+- msdu_head->data, msdu_head->len);
+- ath10k_process_rx(htt->ar, rx_status, msdu_head);
++ ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
++ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
++ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
++ ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
+
+-end:
+ if (fw_desc_len > 0) {
+- ath10k_dbg(ATH10K_DBG_HTT,
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
+ "expecting more fragmented rx in one indication %d\n",
+ fw_desc_len);
+ }
+@@ -1385,12 +1605,12 @@ static void ath10k_htt_rx_frm_tx_compl(s
+ tx_done.discard = true;
+ break;
+ default:
+- ath10k_warn("unhandled tx completion status %d\n", status);
++ ath10k_warn(ar, "unhandled tx completion status %d\n", status);
+ tx_done.discard = true;
+ break;
+ }
+
+- ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+ resp->data_tx_completion.num_msdus);
+
+ for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+@@ -1400,6 +1620,274 @@ static void ath10k_htt_rx_frm_tx_compl(s
+ }
+ }
+
++static void ath10k_htt_rx_addba(struct ath10k *ar, struct htt_resp *resp)
++{
++ struct htt_rx_addba *ev = &resp->rx_addba;
++ struct ath10k_peer *peer;
++ struct ath10k_vif *arvif;
++ u16 info0, tid, peer_id;
++
++ info0 = __le16_to_cpu(ev->info0);
++ tid = MS(info0, HTT_RX_BA_INFO0_TID);
++ peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID);
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
++ "htt rx addba tid %hu peer_id %hu size %hhu\n",
++ tid, peer_id, ev->window_size);
++
++ spin_lock_bh(&ar->data_lock);
++ peer = ath10k_peer_find_by_id(ar, peer_id);
++ if (!peer) {
++ ath10k_warn(ar, "received addba event for invalid peer_id: %hu\n",
++ peer_id);
++ spin_unlock_bh(&ar->data_lock);
++ return;
++ }
++
++ arvif = ath10k_get_arvif(ar, peer->vdev_id);
++ if (!arvif) {
++ ath10k_warn(ar, "received addba event for invalid vdev_id: %u\n",
++ peer->vdev_id);
++ spin_unlock_bh(&ar->data_lock);
++ return;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
++ "htt rx start rx ba session sta %pM tid %hu size %hhu\n",
++ peer->addr, tid, ev->window_size);
++
++ ieee80211_start_rx_ba_session_offl(arvif->vif, peer->addr, tid);
++ spin_unlock_bh(&ar->data_lock);
++}
++
++static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp)
++{
++ struct htt_rx_delba *ev = &resp->rx_delba;
++ struct ath10k_peer *peer;
++ struct ath10k_vif *arvif;
++ u16 info0, tid, peer_id;
++
++ info0 = __le16_to_cpu(ev->info0);
++ tid = MS(info0, HTT_RX_BA_INFO0_TID);
++ peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID);
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
++ "htt rx delba tid %hu peer_id %hu\n",
++ tid, peer_id);
++
++ spin_lock_bh(&ar->data_lock);
++ peer = ath10k_peer_find_by_id(ar, peer_id);
++ if (!peer) {
++ ath10k_warn(ar, "received addba event for invalid peer_id: %hu\n",
++ peer_id);
++ spin_unlock_bh(&ar->data_lock);
++ return;
++ }
++
++ arvif = ath10k_get_arvif(ar, peer->vdev_id);
++ if (!arvif) {
++ ath10k_warn(ar, "received addba event for invalid vdev_id: %u\n",
++ peer->vdev_id);
++ spin_unlock_bh(&ar->data_lock);
++ return;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
++ "htt rx stop rx ba session sta %pM tid %hu\n",
++ peer->addr, tid);
++
++ ieee80211_stop_rx_ba_session_offl(arvif->vif, peer->addr, tid);
++ spin_unlock_bh(&ar->data_lock);
++}
++
++static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list,
++ struct sk_buff_head *amsdu)
++{
++ struct sk_buff *msdu;
++ struct htt_rx_desc *rxd;
++
++ if (skb_queue_empty(list))
++ return -ENOBUFS;
++
++ if (WARN_ON(!skb_queue_empty(amsdu)))
++ return -EINVAL;
++
++ while ((msdu = __skb_dequeue(list))) {
++ __skb_queue_tail(amsdu, msdu);
++
++ rxd = (void *)msdu->data - sizeof(*rxd);
++ if (rxd->msdu_end.info0 &
++ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))
++ break;
++ }
++
++ msdu = skb_peek_tail(amsdu);
++ rxd = (void *)msdu->data - sizeof(*rxd);
++ if (!(rxd->msdu_end.info0 &
++ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) {
++ skb_queue_splice_init(amsdu, list);
++ return -EAGAIN;
++ }
++
++ return 0;
++}
++
++static void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status,
++ struct sk_buff *skb)
++{
++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
++
++ if (!ieee80211_has_protected(hdr->frame_control))
++ return;
++
++ /* Offloaded frames are already decrypted but firmware insists they are
++ * protected in the 802.11 header. Strip the flag. Otherwise mac80211
++ * will drop the frame.
++ */
++
++ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
++ status->flag |= RX_FLAG_DECRYPTED |
++ RX_FLAG_IV_STRIPPED |
++ RX_FLAG_MMIC_STRIPPED;
++}
++
++static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
++ struct sk_buff_head *list)
++{
++ struct ath10k_htt *htt = &ar->htt;
++ struct ieee80211_rx_status *status = &htt->rx_status;
++ struct htt_rx_offload_msdu *rx;
++ struct sk_buff *msdu;
++ size_t offset;
++
++ while ((msdu = __skb_dequeue(list))) {
++ /* Offloaded frames don't have Rx descriptor. Instead they have
++ * a short meta information header.
++ */
++
++ rx = (void *)msdu->data;
++
++ skb_put(msdu, sizeof(*rx));
++ skb_pull(msdu, sizeof(*rx));
++
++ if (skb_tailroom(msdu) < __le16_to_cpu(rx->msdu_len)) {
++ ath10k_warn(ar, "dropping frame: offloaded rx msdu is too long!\n");
++ dev_kfree_skb_any(msdu);
++ continue;
++ }
++
++ skb_put(msdu, __le16_to_cpu(rx->msdu_len));
++
++ /* Offloaded rx header length isn't multiple of 2 nor 4 so the
++ * actual payload is unaligned. Align the frame. Otherwise
++ * mac80211 complains. This shouldn't reduce performance much
++ * because these offloaded frames are rare.
++ */
++ offset = 4 - ((unsigned long)msdu->data & 3);
++ skb_put(msdu, offset);
++ memmove(msdu->data + offset, msdu->data, msdu->len);
++ skb_pull(msdu, offset);
++
++ /* FIXME: The frame is NWifi. Re-construct QoS Control
++ * if possible later.
++ */
++
++ memset(status, 0, sizeof(*status));
++ status->flag |= RX_FLAG_NO_SIGNAL_VAL;
++
++ ath10k_htt_rx_h_rx_offload_prot(status, msdu);
++ ath10k_htt_rx_h_channel(ar, status);
++ ath10k_process_rx(ar, status, msdu);
++ }
++}
++
++static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct ath10k_htt *htt = &ar->htt;
++ struct htt_resp *resp = (void *)skb->data;
++ struct ieee80211_rx_status *status = &htt->rx_status;
++ struct sk_buff_head list;
++ struct sk_buff_head amsdu;
++ u16 peer_id;
++ u16 msdu_count;
++ u8 vdev_id;
++ u8 tid;
++ bool offload;
++ bool frag;
++ int ret;
++
++ lockdep_assert_held(&htt->rx_ring.lock);
++
++ if (htt->rx_confused)
++ return;
++
++ skb_pull(skb, sizeof(resp->hdr));
++ skb_pull(skb, sizeof(resp->rx_in_ord_ind));
++
++ peer_id = __le16_to_cpu(resp->rx_in_ord_ind.peer_id);
++ msdu_count = __le16_to_cpu(resp->rx_in_ord_ind.msdu_count);
++ vdev_id = resp->rx_in_ord_ind.vdev_id;
++ tid = SM(resp->rx_in_ord_ind.info, HTT_RX_IN_ORD_IND_INFO_TID);
++ offload = !!(resp->rx_in_ord_ind.info &
++ HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
++ frag = !!(resp->rx_in_ord_ind.info & HTT_RX_IN_ORD_IND_INFO_FRAG_MASK);
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
++ "htt rx in ord vdev %i peer %i tid %i offload %i frag %i msdu count %i\n",
++ vdev_id, peer_id, tid, offload, frag, msdu_count);
++
++ if (skb->len < msdu_count * sizeof(*resp->rx_in_ord_ind.msdu_descs)) {
++ ath10k_warn(ar, "dropping invalid in order rx indication\n");
++ return;
++ }
++
++ /* The event can deliver more than 1 A-MSDU. Each A-MSDU is later
++ * extracted and processed.
++ */
++ __skb_queue_head_init(&list);
++ ret = ath10k_htt_rx_pop_paddr_list(htt, &resp->rx_in_ord_ind, &list);
++ if (ret < 0) {
++ ath10k_warn(ar, "failed to pop paddr list: %d\n", ret);
++ htt->rx_confused = true;
++ return;
++ }
++
++ /* Offloaded frames are very different and need to be handled
++ * separately.
++ */
++ if (offload)
++ ath10k_htt_rx_h_rx_offload(ar, &list);
++
++ while (!skb_queue_empty(&list)) {
++ __skb_queue_head_init(&amsdu);
++ ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu);
++ switch (ret) {
++ case 0:
++ /* Note: The in-order indication may report interleaved
++ * frames from different PPDUs meaning reported rx rate
++ * to mac80211 isn't accurate/reliable. It's still
++ * better to report something than nothing though. This
++ * should still give an idea about rx rate to the user.
++ */
++ ath10k_htt_rx_h_ppdu(ar, &amsdu, status);
++ ath10k_htt_rx_h_filter(ar, &amsdu, status);
++ ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
++ ath10k_htt_rx_h_deliver(ar, &amsdu, status);
++ break;
++ case -EAGAIN:
++ /* fall through */
++ default:
++ /* Should not happen. */
++ ath10k_warn(ar, "failed to extract amsdu: %d\n", ret);
++ htt->rx_confused = true;
++ __skb_queue_purge(&list);
++ return;
++ }
++ }
++
++ tasklet_schedule(&htt->rx_replenish_task);
++}
++
+ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
+ {
+ struct ath10k_htt *htt = &ar->htt;
+@@ -1407,9 +1895,9 @@ void ath10k_htt_t2h_msg_handler(struct a
+
+ /* confirm alignment */
+ if (!IS_ALIGNED((unsigned long)skb->data, 4))
+- ath10k_warn("unaligned htt message, expect trouble\n");
++ ath10k_warn(ar, "unaligned htt message, expect trouble\n");
+
+- ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n",
+ resp->hdr.msg_type);
+ switch (resp->hdr.msg_type) {
+ case HTT_T2H_MSG_TYPE_VERSION_CONF: {
+@@ -1473,7 +1961,7 @@ void ath10k_htt_t2h_msg_handler(struct a
+ struct ath10k *ar = htt->ar;
+ struct htt_security_indication *ev = &resp->security_indication;
+
+- ath10k_dbg(ATH10K_DBG_HTT,
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
+ "sec ind peer_id %d unicast %d type %d\n",
+ __le16_to_cpu(ev->peer_id),
+ !!(ev->flags & HTT_SECURITY_IS_UNICAST),
+@@ -1482,7 +1970,7 @@ void ath10k_htt_t2h_msg_handler(struct a
+ break;
+ }
+ case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+ skb->data, skb->len);
+ ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind);
+ break;
+@@ -1491,16 +1979,55 @@ void ath10k_htt_t2h_msg_handler(struct a
+ /* FIX THIS */
+ break;
+ case HTT_T2H_MSG_TYPE_STATS_CONF:
+- trace_ath10k_htt_stats(skb->data, skb->len);
++ trace_ath10k_htt_stats(ar, skb->data, skb->len);
+ break;
+ case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
++ /* Firmware can return tx frames if it's unable to fully
++ * process them and suspects host may be able to fix it. ath10k
++ * sends all tx frames as already inspected so this shouldn't
++ * happen unless fw has a bug.
++ */
++ ath10k_warn(ar, "received an unexpected htt tx inspect event\n");
++ break;
+ case HTT_T2H_MSG_TYPE_RX_ADDBA:
++ ath10k_htt_rx_addba(ar, resp);
++ break;
+ case HTT_T2H_MSG_TYPE_RX_DELBA:
+- case HTT_T2H_MSG_TYPE_RX_FLUSH:
++ ath10k_htt_rx_delba(ar, resp);
++ break;
++ case HTT_T2H_MSG_TYPE_PKTLOG: {
++ struct ath10k_pktlog_hdr *hdr =
++ (struct ath10k_pktlog_hdr *)resp->pktlog_msg.payload;
++
++ trace_ath10k_htt_pktlog(ar, resp->pktlog_msg.payload,
++ sizeof(*hdr) +
++ __le16_to_cpu(hdr->size));
++ break;
++ }
++ case HTT_T2H_MSG_TYPE_RX_FLUSH: {
++ /* Ignore this event because mac80211 takes care of Rx
++ * aggregation reordering.
++ */
++ break;
++ }
++ case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
++ spin_lock_bh(&htt->rx_ring.lock);
++ __skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
++ spin_unlock_bh(&htt->rx_ring.lock);
++ tasklet_schedule(&htt->txrx_compl_task);
++ return;
++ }
++ case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND:
++ /* FIXME: This WMI-TLV event is overlapping with 10.2
++ * CHAN_CHANGE - both being 0xF. Neither is being used in
++ * practice so no immediate action is necessary. Nevertheless
++ * HTT may need an abstraction layer like WMI has one day.
++ */
++ break;
+ default:
+- ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n",
+- resp->hdr.msg_type);
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
++ ath10k_warn(ar, "htt event (%d) not handled\n",
++ resp->hdr.msg_type);
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+ skb->data, skb->len);
+ break;
+ };
+@@ -1512,6 +2039,7 @@ void ath10k_htt_t2h_msg_handler(struct a
+ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
+ {
+ struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
++ struct ath10k *ar = htt->ar;
+ struct htt_resp *resp;
+ struct sk_buff *skb;
+
+@@ -1528,5 +2056,10 @@ static void ath10k_htt_txrx_compl_task(u
+ ath10k_htt_rx_handler(htt, &resp->rx_ind);
+ dev_kfree_skb_any(skb);
+ }
++
++ while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) {
++ ath10k_htt_rx_in_ord_ind(ar, skb);
++ dev_kfree_skb_any(skb);
++ }
+ spin_unlock_bh(&htt->rx_ring.lock);
+ }
+--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
++++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
+@@ -56,98 +56,74 @@ exit:
+ return ret;
+ }
+
+-int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
++int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
+ {
+- int msdu_id;
++ struct ath10k *ar = htt->ar;
++ int ret;
+
+ lockdep_assert_held(&htt->tx_lock);
+
+- msdu_id = find_first_zero_bit(htt->used_msdu_ids,
+- htt->max_num_pending_tx);
+- if (msdu_id == htt->max_num_pending_tx)
+- return -ENOBUFS;
+-
+- ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
+- __set_bit(msdu_id, htt->used_msdu_ids);
+- return msdu_id;
++ ret = idr_alloc(&htt->pending_tx, skb, 0, 0x10000, GFP_ATOMIC);
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", ret);
++
++ return ret;
+ }
+
+ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
+ {
++ struct ath10k *ar = htt->ar;
++
+ lockdep_assert_held(&htt->tx_lock);
+
+- if (!test_bit(msdu_id, htt->used_msdu_ids))
+- ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id);
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
+
+- ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
+- __clear_bit(msdu_id, htt->used_msdu_ids);
++ idr_remove(&htt->pending_tx, msdu_id);
+ }
+
+-int ath10k_htt_tx_attach(struct ath10k_htt *htt)
++int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
+ {
+- spin_lock_init(&htt->tx_lock);
+- init_waitqueue_head(&htt->empty_tx_wq);
+-
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features))
+- htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
+- else
+- htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC;
++ struct ath10k *ar = htt->ar;
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
+ htt->max_num_pending_tx);
+
+- htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
+- htt->max_num_pending_tx, GFP_KERNEL);
+- if (!htt->pending_tx)
+- return -ENOMEM;
+-
+- htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
+- BITS_TO_LONGS(htt->max_num_pending_tx),
+- GFP_KERNEL);
+- if (!htt->used_msdu_ids) {
+- kfree(htt->pending_tx);
+- return -ENOMEM;
+- }
++ spin_lock_init(&htt->tx_lock);
++ idr_init(&htt->pending_tx);
+
+ htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev,
+ sizeof(struct ath10k_htt_txbuf), 4, 0);
+ if (!htt->tx_pool) {
+- kfree(htt->used_msdu_ids);
+- kfree(htt->pending_tx);
++ idr_destroy(&htt->pending_tx);
+ return -ENOMEM;
+ }
+
+ return 0;
+ }
+
+-static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
++static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx)
+ {
++ struct ath10k *ar = ctx;
++ struct ath10k_htt *htt = &ar->htt;
+ struct htt_tx_done tx_done = {0};
+- int msdu_id;
+-
+- spin_lock_bh(&htt->tx_lock);
+- for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
+- if (!test_bit(msdu_id, htt->used_msdu_ids))
+- continue;
+
+- ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
+- msdu_id);
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id);
+
+- tx_done.discard = 1;
+- tx_done.msdu_id = msdu_id;
++ tx_done.discard = 1;
++ tx_done.msdu_id = msdu_id;
+
+- ath10k_txrx_tx_unref(htt, &tx_done);
+- }
++ spin_lock_bh(&htt->tx_lock);
++ ath10k_txrx_tx_unref(htt, &tx_done);
+ spin_unlock_bh(&htt->tx_lock);
++
++ return 0;
+ }
+
+-void ath10k_htt_tx_detach(struct ath10k_htt *htt)
++void ath10k_htt_tx_free(struct ath10k_htt *htt)
+ {
+- ath10k_htt_tx_cleanup_pending(htt);
+- kfree(htt->pending_tx);
+- kfree(htt->used_msdu_ids);
++ idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
++ idr_destroy(&htt->pending_tx);
+ dma_pool_destroy(htt->tx_pool);
+- return;
+ }
+
+ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+@@ -157,6 +133,7 @@ void ath10k_htt_htc_tx_complete(struct a
+
+ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
+ {
++ struct ath10k *ar = htt->ar;
+ struct sk_buff *skb;
+ struct htt_cmd *cmd;
+ int len = 0;
+@@ -165,7 +142,7 @@ int ath10k_htt_h2t_ver_req_msg(struct at
+ len += sizeof(cmd->hdr);
+ len += sizeof(cmd->ver_req);
+
+- skb = ath10k_htc_alloc_skb(len);
++ skb = ath10k_htc_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+@@ -184,6 +161,7 @@ int ath10k_htt_h2t_ver_req_msg(struct at
+
+ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie)
+ {
++ struct ath10k *ar = htt->ar;
+ struct htt_stats_req *req;
+ struct sk_buff *skb;
+ struct htt_cmd *cmd;
+@@ -192,7 +170,7 @@ int ath10k_htt_h2t_stats_req(struct ath1
+ len += sizeof(cmd->hdr);
+ len += sizeof(cmd->stats_req);
+
+- skb = ath10k_htc_alloc_skb(len);
++ skb = ath10k_htc_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+@@ -214,7 +192,8 @@ int ath10k_htt_h2t_stats_req(struct ath1
+
+ ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
+ if (ret) {
+- ath10k_warn("failed to send htt type stats request: %d", ret);
++ ath10k_warn(ar, "failed to send htt type stats request: %d",
++ ret);
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+@@ -224,6 +203,7 @@ int ath10k_htt_h2t_stats_req(struct ath1
+
+ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
+ {
++ struct ath10k *ar = htt->ar;
+ struct sk_buff *skb;
+ struct htt_cmd *cmd;
+ struct htt_rx_ring_setup_ring *ring;
+@@ -242,7 +222,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struc
+
+ len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr)
+ + (sizeof(*ring) * num_rx_ring);
+- skb = ath10k_htc_alloc_skb(len);
++ skb = ath10k_htc_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+@@ -307,9 +287,57 @@ int ath10k_htt_send_rx_ring_cfg_ll(struc
+ return 0;
+ }
+
++int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
++ u8 max_subfrms_ampdu,
++ u8 max_subfrms_amsdu)
++{
++ struct ath10k *ar = htt->ar;
++ struct htt_aggr_conf *aggr_conf;
++ struct sk_buff *skb;
++ struct htt_cmd *cmd;
++ int len;
++ int ret;
++
++ /* Firmware defaults are: amsdu = 3 and ampdu = 64 */
++
++ if (max_subfrms_ampdu == 0 || max_subfrms_ampdu > 64)
++ return -EINVAL;
++
++ if (max_subfrms_amsdu == 0 || max_subfrms_amsdu > 31)
++ return -EINVAL;
++
++ len = sizeof(cmd->hdr);
++ len += sizeof(cmd->aggr_conf);
++
++ skb = ath10k_htc_alloc_skb(ar, len);
++ if (!skb)
++ return -ENOMEM;
++
++ skb_put(skb, len);
++ cmd = (struct htt_cmd *)skb->data;
++ cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_AGGR_CFG;
++
++ aggr_conf = &cmd->aggr_conf;
++ aggr_conf->max_num_ampdu_subframes = max_subfrms_ampdu;
++ aggr_conf->max_num_amsdu_subframes = max_subfrms_amsdu;
++
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt h2t aggr cfg msg amsdu %d ampdu %d",
++ aggr_conf->max_num_amsdu_subframes,
++ aggr_conf->max_num_ampdu_subframes);
++
++ ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
++ if (ret) {
++ dev_kfree_skb_any(skb);
++ return ret;
++ }
++
++ return 0;
++}
++
+ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+ {
+- struct device *dev = htt->ar->dev;
++ struct ath10k *ar = htt->ar;
++ struct device *dev = ar->dev;
+ struct sk_buff *txdesc = NULL;
+ struct htt_cmd *cmd;
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
+@@ -318,7 +346,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt
+ int msdu_id = -1;
+ int res;
+
+-
+ res = ath10k_htt_tx_inc_pending(htt);
+ if (res)
+ goto err;
+@@ -327,16 +354,15 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt
+ len += sizeof(cmd->mgmt_tx);
+
+ spin_lock_bh(&htt->tx_lock);
+- res = ath10k_htt_tx_alloc_msdu_id(htt);
++ res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
+ if (res < 0) {
+ spin_unlock_bh(&htt->tx_lock);
+ goto err_tx_dec;
+ }
+ msdu_id = res;
+- htt->pending_tx[msdu_id] = msdu;
+ spin_unlock_bh(&htt->tx_lock);
+
+- txdesc = ath10k_htc_alloc_skb(len);
++ txdesc = ath10k_htc_alloc_skb(ar, len);
+ if (!txdesc) {
+ res = -ENOMEM;
+ goto err_free_msdu_id;
+@@ -372,7 +398,6 @@ err_free_txdesc:
+ dev_kfree_skb_any(txdesc);
+ err_free_msdu_id:
+ spin_lock_bh(&htt->tx_lock);
+- htt->pending_tx[msdu_id] = NULL;
+ ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+ spin_unlock_bh(&htt->tx_lock);
+ err_tx_dec:
+@@ -383,7 +408,8 @@ err:
+
+ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+ {
+- struct device *dev = htt->ar->dev;
++ struct ath10k *ar = htt->ar;
++ struct device *dev = ar->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
+ struct ath10k_hif_sg_item sg_items[2];
+@@ -403,13 +429,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt
+ goto err;
+
+ spin_lock_bh(&htt->tx_lock);
+- res = ath10k_htt_tx_alloc_msdu_id(htt);
++ res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
+ if (res < 0) {
+ spin_unlock_bh(&htt->tx_lock);
+ goto err_tx_dec;
+ }
+ msdu_id = res;
+- htt->pending_tx[msdu_id] = msdu;
+ spin_unlock_bh(&htt->tx_lock);
+
+ prefetch_len = min(htt->prefetch_len, msdu->len);
+@@ -423,10 +448,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt
+
+ skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC,
+ &paddr);
+- if (!skb_cb->htt.txbuf)
++ if (!skb_cb->htt.txbuf) {
++ res = -ENOMEM;
+ goto err_free_msdu_id;
++ }
+ skb_cb->htt.txbuf_paddr = paddr;
+
++ if ((ieee80211_is_action(hdr->frame_control) ||
++ ieee80211_is_deauth(hdr->frame_control) ||
++ ieee80211_is_disassoc(hdr->frame_control)) &&
++ ieee80211_has_protected(hdr->frame_control))
++ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
++
+ skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
+ DMA_TO_DEVICE);
+ res = dma_mapping_error(dev, skb_cb->paddr);
+@@ -482,8 +515,16 @@ int ath10k_htt_tx(struct ath10k_htt *htt
+
+ flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
+ flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
+- flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
+- flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
++ if (msdu->ip_summed == CHECKSUM_PARTIAL) {
++ flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD;
++ flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD;
++ }
++
++ /* Prevent firmware from sending up tx inspection requests. There's
++ * nothing ath10k can do with frames requested for inspection so force
++ * it to simply rely a regular tx completion with discard status.
++ */
++ flags1 |= HTT_DATA_TX_DESC_FLAGS1_POSTPONED;
+
+ skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
+ skb_cb->htt.txbuf->cmd_tx.flags0 = flags0;
+@@ -491,14 +532,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt
+ skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
+ skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
+ skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+- skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
++ skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
++ skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
+
+- ath10k_dbg(ATH10K_DBG_HTT,
+- "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
++ trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
++ ath10k_dbg(ar, ATH10K_DBG_HTT,
++ "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
+ flags0, flags1, msdu->len, msdu_id, frags_paddr,
+- (u32)skb_cb->paddr, vdev_id, tid);
+- ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
++ (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
++ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
+ msdu->data, msdu->len);
++ trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
++ trace_ath10k_tx_payload(ar, msdu->data, msdu->len);
+
+ sg_items[0].transfer_id = 0;
+ sg_items[0].transfer_context = NULL;
+@@ -531,7 +576,6 @@ err_free_txbuf:
+ skb_cb->htt.txbuf_paddr);
+ err_free_msdu_id:
+ spin_lock_bh(&htt->tx_lock);
+- htt->pending_tx[msdu_id] = NULL;
+ ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+ spin_unlock_bh(&htt->tx_lock);
+ err_tx_dec:
+--- a/drivers/net/wireless/ath/ath10k/hw.h
++++ b/drivers/net/wireless/ath/ath10k/hw.h
+@@ -20,24 +20,73 @@
+
+ #include "targaddrs.h"
+
++#define ATH10K_FW_DIR "ath10k"
++
+ /* QCA988X 1.0 definitions (unsupported) */
+ #define QCA988X_HW_1_0_CHIP_ID_REV 0x0
+
+ /* QCA988X 2.0 definitions */
+ #define QCA988X_HW_2_0_VERSION 0x4100016c
+ #define QCA988X_HW_2_0_CHIP_ID_REV 0x2
+-#define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0"
++#define QCA988X_HW_2_0_FW_DIR ATH10K_FW_DIR "/QCA988X/hw2.0"
+ #define QCA988X_HW_2_0_FW_FILE "firmware.bin"
+-#define QCA988X_HW_2_0_FW_2_FILE "firmware-2.bin"
+ #define QCA988X_HW_2_0_OTP_FILE "otp.bin"
+ #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
+ #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
+
++/* QCA6174 target BMI version signatures */
++#define QCA6174_HW_1_0_VERSION 0x05000000
++#define QCA6174_HW_1_1_VERSION 0x05000001
++#define QCA6174_HW_1_3_VERSION 0x05000003
++#define QCA6174_HW_2_1_VERSION 0x05010000
++#define QCA6174_HW_3_0_VERSION 0x05020000
++#define QCA6174_HW_3_2_VERSION 0x05030000
++
++enum qca6174_pci_rev {
++ QCA6174_PCI_REV_1_1 = 0x11,
++ QCA6174_PCI_REV_1_3 = 0x13,
++ QCA6174_PCI_REV_2_0 = 0x20,
++ QCA6174_PCI_REV_3_0 = 0x30,
++};
++
++enum qca6174_chip_id_rev {
++ QCA6174_HW_1_0_CHIP_ID_REV = 0,
++ QCA6174_HW_1_1_CHIP_ID_REV = 1,
++ QCA6174_HW_1_3_CHIP_ID_REV = 2,
++ QCA6174_HW_2_1_CHIP_ID_REV = 4,
++ QCA6174_HW_2_2_CHIP_ID_REV = 5,
++ QCA6174_HW_3_0_CHIP_ID_REV = 8,
++ QCA6174_HW_3_1_CHIP_ID_REV = 9,
++ QCA6174_HW_3_2_CHIP_ID_REV = 10,
++};
++
++#define QCA6174_HW_2_1_FW_DIR "ath10k/QCA6174/hw2.1"
++#define QCA6174_HW_2_1_FW_FILE "firmware.bin"
++#define QCA6174_HW_2_1_OTP_FILE "otp.bin"
++#define QCA6174_HW_2_1_BOARD_DATA_FILE "board.bin"
++#define QCA6174_HW_2_1_PATCH_LOAD_ADDR 0x1234
++
++#define QCA6174_HW_3_0_FW_DIR "ath10k/QCA6174/hw3.0"
++#define QCA6174_HW_3_0_FW_FILE "firmware.bin"
++#define QCA6174_HW_3_0_OTP_FILE "otp.bin"
++#define QCA6174_HW_3_0_BOARD_DATA_FILE "board.bin"
++#define QCA6174_HW_3_0_PATCH_LOAD_ADDR 0x1234
++
+ #define ATH10K_FW_API2_FILE "firmware-2.bin"
++#define ATH10K_FW_API3_FILE "firmware-3.bin"
++
++/* added support for ATH10K_FW_IE_WMI_OP_VERSION */
++#define ATH10K_FW_API4_FILE "firmware-4.bin"
++
++#define ATH10K_FW_UTF_FILE "utf.bin"
+
+ /* includes also the null byte */
+ #define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K"
+
++#define REG_DUMP_COUNT_QCA988X 60
++
++#define QCA988X_CAL_DATA_LEN 2116
++
+ struct ath10k_fw_ie {
+ __le32 id;
+ __le32 len;
+@@ -50,8 +99,57 @@ enum ath10k_fw_ie_type {
+ ATH10K_FW_IE_FEATURES = 2,
+ ATH10K_FW_IE_FW_IMAGE = 3,
+ ATH10K_FW_IE_OTP_IMAGE = 4,
++
++ /* WMI "operations" interface version, 32 bit value. Supported from
++ * FW API 4 and above.
++ */
++ ATH10K_FW_IE_WMI_OP_VERSION = 5,
++};
++
++enum ath10k_fw_wmi_op_version {
++ ATH10K_FW_WMI_OP_VERSION_UNSET = 0,
++
++ ATH10K_FW_WMI_OP_VERSION_MAIN = 1,
++ ATH10K_FW_WMI_OP_VERSION_10_1 = 2,
++ ATH10K_FW_WMI_OP_VERSION_10_2 = 3,
++ ATH10K_FW_WMI_OP_VERSION_TLV = 4,
++ ATH10K_FW_WMI_OP_VERSION_10_2_4 = 5,
++
++ /* keep last */
++ ATH10K_FW_WMI_OP_VERSION_MAX,
++};
++
++enum ath10k_hw_rev {
++ ATH10K_HW_QCA988X,
++ ATH10K_HW_QCA6174,
++};
++
++struct ath10k_hw_regs {
++ u32 rtc_state_cold_reset_mask;
++ u32 rtc_soc_base_address;
++ u32 rtc_wmac_base_address;
++ u32 soc_core_base_address;
++ u32 ce_wrapper_base_address;
++ u32 ce0_base_address;
++ u32 ce1_base_address;
++ u32 ce2_base_address;
++ u32 ce3_base_address;
++ u32 ce4_base_address;
++ u32 ce5_base_address;
++ u32 ce6_base_address;
++ u32 ce7_base_address;
++ u32 soc_reset_control_si0_rst_mask;
++ u32 soc_reset_control_ce_rst_mask;
++ u32 soc_chip_id_address;
++ u32 scratch_3_address;
+ };
+
++extern const struct ath10k_hw_regs qca988x_regs;
++extern const struct ath10k_hw_regs qca6174_regs;
++
++#define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X)
++#define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174)
++
+ /* Known pecularities:
+ * - current FW doesn't support raw rx mode (last tested v599)
+ * - current FW dumps upon raw tx mode (last tested v599)
+@@ -73,6 +171,15 @@ enum ath10k_mcast2ucast_mode {
+ ATH10K_MCAST2UCAST_ENABLED = 1,
+ };
+
++struct ath10k_pktlog_hdr {
++ __le16 flags;
++ __le16 missed_cnt;
++ __le16 log_type;
++ __le16 size;
++ __le32 timestamp;
++ u8 payload[0];
++} __packed;
++
+ /* Target specific defines for MAIN firmware */
+ #define TARGET_NUM_VDEVS 8
+ #define TARGET_NUM_PEER_AST 2
+@@ -80,11 +187,13 @@ enum ath10k_mcast2ucast_mode {
+ #define TARGET_DMA_BURST_SIZE 0
+ #define TARGET_MAC_AGGR_DELIM 0
+ #define TARGET_AST_SKID_LIMIT 16
+-#define TARGET_NUM_PEERS 16
++#define TARGET_NUM_STATIONS 16
++#define TARGET_NUM_PEERS ((TARGET_NUM_STATIONS) + \
++ (TARGET_NUM_VDEVS))
+ #define TARGET_NUM_OFFLOAD_PEERS 0
+ #define TARGET_NUM_OFFLOAD_REORDER_BUFS 0
+ #define TARGET_NUM_PEER_KEYS 2
+-#define TARGET_NUM_TIDS (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
++#define TARGET_NUM_TIDS ((TARGET_NUM_PEERS) * 2)
+ #define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
+ #define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
+ #define TARGET_RX_TIMEOUT_LO_PRI 100
+@@ -115,12 +224,15 @@ enum ath10k_mcast2ucast_mode {
+ #define TARGET_10X_DMA_BURST_SIZE 0
+ #define TARGET_10X_MAC_AGGR_DELIM 0
+ #define TARGET_10X_AST_SKID_LIMIT 16
+-#define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS))
+-#define TARGET_10X_NUM_PEERS_MAX 128
++#define TARGET_10X_NUM_STATIONS 128
++#define TARGET_10X_NUM_PEERS ((TARGET_10X_NUM_STATIONS) + \
++ (TARGET_10X_NUM_VDEVS))
+ #define TARGET_10X_NUM_OFFLOAD_PEERS 0
+ #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0
+ #define TARGET_10X_NUM_PEER_KEYS 2
+-#define TARGET_10X_NUM_TIDS 256
++#define TARGET_10X_NUM_TIDS_MAX 256
++#define TARGET_10X_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \
++ (TARGET_10X_NUM_PEERS) * 2)
+ #define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
+ #define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
+ #define TARGET_10X_RX_TIMEOUT_LO_PRI 100
+@@ -140,6 +252,18 @@ enum ath10k_mcast2ucast_mode {
+ #define TARGET_10X_NUM_MSDU_DESC (1024 + 400)
+ #define TARGET_10X_MAX_FRAG_ENTRIES 0
+
++/* 10.2 parameters */
++#define TARGET_10_2_DMA_BURST_SIZE 1
++
++/* Target specific defines for WMI-TLV firmware */
++#define TARGET_TLV_NUM_VDEVS 3
++#define TARGET_TLV_NUM_STATIONS 32
++#define TARGET_TLV_NUM_PEERS ((TARGET_TLV_NUM_STATIONS) + \
++ (TARGET_TLV_NUM_VDEVS) + \
++ 2)
++#define TARGET_TLV_NUM_TIDS ((TARGET_TLV_NUM_PEERS) * 2)
++#define TARGET_TLV_NUM_MSDU_DESC (1024 + 32)
++
+ /* Number of Copy Engines supported */
+ #define CE_COUNT 8
+
+@@ -170,7 +294,7 @@ enum ath10k_mcast2ucast_mode {
+ /* as of IP3.7.1 */
+ #define RTC_STATE_V_ON 3
+
+-#define RTC_STATE_COLD_RESET_MASK 0x00000400
++#define RTC_STATE_COLD_RESET_MASK ar->regs->rtc_state_cold_reset_mask
+ #define RTC_STATE_V_LSB 0
+ #define RTC_STATE_V_MASK 0x00000007
+ #define RTC_STATE_ADDRESS 0x0000
+@@ -179,12 +303,12 @@ enum ath10k_mcast2ucast_mode {
+ #define PCIE_SOC_WAKE_RESET 0x00000000
+ #define SOC_GLOBAL_RESET_ADDRESS 0x0008
+
+-#define RTC_SOC_BASE_ADDRESS 0x00004000
+-#define RTC_WMAC_BASE_ADDRESS 0x00005000
++#define RTC_SOC_BASE_ADDRESS ar->regs->rtc_soc_base_address
++#define RTC_WMAC_BASE_ADDRESS ar->regs->rtc_wmac_base_address
+ #define MAC_COEX_BASE_ADDRESS 0x00006000
+ #define BT_COEX_BASE_ADDRESS 0x00007000
+ #define SOC_PCIE_BASE_ADDRESS 0x00008000
+-#define SOC_CORE_BASE_ADDRESS 0x00009000
++#define SOC_CORE_BASE_ADDRESS ar->regs->soc_core_base_address
+ #define WLAN_UART_BASE_ADDRESS 0x0000c000
+ #define WLAN_SI_BASE_ADDRESS 0x00010000
+ #define WLAN_GPIO_BASE_ADDRESS 0x00014000
+@@ -193,23 +317,23 @@ enum ath10k_mcast2ucast_mode {
+ #define EFUSE_BASE_ADDRESS 0x00030000
+ #define FPGA_REG_BASE_ADDRESS 0x00039000
+ #define WLAN_UART2_BASE_ADDRESS 0x00054c00
+-#define CE_WRAPPER_BASE_ADDRESS 0x00057000
+-#define CE0_BASE_ADDRESS 0x00057400
+-#define CE1_BASE_ADDRESS 0x00057800
+-#define CE2_BASE_ADDRESS 0x00057c00
+-#define CE3_BASE_ADDRESS 0x00058000
+-#define CE4_BASE_ADDRESS 0x00058400
+-#define CE5_BASE_ADDRESS 0x00058800
+-#define CE6_BASE_ADDRESS 0x00058c00
+-#define CE7_BASE_ADDRESS 0x00059000
++#define CE_WRAPPER_BASE_ADDRESS ar->regs->ce_wrapper_base_address
++#define CE0_BASE_ADDRESS ar->regs->ce0_base_address
++#define CE1_BASE_ADDRESS ar->regs->ce1_base_address
++#define CE2_BASE_ADDRESS ar->regs->ce2_base_address
++#define CE3_BASE_ADDRESS ar->regs->ce3_base_address
++#define CE4_BASE_ADDRESS ar->regs->ce4_base_address
++#define CE5_BASE_ADDRESS ar->regs->ce5_base_address
++#define CE6_BASE_ADDRESS ar->regs->ce6_base_address
++#define CE7_BASE_ADDRESS ar->regs->ce7_base_address
+ #define DBI_BASE_ADDRESS 0x00060000
+ #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000
+ #define PCIE_LOCAL_BASE_ADDRESS 0x00080000
+
+ #define SOC_RESET_CONTROL_ADDRESS 0x00000000
+ #define SOC_RESET_CONTROL_OFFSET 0x00000000
+-#define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001
+-#define SOC_RESET_CONTROL_CE_RST_MASK 0x00040000
++#define SOC_RESET_CONTROL_SI0_RST_MASK ar->regs->soc_reset_control_si0_rst_mask
++#define SOC_RESET_CONTROL_CE_RST_MASK ar->regs->soc_reset_control_ce_rst_mask
+ #define SOC_RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040
+ #define SOC_CPU_CLOCK_OFFSET 0x00000020
+ #define SOC_CPU_CLOCK_STANDARD_LSB 0
+@@ -223,7 +347,7 @@ enum ath10k_mcast2ucast_mode {
+ #define SOC_LF_TIMER_CONTROL0_ADDRESS 0x00000050
+ #define SOC_LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004
+
+-#define SOC_CHIP_ID_ADDRESS 0x000000ec
++#define SOC_CHIP_ID_ADDRESS ar->regs->soc_chip_id_address
+ #define SOC_CHIP_ID_REV_LSB 8
+ #define SOC_CHIP_ID_REV_MASK 0x00000f00
+
+@@ -274,11 +398,12 @@ enum ath10k_mcast2ucast_mode {
+ #define SI_RX_DATA1_OFFSET 0x00000014
+
+ #define CORE_CTRL_CPU_INTR_MASK 0x00002000
++#define CORE_CTRL_PCIE_REG_31_MASK 0x00000800
+ #define CORE_CTRL_ADDRESS 0x0000
+ #define PCIE_INTR_ENABLE_ADDRESS 0x0008
+ #define PCIE_INTR_CAUSE_ADDRESS 0x000c
+ #define PCIE_INTR_CLR_ADDRESS 0x0014
+-#define SCRATCH_3_ADDRESS 0x0030
++#define SCRATCH_3_ADDRESS ar->regs->scratch_3_address
+ #define CPU_INTR_ADDRESS 0x0010
+
+ /* Firmware indications to the Host via SCRATCH_3 register. */
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -26,6 +26,9 @@
+ #include "wmi.h"
+ #include "htt.h"
+ #include "txrx.h"
++#include "testmode.h"
++#include "wmi.h"
++#include "wmi-ops.h"
+
+ /**********/
+ /* Crypto */
+@@ -34,8 +37,9 @@
+ static int ath10k_send_key(struct ath10k_vif *arvif,
+ struct ieee80211_key_conf *key,
+ enum set_key_cmd cmd,
+- const u8 *macaddr)
++ const u8 *macaddr, bool def_idx)
+ {
++ struct ath10k *ar = arvif->ar;
+ struct wmi_vdev_install_key_arg arg = {
+ .vdev_id = arvif->vdev_id,
+ .key_idx = key->keyidx,
+@@ -54,7 +58,7 @@ static int ath10k_send_key(struct ath10k
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ arg.key_cipher = WMI_CIPHER_AES_CCM;
+- key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
++ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ arg.key_cipher = WMI_CIPHER_TKIP;
+@@ -68,9 +72,12 @@ static int ath10k_send_key(struct ath10k
+ * Otherwise pairwise key must be set */
+ if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
+ arg.key_flags = WMI_KEY_PAIRWISE;
++
++ if (def_idx)
++ arg.key_flags |= WMI_KEY_TX_USAGE;
+ break;
+ default:
+- ath10k_warn("cipher %d is not supported\n", key->cipher);
++ ath10k_warn(ar, "cipher %d is not supported\n", key->cipher);
+ return -EOPNOTSUPP;
+ }
+
+@@ -85,7 +92,7 @@ static int ath10k_send_key(struct ath10k
+ static int ath10k_install_key(struct ath10k_vif *arvif,
+ struct ieee80211_key_conf *key,
+ enum set_key_cmd cmd,
+- const u8 *macaddr)
++ const u8 *macaddr, bool def_idx)
+ {
+ struct ath10k *ar = arvif->ar;
+ int ret;
+@@ -94,7 +101,7 @@ static int ath10k_install_key(struct ath
+
+ reinit_completion(&ar->install_key_done);
+
+- ret = ath10k_send_key(arvif, key, cmd, macaddr);
++ ret = ath10k_send_key(arvif, key, cmd, macaddr, def_idx);
+ if (ret)
+ return ret;
+
+@@ -112,6 +119,7 @@ static int ath10k_install_peer_wep_keys(
+ struct ath10k_peer *peer;
+ int ret;
+ int i;
++ bool def_idx;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+@@ -125,13 +133,20 @@ static int ath10k_install_peer_wep_keys(
+ for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
+ if (arvif->wep_keys[i] == NULL)
+ continue;
++ /* set TX_USAGE flag for default key id */
++ if (arvif->def_wep_key_idx == i)
++ def_idx = true;
++ else
++ def_idx = false;
+
+ ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
+- addr);
++ addr, def_idx);
+ if (ret)
+ return ret;
+
++ spin_lock_bh(&ar->data_lock);
+ peer->keys[i] = arvif->wep_keys[i];
++ spin_unlock_bh(&ar->data_lock);
+ }
+
+ return 0;
+@@ -159,21 +174,49 @@ static int ath10k_clear_peer_keys(struct
+ if (peer->keys[i] == NULL)
+ continue;
+
++ /* key flags are not required to delete the key */
+ ret = ath10k_install_key(arvif, peer->keys[i],
+- DISABLE_KEY, addr);
++ DISABLE_KEY, addr, false);
+ if (ret && first_errno == 0)
+ first_errno = ret;
+
+ if (ret)
+- ath10k_warn("failed to remove peer wep key %d: %d\n",
++ ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
+ i, ret);
+
++ spin_lock_bh(&ar->data_lock);
+ peer->keys[i] = NULL;
++ spin_unlock_bh(&ar->data_lock);
+ }
+
+ return first_errno;
+ }
+
++bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
++ u8 keyidx)
++{
++ struct ath10k_peer *peer;
++ int i;
++
++ lockdep_assert_held(&ar->data_lock);
++
++ /* We don't know which vdev this peer belongs to,
++ * since WMI doesn't give us that information.
++ *
++ * FIXME: multi-bss needs to be handled.
++ */
++ peer = ath10k_peer_find(ar, 0, addr);
++ if (!peer)
++ return false;
++
++ for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
++ if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
++ return true;
++ }
++
++ return false;
++}
++
+ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
+ struct ieee80211_key_conf *key)
+ {
+@@ -194,7 +237,7 @@ static int ath10k_clear_vdev_key(struct
+ list_for_each_entry(peer, &ar->peers, list) {
+ for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+ if (peer->keys[i] == key) {
+- memcpy(addr, peer->addr, ETH_ALEN);
++ ether_addr_copy(addr, peer->addr);
+ peer->keys[i] = NULL;
+ break;
+ }
+@@ -207,20 +250,19 @@ static int ath10k_clear_vdev_key(struct
+
+ if (i == ARRAY_SIZE(peer->keys))
+ break;
+-
+- ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
++ /* key flags are not required to delete the key */
++ ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, false);
+ if (ret && first_errno == 0)
+ first_errno = ret;
+
+ if (ret)
+- ath10k_warn("failed to remove key for %pM: %d\n",
++ ath10k_warn(ar, "failed to remove key for %pM: %d\n",
+ addr, ret);
+ }
+
+ return first_errno;
+ }
+
+-
+ /*********************/
+ /* General utilities */
+ /*********************/
+@@ -234,7 +276,10 @@ chan_to_phymode(const struct cfg80211_ch
+ case IEEE80211_BAND_2GHZ:
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+- phymode = MODE_11G;
++ if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM)
++ phymode = MODE_11B;
++ else
++ phymode = MODE_11G;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ phymode = MODE_11NG_HT20;
+@@ -322,22 +367,24 @@ static int ath10k_peer_create(struct ath
+
+ lockdep_assert_held(&ar->conf_mutex);
+
++ if (ar->num_peers >= ar->max_num_peers)
++ return -ENOBUFS;
++
+ ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
+ if (ret) {
+- ath10k_warn("failed to create wmi peer %pM on vdev %i: %i\n",
++ ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
+ addr, vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
+ if (ret) {
+- ath10k_warn("failed to wait for created wmi peer %pM on vdev %i: %i\n",
++ ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n",
+ addr, vdev_id, ret);
+ return ret;
+ }
+- spin_lock_bh(&ar->data_lock);
++
+ ar->num_peers++;
+- spin_unlock_bh(&ar->data_lock);
+
+ return 0;
+ }
+@@ -352,7 +399,7 @@ static int ath10k_mac_set_kickout(struct
+ ret = ath10k_wmi_pdev_set_param(ar, param,
+ ATH10K_KICKOUT_THRESHOLD);
+ if (ret) {
+- ath10k_warn("failed to set kickout threshold on vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set kickout threshold on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -361,7 +408,7 @@ static int ath10k_mac_set_kickout(struct
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+ ATH10K_KEEPALIVE_MIN_IDLE);
+ if (ret) {
+- ath10k_warn("failed to set keepalive minimum idle time on vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set keepalive minimum idle time on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -370,7 +417,7 @@ static int ath10k_mac_set_kickout(struct
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+ ATH10K_KEEPALIVE_MAX_IDLE);
+ if (ret) {
+- ath10k_warn("failed to set keepalive maximum idle time on vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set keepalive maximum idle time on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -379,7 +426,7 @@ static int ath10k_mac_set_kickout(struct
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+ ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
+ if (ret) {
+- ath10k_warn("failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -387,15 +434,11 @@ static int ath10k_mac_set_kickout(struct
+ return 0;
+ }
+
+-static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
++static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+ {
+ struct ath10k *ar = arvif->ar;
+ u32 vdev_param;
+
+- if (value != 0xFFFFFFFF)
+- value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
+- ATH10K_RTS_MAX);
+-
+ vdev_param = ar->wmi.vdev_param->rts_threshold;
+ return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
+ }
+@@ -428,9 +471,7 @@ static int ath10k_peer_delete(struct ath
+ if (ret)
+ return ret;
+
+- spin_lock_bh(&ar->data_lock);
+ ar->num_peers--;
+- spin_unlock_bh(&ar->data_lock);
+
+ return 0;
+ }
+@@ -446,7 +487,7 @@ static void ath10k_peer_cleanup(struct a
+ if (peer->vdev_id != vdev_id)
+ continue;
+
+- ath10k_warn("removing stale peer %pM from vdev_id %d\n",
++ ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
+ peer->addr, vdev_id);
+
+ list_del(&peer->list);
+@@ -467,20 +508,63 @@ static void ath10k_peer_cleanup_all(stru
+ list_del(&peer->list);
+ kfree(peer);
+ }
+- ar->num_peers = 0;
+ spin_unlock_bh(&ar->data_lock);
++
++ ar->num_peers = 0;
++ ar->num_stations = 0;
+ }
+
+ /************************/
+ /* Interface management */
+ /************************/
+
++void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++
++ lockdep_assert_held(&ar->data_lock);
++
++ if (!arvif->beacon)
++ return;
++
++ if (!arvif->beacon_buf)
++ dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
++ arvif->beacon->len, DMA_TO_DEVICE);
++
++ if (WARN_ON(arvif->beacon_state != ATH10K_BEACON_SCHEDULED &&
++ arvif->beacon_state != ATH10K_BEACON_SENT))
++ return;
++
++ dev_kfree_skb_any(arvif->beacon);
++
++ arvif->beacon = NULL;
++ arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
++}
++
++static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++
++ lockdep_assert_held(&ar->data_lock);
++
++ ath10k_mac_vif_beacon_free(arvif);
++
++ if (arvif->beacon_buf) {
++ dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
++ arvif->beacon_buf, arvif->beacon_paddr);
++ arvif->beacon_buf = NULL;
++ }
++}
++
+ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+ {
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
++ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
++ return -ESHUTDOWN;
++
+ ret = wait_for_completion_timeout(&ar->vdev_setup_done,
+ ATH10K_VDEV_SETUP_TIMEOUT_HZ);
+ if (ret == 0)
+@@ -489,19 +573,6 @@ static inline int ath10k_vdev_setup_sync
+ return 0;
+ }
+
+-static bool ath10k_monitor_is_enabled(struct ath10k *ar)
+-{
+- lockdep_assert_held(&ar->conf_mutex);
+-
+- ath10k_dbg(ATH10K_DBG_MAC,
+- "mac monitor refs: promisc %d monitor %d cac %d\n",
+- ar->promisc, ar->monitor,
+- test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags));
+-
+- return ar->promisc || ar->monitor ||
+- test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+-}
+-
+ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
+ {
+ struct cfg80211_chan_def *chandef = &ar->chandef;
+@@ -526,37 +597,39 @@ static int ath10k_monitor_vdev_start(str
+ arg.channel.max_reg_power = channel->max_reg_power * 2;
+ arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
+
++ reinit_completion(&ar->vdev_setup_done);
++
+ ret = ath10k_wmi_vdev_start(ar, &arg);
+ if (ret) {
+- ath10k_warn("failed to request monitor vdev %i start: %d\n",
++ ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n",
+ vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_vdev_setup_sync(ar);
+ if (ret) {
+- ath10k_warn("failed to synchronize setup for monitor vdev %i: %d\n",
++ ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n",
+ vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
+ if (ret) {
+- ath10k_warn("failed to put up monitor vdev %i: %d\n",
++ ath10k_warn(ar, "failed to put up monitor vdev %i: %d\n",
+ vdev_id, ret);
+ goto vdev_stop;
+ }
+
+ ar->monitor_vdev_id = vdev_id;
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
+ ar->monitor_vdev_id);
+ return 0;
+
+ vdev_stop:
+ ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+ if (ret)
+- ath10k_warn("failed to stop monitor vdev %i after start failure: %d\n",
++ ath10k_warn(ar, "failed to stop monitor vdev %i after start failure: %d\n",
+ ar->monitor_vdev_id, ret);
+
+ return ret;
+@@ -570,20 +643,22 @@ static int ath10k_monitor_vdev_stop(stru
+
+ ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
+ if (ret)
+- ath10k_warn("failed to put down monitor vdev %i: %d\n",
++ ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n",
+ ar->monitor_vdev_id, ret);
+
++ reinit_completion(&ar->vdev_setup_done);
++
+ ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+ if (ret)
+- ath10k_warn("failed to to request monitor vdev %i stop: %d\n",
++ ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n",
+ ar->monitor_vdev_id, ret);
+
+ ret = ath10k_vdev_setup_sync(ar);
+ if (ret)
+- ath10k_warn("failed to synchronise monitor vdev %i: %d\n",
++ ath10k_warn(ar, "failed to synchronize monitor vdev %i stop: %d\n",
+ ar->monitor_vdev_id, ret);
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
+ ar->monitor_vdev_id);
+ return ret;
+ }
+@@ -594,35 +669,29 @@ static int ath10k_monitor_vdev_create(st
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- bit = ffs(ar->free_vdev_map);
+- if (bit == 0) {
+- ath10k_warn("failed to find free vdev id for monitor vdev\n");
++ if (ar->free_vdev_map == 0) {
++ ath10k_warn(ar, "failed to find free vdev id for monitor vdev\n");
+ return -ENOMEM;
+ }
+
+- ar->monitor_vdev_id = bit - 1;
+- ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
++ bit = __ffs64(ar->free_vdev_map);
++
++ ar->monitor_vdev_id = bit;
+
+ ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
+ WMI_VDEV_TYPE_MONITOR,
+ 0, ar->mac_addr);
+ if (ret) {
+- ath10k_warn("failed to request monitor vdev %i creation: %d\n",
++ ath10k_warn(ar, "failed to request monitor vdev %i creation: %d\n",
+ ar->monitor_vdev_id, ret);
+- goto vdev_fail;
++ return ret;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
++ ar->free_vdev_map &= ~(1LL << ar->monitor_vdev_id);
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
+ ar->monitor_vdev_id);
+
+ return 0;
+-
+-vdev_fail:
+- /*
+- * Restore the ID to the global map.
+- */
+- ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+- return ret;
+ }
+
+ static int ath10k_monitor_vdev_delete(struct ath10k *ar)
+@@ -633,14 +702,14 @@ static int ath10k_monitor_vdev_delete(st
+
+ ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
+ if (ret) {
+- ath10k_warn("failed to request wmi monitor vdev %i removal: %d\n",
++ ath10k_warn(ar, "failed to request wmi monitor vdev %i removal: %d\n",
+ ar->monitor_vdev_id, ret);
+ return ret;
+ }
+
+- ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
++ ar->free_vdev_map |= 1LL << ar->monitor_vdev_id;
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
+ ar->monitor_vdev_id);
+ return ret;
+ }
+@@ -651,63 +720,70 @@ static int ath10k_monitor_start(struct a
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- if (!ath10k_monitor_is_enabled(ar)) {
+- ath10k_warn("trying to start monitor with no references\n");
+- return 0;
+- }
+-
+- if (ar->monitor_started) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor already started\n");
+- return 0;
+- }
+-
+ ret = ath10k_monitor_vdev_create(ar);
+ if (ret) {
+- ath10k_warn("failed to create monitor vdev: %d\n", ret);
++ ath10k_warn(ar, "failed to create monitor vdev: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
+ if (ret) {
+- ath10k_warn("failed to start monitor vdev: %d\n", ret);
++ ath10k_warn(ar, "failed to start monitor vdev: %d\n", ret);
+ ath10k_monitor_vdev_delete(ar);
+ return ret;
+ }
+
+ ar->monitor_started = true;
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor started\n");
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor started\n");
+
+ return 0;
+ }
+
+-static void ath10k_monitor_stop(struct ath10k *ar)
++static int ath10k_monitor_stop(struct ath10k *ar)
+ {
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- if (ath10k_monitor_is_enabled(ar)) {
+- ath10k_dbg(ATH10K_DBG_MAC,
+- "mac monitor will be stopped later\n");
+- return;
++ ret = ath10k_monitor_vdev_stop(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to stop monitor vdev: %d\n", ret);
++ return ret;
+ }
+
+- if (!ar->monitor_started) {
+- ath10k_dbg(ATH10K_DBG_MAC,
+- "mac monitor probably failed to start earlier\n");
+- return;
++ ret = ath10k_monitor_vdev_delete(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to delete monitor vdev: %d\n", ret);
++ return ret;
+ }
+
+- ret = ath10k_monitor_vdev_stop(ar);
+- if (ret)
+- ath10k_warn("failed to stop monitor vdev: %d\n", ret);
++ ar->monitor_started = false;
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor stopped\n");
+
+- ret = ath10k_monitor_vdev_delete(ar);
+- if (ret)
+- ath10k_warn("failed to delete monitor vdev: %d\n", ret);
++ return 0;
++}
+
+- ar->monitor_started = false;
+- ath10k_dbg(ATH10K_DBG_MAC, "mac monitor stopped\n");
++static int ath10k_monitor_recalc(struct ath10k *ar)
++{
++ bool should_start;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ should_start = ar->monitor ||
++ ar->filter_flags & FIF_PROMISC_IN_BSS ||
++ test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
++ "mac monitor recalc started? %d should? %d\n",
++ ar->monitor_started, should_start);
++
++ if (should_start == ar->monitor_started)
++ return 0;
++
++ if (should_start)
++ return ath10k_monitor_start(ar);
++
++ return ath10k_monitor_stop(ar);
+ }
+
+ static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif)
+@@ -738,14 +814,14 @@ static int ath10k_start_cac(struct ath10
+
+ set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+- ret = ath10k_monitor_start(ar);
++ ret = ath10k_monitor_recalc(ar);
+ if (ret) {
+- ath10k_warn("failed to start monitor (cac): %d\n", ret);
++ ath10k_warn(ar, "failed to start monitor (cac): %d\n", ret);
+ clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+ return ret;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
+ ar->monitor_vdev_id);
+
+ return 0;
+@@ -762,7 +838,7 @@ static int ath10k_stop_cac(struct ath10k
+ clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+ ath10k_monitor_stop(ar);
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac finished\n");
+
+ return 0;
+ }
+@@ -788,12 +864,12 @@ static void ath10k_recalc_radar_detectio
+ * radiation is not allowed, make this channel DFS_UNAVAILABLE
+ * by indicating that radar was detected.
+ */
+- ath10k_warn("failed to start CAC: %d\n", ret);
++ ath10k_warn(ar, "failed to start CAC: %d\n", ret);
+ ieee80211_radar_detected(ar->hw);
+ }
+ }
+
+-static int ath10k_vdev_start(struct ath10k_vif *arvif)
++static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, bool restart)
+ {
+ struct ath10k *ar = arvif->ar;
+ struct cfg80211_chan_def *chandef = &ar->chandef;
+@@ -830,22 +906,27 @@ static int ath10k_vdev_start(struct ath1
+ arg.ssid_len = arvif->vif->bss_conf.ssid_len;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, 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 (restart)
++ ret = ath10k_wmi_vdev_restart(ar, &arg);
++ else
++ ret = ath10k_wmi_vdev_start(ar, &arg);
++
+ if (ret) {
+- ath10k_warn("failed to start WMI vdev %i: %d\n",
++ ath10k_warn(ar, "failed to start WMI vdev %i: %d\n",
+ arg.vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_vdev_setup_sync(ar);
+ if (ret) {
+- ath10k_warn("failed to synchronise setup for vdev %i: %d\n",
+- arg.vdev_id, ret);
++ ath10k_warn(ar,
++ "failed to synchronize setup for vdev %i restart %d: %d\n",
++ arg.vdev_id, restart, ret);
+ return ret;
+ }
+
+@@ -855,6 +936,16 @@ static int ath10k_vdev_start(struct ath1
+ return ret;
+ }
+
++static int ath10k_vdev_start(struct ath10k_vif *arvif)
++{
++ return ath10k_vdev_start_restart(arvif, false);
++}
++
++static int ath10k_vdev_restart(struct ath10k_vif *arvif)
++{
++ return ath10k_vdev_start_restart(arvif, true);
++}
++
+ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+ {
+ struct ath10k *ar = arvif->ar;
+@@ -866,14 +957,14 @@ static int ath10k_vdev_stop(struct ath10
+
+ ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+ if (ret) {
+- ath10k_warn("failed to stop WMI vdev %i: %d\n",
++ ath10k_warn(ar, "failed to stop WMI vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_vdev_setup_sync(ar);
+ if (ret) {
+- ath10k_warn("failed to syncronise setup for vdev %i: %d\n",
++ ath10k_warn(ar, "failed to synchronize setup for vdev %i stop: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -888,9 +979,147 @@ static int ath10k_vdev_stop(struct ath10
+ return ret;
+ }
+
++static int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif,
++ struct sk_buff *bcn)
++{
++ struct ath10k *ar = arvif->ar;
++ struct ieee80211_mgmt *mgmt;
++ const u8 *p2p_ie;
++ int ret;
++
++ if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
++ return 0;
++
++ if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
++ return 0;
++
++ mgmt = (void *)bcn->data;
++ p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
++ mgmt->u.beacon.variable,
++ bcn->len - (mgmt->u.beacon.variable -
++ bcn->data));
++ if (!p2p_ie)
++ return -ENOENT;
++
++ ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie);
++ if (ret) {
++ ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ath10k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui,
++ u8 oui_type, size_t ie_offset)
++{
++ size_t len;
++ const u8 *next;
++ const u8 *end;
++ u8 *ie;
++
++ if (WARN_ON(skb->len < ie_offset))
++ return -EINVAL;
++
++ ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
++ skb->data + ie_offset,
++ skb->len - ie_offset);
++ if (!ie)
++ return -ENOENT;
++
++ len = ie[1] + 2;
++ end = skb->data + skb->len;
++ next = ie + len;
++
++ if (WARN_ON(next > end))
++ return -EINVAL;
++
++ memmove(ie, next, end - next);
++ skb_trim(skb, skb->len - len);
++
++ return 0;
++}
++
++static int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++ struct ieee80211_hw *hw = ar->hw;
++ struct ieee80211_vif *vif = arvif->vif;
++ struct ieee80211_mutable_offsets offs = {};
++ struct sk_buff *bcn;
++ int ret;
++
++ if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
++ return 0;
++
++ bcn = ieee80211_beacon_get_template(hw, vif, &offs);
++ if (!bcn) {
++ ath10k_warn(ar, "failed to get beacon template from mac80211\n");
++ return -EPERM;
++ }
++
++ ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn);
++ if (ret) {
++ ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret);
++ kfree_skb(bcn);
++ return ret;
++ }
++
++ /* P2P IE is inserted by firmware automatically (as configured above)
++ * so remove it from the base beacon template to avoid duplicate P2P
++ * IEs in beacon frames.
++ */
++ ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
++ offsetof(struct ieee80211_mgmt,
++ u.beacon.variable));
++
++ ret = ath10k_wmi_bcn_tmpl(ar, arvif->vdev_id, offs.tim_offset, bcn, 0,
++ 0, NULL, 0);
++ kfree_skb(bcn);
++
++ if (ret) {
++ ath10k_warn(ar, "failed to submit beacon template command: %d\n",
++ ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++ struct ieee80211_hw *hw = ar->hw;
++ struct ieee80211_vif *vif = arvif->vif;
++ struct sk_buff *prb;
++ int ret;
++
++ if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map))
++ return 0;
++
++ prb = ieee80211_proberesp_get(hw, vif);
++ if (!prb) {
++ ath10k_warn(ar, "failed to get probe resp template from mac80211\n");
++ return -EPERM;
++ }
++
++ ret = ath10k_wmi_prb_tmpl(ar, arvif->vdev_id, prb);
++ kfree_skb(prb);
++
++ if (ret) {
++ ath10k_warn(ar, "failed to submit probe resp template command: %d\n",
++ ret);
++ return ret;
++ }
++
++ return 0;
++}
++
+ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
+- struct ieee80211_bss_conf *info)
++ struct ieee80211_bss_conf *info)
+ {
++ struct ath10k *ar = arvif->ar;
+ int ret = 0;
+
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+@@ -902,15 +1131,7 @@ static void ath10k_control_beaconing(str
+ arvif->is_up = false;
+
+ spin_lock_bh(&arvif->ar->data_lock);
+- if (arvif->beacon) {
+- dma_unmap_single(arvif->ar->dev,
+- ATH10K_SKB_CB(arvif->beacon)->paddr,
+- arvif->beacon->len, DMA_TO_DEVICE);
+- dev_kfree_skb_any(arvif->beacon);
+-
+- arvif->beacon = NULL;
+- arvif->beacon_sent = false;
+- }
++ ath10k_mac_vif_beacon_free(arvif);
+ spin_unlock_bh(&arvif->ar->data_lock);
+
+ return;
+@@ -923,12 +1144,12 @@ static void ath10k_control_beaconing(str
+ return;
+
+ arvif->aid = 0;
+- memcpy(arvif->bssid, info->bssid, ETH_ALEN);
++ ether_addr_copy(arvif->bssid, info->bssid);
+
+ ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+ arvif->bssid);
+ if (ret) {
+- ath10k_warn("failed to bring up vdev %d: %i\n",
++ ath10k_warn(ar, "failed to bring up vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ ath10k_vdev_stop(arvif);
+ return;
+@@ -937,13 +1158,14 @@ static void ath10k_control_beaconing(str
+ arvif->is_started = true;
+ arvif->is_up = true;
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
+ }
+
+ static void ath10k_control_ibss(struct ath10k_vif *arvif,
+ struct ieee80211_bss_conf *info,
+ const u8 self_peer[ETH_ALEN])
+ {
++ struct ath10k *ar = arvif->ar;
+ u32 vdev_param;
+ int ret = 0;
+
+@@ -952,20 +1174,12 @@ static void ath10k_control_ibss(struct a
+ if (!info->ibss_joined) {
+ ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
+ if (ret)
+- ath10k_warn("failed to delete IBSS self peer %pM for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to delete IBSS self peer %pM for vdev %d: %d\n",
+ self_peer, arvif->vdev_id, ret);
+
+ if (is_zero_ether_addr(arvif->bssid))
+ return;
+
+- ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
+- arvif->bssid);
+- if (ret) {
+- ath10k_warn("failed to delete IBSS BSSID peer %pM for vdev %d: %d\n",
+- arvif->bssid, arvif->vdev_id, ret);
+- return;
+- }
+-
+ memset(arvif->bssid, 0, ETH_ALEN);
+
+ return;
+@@ -973,7 +1187,7 @@ static void ath10k_control_ibss(struct a
+
+ ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
+ if (ret) {
+- ath10k_warn("failed to create IBSS self peer %pM for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to create IBSS self peer %pM for vdev %d: %d\n",
+ self_peer, arvif->vdev_id, ret);
+ return;
+ }
+@@ -982,103 +1196,211 @@ static void ath10k_control_ibss(struct a
+ ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
+ ATH10K_DEFAULT_ATIM);
+ if (ret)
+- ath10k_warn("failed to set IBSS ATIM for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to set IBSS ATIM for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ }
+
+-/*
+- * Review this when mac80211 gains per-interface powersave support.
+- */
+-static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
++static int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif)
+ {
+ struct ath10k *ar = arvif->ar;
+- struct ieee80211_conf *conf = &ar->hw->conf;
+- enum wmi_sta_powersave_param param;
+- enum wmi_sta_ps_mode psmode;
++ u32 param;
++ u32 value;
+ int ret;
+
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+
+- if (arvif->vif->type != NL80211_IFTYPE_STATION)
+- return 0;
+-
+- if (conf->flags & IEEE80211_CONF_PS) {
+- psmode = WMI_STA_PS_MODE_ENABLED;
+- param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
++ if (arvif->u.sta.uapsd)
++ value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER;
++ else
++ value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+
+- ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
+- conf->dynamic_ps_timeout);
+- if (ret) {
+- ath10k_warn("failed to set inactivity time for vdev %d: %i\n",
+- arvif->vdev_id, ret);
+- return ret;
+- }
+- } else {
+- psmode = WMI_STA_PS_MODE_DISABLED;
++ param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
++ ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value);
++ if (ret) {
++ ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n",
++ value, arvif->vdev_id, ret);
++ return ret;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
+- arvif->vdev_id, psmode ? "enable" : "disable");
++ return 0;
++}
+
+- ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
++static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++ u32 param;
++ u32 value;
++ int ret;
++
++ lockdep_assert_held(&arvif->ar->conf_mutex);
++
++ if (arvif->u.sta.uapsd)
++ value = WMI_STA_PS_PSPOLL_COUNT_UAPSD;
++ else
++ value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
++
++ param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
++ ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
++ param, value);
+ if (ret) {
+- ath10k_warn("failed to set PS Mode %d for vdev %d: %d\n",
+- psmode, arvif->vdev_id, ret);
++ ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n",
++ value, arvif->vdev_id, ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+-/**********************/
+-/* Station management */
+-/**********************/
+-
+-static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
+- struct ath10k_vif *arvif,
+- struct ieee80211_sta *sta,
+- struct ieee80211_bss_conf *bss_conf,
+- struct wmi_peer_assoc_complete_arg *arg)
++static int ath10k_mac_ps_vif_count(struct ath10k *ar)
+ {
++ struct ath10k_vif *arvif;
++ int num = 0;
++
+ lockdep_assert_held(&ar->conf_mutex);
+
+- memcpy(arg->addr, sta->addr, ETH_ALEN);
+- arg->vdev_id = arvif->vdev_id;
+- arg->peer_aid = sta->aid;
+- arg->peer_flags |= WMI_PEER_AUTH;
++ list_for_each_entry(arvif, &ar->arvifs, list)
++ if (arvif->ps)
++ num++;
+
+- if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+- /*
+- * Seems FW have problems with Power Save in STA
+- * mode when we setup this parameter to high (eg. 5).
+- * Often we see that FW don't send NULL (with clean P flags)
+- * frame even there is info about buffered frames in beacons.
+- * Sometimes we have to wait more than 10 seconds before FW
+- * will wakeup. Often sending one ping from AP to our device
+- * just fail (more than 50%).
+- *
+- * Seems setting this FW parameter to 1 couse FW
+- * will check every beacon and will wakup immediately
+- * after detection buffered data.
+- */
+- arg->peer_listen_intval = 1;
+- else
+- arg->peer_listen_intval = ar->hw->conf.listen_interval;
++ return num;
++}
+
+- arg->peer_num_spatial_streams = 1;
++static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++ struct ieee80211_vif *vif = arvif->vif;
++ struct ieee80211_conf *conf = &ar->hw->conf;
++ enum wmi_sta_powersave_param param;
++ enum wmi_sta_ps_mode psmode;
++ int ret;
++ int ps_timeout;
++ bool enable_ps;
+
+- /*
+- * The assoc capabilities are available only in managed mode.
++ lockdep_assert_held(&arvif->ar->conf_mutex);
++
++ if (arvif->vif->type != NL80211_IFTYPE_STATION)
++ return 0;
++
++ enable_ps = arvif->ps;
++
++ if (enable_ps && ath10k_mac_ps_vif_count(ar) > 1 &&
++ !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
++ ar->fw_features)) {
++ ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
++ arvif->vdev_id);
++ enable_ps = false;
++ }
++
++ if (enable_ps) {
++ psmode = WMI_STA_PS_MODE_ENABLED;
++ param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
++
++ ps_timeout = conf->dynamic_ps_timeout;
++ if (ps_timeout == 0) {
++ /* Firmware doesn't like 0 */
++ ps_timeout = ieee80211_tu_to_usec(
++ vif->bss_conf.beacon_int) / 1000;
++ }
++
++ ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
++ ps_timeout);
++ if (ret) {
++ ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++ } else {
++ psmode = WMI_STA_PS_MODE_DISABLED;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d psmode %s\n",
++ arvif->vdev_id, psmode ? "enable" : "disable");
++
++ ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode);
++ if (ret) {
++ ath10k_warn(ar, "failed to set PS Mode %d for vdev %d: %d\n",
++ psmode, arvif->vdev_id, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++ struct wmi_sta_keepalive_arg arg = {};
++ int ret;
++
++ lockdep_assert_held(&arvif->ar->conf_mutex);
++
++ if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
++ return 0;
++
++ if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map))
++ return 0;
++
++ /* Some firmware revisions have a bug and ignore the `enabled` field.
++ * Instead use the interval to disable the keepalive.
++ */
++ arg.vdev_id = arvif->vdev_id;
++ arg.enabled = 1;
++ arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME;
++ arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE;
++
++ ret = ath10k_wmi_sta_keepalive(ar, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++/**********************/
++/* Station management */
++/**********************/
++
++static u32 ath10k_peer_assoc_h_listen_intval(struct ath10k *ar,
++ struct ieee80211_vif *vif)
++{
++ /* Some firmware revisions have unstable STA powersave when listen
++ * interval is set too high (e.g. 5). The symptoms are firmware doesn't
++ * generate NullFunc frames properly even if buffered frames have been
++ * indicated in Beacon TIM. Firmware would seldom wake up to pull
++ * buffered frames. Often pinging the device from AP would simply fail.
++ *
++ * As a workaround set it to 1.
+ */
+- if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
+- arg->peer_caps = bss_conf->assoc_capability;
++ if (vif->type == NL80211_IFTYPE_STATION)
++ return 1;
++
++ return ar->hw->conf.listen_interval;
++}
++
++static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta,
++ struct wmi_peer_assoc_complete_arg *arg)
++{
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ ether_addr_copy(arg->addr, sta->addr);
++ arg->vdev_id = arvif->vdev_id;
++ arg->peer_aid = sta->aid;
++ arg->peer_flags |= WMI_PEER_AUTH;
++ arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
++ arg->peer_num_spatial_streams = 1;
++ arg->peer_caps = vif->bss_conf.assoc_capability;
+ }
+
+ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
+- struct ath10k_vif *arvif,
++ struct ieee80211_vif *vif,
+ struct wmi_peer_assoc_complete_arg *arg)
+ {
+- struct ieee80211_vif *vif = arvif->vif;
+ struct ieee80211_bss_conf *info = &vif->bss_conf;
+ struct cfg80211_bss *bss;
+ const u8 *rsnie = NULL;
+@@ -1097,21 +1419,21 @@ static void ath10k_peer_assoc_h_crypto(s
+ ies = rcu_dereference(bss->ies);
+
+ wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+- WLAN_OUI_TYPE_MICROSOFT_WPA,
+- ies->data,
+- ies->len);
++ WLAN_OUI_TYPE_MICROSOFT_WPA,
++ ies->data,
++ ies->len);
+ rcu_read_unlock();
+ cfg80211_put_bss(ar->hw->wiphy, bss);
+ }
+
+ /* FIXME: base on RSN IE/WPA IE is a correct idea? */
+ if (rsnie || wpaie) {
+- ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
+ arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+ }
+
+ if (wpaie) {
+- ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
+ arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+ }
+ }
+@@ -1149,6 +1471,7 @@ static void ath10k_peer_assoc_h_ht(struc
+ {
+ const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ int i, n;
++ u32 stbc;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+@@ -1185,7 +1508,6 @@ static void ath10k_peer_assoc_h_ht(struc
+ }
+
+ if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
+- u32 stbc;
+ stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
+ stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
+@@ -1220,7 +1542,7 @@ static void ath10k_peer_assoc_h_ht(struc
+ arg->peer_num_spatial_streams = sta->rx_nss;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
++ ath10k_dbg(ar, 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);
+@@ -1237,7 +1559,7 @@ static int ath10k_peer_assoc_qos_ap(stru
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (sta->wme && sta->uapsd_queues) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
+ sta->uapsd_queues, sta->max_sp);
+
+ if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+@@ -1253,7 +1575,6 @@ static int ath10k_peer_assoc_qos_ap(stru
+ uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
+ WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
+
+-
+ if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
+ max_sp = sta->max_sp;
+
+@@ -1262,7 +1583,7 @@ static int ath10k_peer_assoc_qos_ap(stru
+ WMI_AP_PS_PEER_PARAM_UAPSD,
+ uapsd);
+ if (ret) {
+- ath10k_warn("failed to set ap ps peer param uapsd for vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set ap ps peer param uapsd for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -1272,7 +1593,7 @@ static int ath10k_peer_assoc_qos_ap(stru
+ WMI_AP_PS_PEER_PARAM_MAX_SP,
+ max_sp);
+ if (ret) {
+- ath10k_warn("failed to set ap ps peer param max sp for vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set ap ps peer param max sp for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -1282,9 +1603,10 @@ static int ath10k_peer_assoc_qos_ap(stru
+ sta->listen_interval - mac80211 patch required.
+ Currently use 10 seconds */
+ ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
+- WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10);
++ WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
++ 10);
+ if (ret) {
+- ath10k_warn("failed to set ap ps peer param ageout time for vdev %i: %d\n",
++ ath10k_warn(ar, "failed to set ap ps peer param ageout time for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -1304,8 +1626,11 @@ static void ath10k_peer_assoc_h_vht(stru
+ return;
+
+ arg->peer_flags |= WMI_PEER_VHT;
+- arg->peer_vht_caps = vht_cap->cap;
+
++ if (ar->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)
++ arg->peer_flags |= WMI_PEER_VHT_2G;
++
++ arg->peer_vht_caps = vht_cap->cap;
+
+ ampdu_factor = (vht_cap->cap &
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+@@ -1331,16 +1656,17 @@ static void ath10k_peer_assoc_h_vht(stru
+ 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 %pM max_mpdu %d flags 0x%x\n",
++ ath10k_dbg(ar, 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,
+- struct ath10k_vif *arvif,
++ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+- struct ieee80211_bss_conf *bss_conf,
+ struct wmi_peer_assoc_complete_arg *arg)
+ {
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++
+ switch (arvif->vdev_type) {
+ case WMI_VDEV_TYPE_AP:
+ if (sta->wme)
+@@ -1352,16 +1678,29 @@ static void ath10k_peer_assoc_h_qos(stru
+ }
+ break;
+ case WMI_VDEV_TYPE_STA:
+- if (bss_conf->qos)
++ if (vif->bss_conf.qos)
++ arg->peer_flags |= WMI_PEER_QOS;
++ break;
++ case WMI_VDEV_TYPE_IBSS:
++ if (sta->wme)
+ arg->peer_flags |= WMI_PEER_QOS;
+ break;
+ default:
+ break;
+ }
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n",
++ sta->addr, !!(arg->peer_flags & WMI_PEER_QOS));
++}
++
++static bool ath10k_mac_sta_has_11g_rates(struct ieee80211_sta *sta)
++{
++ /* First 4 rates in ath10k_rates are CCK (11b) rates. */
++ return sta->supp_rates[IEEE80211_BAND_2GHZ] >> 4;
+ }
+
+ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
+- struct ath10k_vif *arvif,
++ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct wmi_peer_assoc_complete_arg *arg)
+ {
+@@ -1369,13 +1708,20 @@ static void ath10k_peer_assoc_h_phymode(
+
+ switch (ar->hw->conf.chandef.chan->band) {
+ case IEEE80211_BAND_2GHZ:
+- if (sta->ht_cap.ht_supported) {
++ if (sta->vht_cap.vht_supported) {
++ if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
++ phymode = MODE_11AC_VHT40;
++ else
++ phymode = MODE_11AC_VHT20;
++ } else if (sta->ht_cap.ht_supported) {
+ if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+ phymode = MODE_11NG_HT40;
+ else
+ phymode = MODE_11NG_HT20;
+- } else {
++ } else if (ath10k_mac_sta_has_11g_rates(sta)) {
+ phymode = MODE_11G;
++ } else {
++ phymode = MODE_11B;
+ }
+
+ break;
+@@ -1404,7 +1750,7 @@ static void ath10k_peer_assoc_h_phymode(
+ break;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM phymode %s\n",
+ sta->addr, ath10k_wmi_phymode_str(phymode));
+
+ arg->peer_phymode = phymode;
+@@ -1412,22 +1758,21 @@ static void ath10k_peer_assoc_h_phymode(
+ }
+
+ static int ath10k_peer_assoc_prepare(struct ath10k *ar,
+- struct ath10k_vif *arvif,
++ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+- struct ieee80211_bss_conf *bss_conf,
+ struct wmi_peer_assoc_complete_arg *arg)
+ {
+ lockdep_assert_held(&ar->conf_mutex);
+
+ memset(arg, 0, sizeof(*arg));
+
+- ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg);
+- ath10k_peer_assoc_h_crypto(ar, arvif, arg);
++ ath10k_peer_assoc_h_basic(ar, vif, sta, arg);
++ ath10k_peer_assoc_h_crypto(ar, vif, arg);
+ ath10k_peer_assoc_h_rates(ar, sta, arg);
+ ath10k_peer_assoc_h_ht(ar, sta, arg);
+ ath10k_peer_assoc_h_vht(ar, sta, arg);
+- ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg);
+- ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg);
++ ath10k_peer_assoc_h_qos(ar, vif, sta, arg);
++ ath10k_peer_assoc_h_phymode(ar, vif, sta, arg);
+
+ return 0;
+ }
+@@ -1459,6 +1804,68 @@ static int ath10k_setup_peer_smps(struct
+ ath10k_smps_map[smps]);
+ }
+
++static int ath10k_mac_vif_recalc_txbf(struct ath10k *ar,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta_vht_cap vht_cap)
++{
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++ int ret;
++ u32 param;
++ u32 value;
++
++ if (!(ar->vht_cap_info &
++ (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
++ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
++ IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
++ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
++ return 0;
++
++ param = ar->wmi.vdev_param->txbf;
++ value = 0;
++
++ if (WARN_ON(param == WMI_VDEV_PARAM_UNSUPPORTED))
++ return 0;
++
++ /* The following logic is correct. If a remote STA advertises support
++ * for being a beamformer then we should enable us being a beamformee.
++ */
++
++ if (ar->vht_cap_info &
++ (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
++ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
++ if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
++ value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
++
++ if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
++ value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE;
++ }
++
++ if (ar->vht_cap_info &
++ (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
++ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
++ if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
++ value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
++
++ if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
++ value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER;
++ }
++
++ if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFEE)
++ value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
++
++ if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFER)
++ value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
++
++ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, value);
++ if (ret) {
++ ath10k_warn(ar, "failed to submit vdev param txbf 0x%x: %d\n",
++ value, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
+ /* can be called only in mac80211 callbacks due to `key_count` usage */
+ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+@@ -1467,17 +1874,21 @@ static void ath10k_bss_assoc(struct ieee
+ struct ath10k *ar = hw->priv;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ieee80211_sta_ht_cap ht_cap;
++ struct ieee80211_sta_vht_cap vht_cap;
+ struct wmi_peer_assoc_complete_arg peer_arg;
+ struct ieee80211_sta *ap_sta;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n",
++ arvif->vdev_id, arvif->bssid, arvif->aid);
++
+ rcu_read_lock();
+
+ ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
+ if (!ap_sta) {
+- ath10k_warn("failed to find station entry for bss %pM vdev %i\n",
++ ath10k_warn(ar, "failed to find station entry for bss %pM vdev %i\n",
+ bss_conf->bssid, arvif->vdev_id);
+ rcu_read_unlock();
+ return;
+@@ -1486,11 +1897,11 @@ static void ath10k_bss_assoc(struct ieee
+ /* ap_sta must be accessed only within rcu section which must be left
+ * before calling ath10k_setup_peer_smps() which might sleep. */
+ ht_cap = ap_sta->ht_cap;
++ vht_cap = ap_sta->vht_cap;
+
+- ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
+- bss_conf, &peer_arg);
++ ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
+ if (ret) {
+- ath10k_warn("failed to prepare peer assoc for %pM vdev %i: %d\n",
++ ath10k_warn(ar, "failed to prepare peer assoc for %pM vdev %i: %d\n",
+ bss_conf->bssid, arvif->vdev_id, ret);
+ rcu_read_unlock();
+ return;
+@@ -1500,88 +1911,100 @@ static void ath10k_bss_assoc(struct ieee
+
+ ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+ if (ret) {
+- ath10k_warn("failed to run peer assoc for %pM vdev %i: %d\n",
++ ath10k_warn(ar, "failed to run peer assoc for %pM vdev %i: %d\n",
+ bss_conf->bssid, arvif->vdev_id, ret);
+ return;
+ }
+
+ ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap);
+ if (ret) {
+- ath10k_warn("failed to setup peer SMPS for vdev %i: %d\n",
++ ath10k_warn(ar, "failed to setup peer SMPS for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
++ if (ret) {
++ ath10k_warn(ar, "failed to recalc txbf for vdev %i on bss %pM: %d\n",
++ arvif->vdev_id, bss_conf->bssid, ret);
++ return;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d up (associated) bssid %pM aid %d\n",
+ arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+
++ WARN_ON(arvif->is_up);
++
+ arvif->aid = bss_conf->aid;
+- memcpy(arvif->bssid, bss_conf->bssid, ETH_ALEN);
++ ether_addr_copy(arvif->bssid, bss_conf->bssid);
+
+ ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
+ if (ret) {
+- ath10k_warn("failed to set vdev %d up: %d\n",
++ ath10k_warn(ar, "failed to set vdev %d up: %d\n",
+ arvif->vdev_id, ret);
+ return;
+ }
+
+ arvif->is_up = true;
++
++ /* Workaround: Some firmware revisions (tested with qca6174
++ * WLAN.RM.2.0-00073) have buggy powersave state machine and must be
++ * poked with peer param command.
++ */
++ ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, arvif->bssid,
++ WMI_PEER_DUMMY_VAR, 1);
++ if (ret) {
++ ath10k_warn(ar, "failed to poke peer %pM param for ps workaround on vdev %i: %d\n",
++ arvif->bssid, arvif->vdev_id, ret);
++ return;
++ }
+ }
+
+-/*
+- * FIXME: flush TIDs
+- */
+ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+ {
+ struct ath10k *ar = hw->priv;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++ struct ieee80211_sta_vht_cap vht_cap = {};
+ 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 we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
+- * report beacons from previously associated network through HTT.
+- * This in turn would spam mac80211 WARN_ON if we bring down all
+- * interfaces as it expects there is no rx when no interface is
+- * running.
+- */
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n",
++ arvif->vdev_id, arvif->bssid);
+
+- /* FIXME: why don't we print error if wmi call fails? */
+ ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
++ if (ret)
++ ath10k_warn(ar, "faield to down vdev %i: %d\n",
++ arvif->vdev_id, ret);
+
+- arvif->def_wep_key_idx = 0;
++ arvif->def_wep_key_idx = -1;
++
++ ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap);
++ if (ret) {
++ ath10k_warn(ar, "failed to recalc txbf for vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ return;
++ }
+
+- arvif->is_started = false;
+ arvif->is_up = false;
+ }
+
+-static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
+- struct ieee80211_sta *sta, bool reassoc)
++static int ath10k_station_assoc(struct ath10k *ar,
++ struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta,
++ bool reassoc)
+ {
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct wmi_peer_assoc_complete_arg peer_arg;
+ int ret = 0;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
++ ret = ath10k_peer_assoc_prepare(ar, vif, sta, &peer_arg);
+ if (ret) {
+- ath10k_warn("failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
++ ath10k_warn(ar, "failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
+ sta->addr, arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -1589,48 +2012,59 @@ static int ath10k_station_assoc(struct a
+ peer_arg.peer_reassoc = reassoc;
+ ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
+ if (ret) {
+- ath10k_warn("failed to run peer assoc for STA %pM vdev %i: %d\n",
++ ath10k_warn(ar, "failed to run peer assoc for STA %pM vdev %i: %d\n",
+ sta->addr, arvif->vdev_id, ret);
+ return ret;
+ }
+
+- ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
+- if (ret) {
+- ath10k_warn("failed to setup peer SMPS for vdev %d: %d\n",
+- arvif->vdev_id, ret);
+- return ret;
+- }
+-
+- if (!sta->wme) {
+- arvif->num_legacy_stations++;
+- ret = ath10k_recalc_rtscts_prot(arvif);
++ /* Re-assoc is run only to update supported rates for given station. It
++ * doesn't make much sense to reconfigure the peer completely.
++ */
++ if (!reassoc) {
++ ret = ath10k_setup_peer_smps(ar, arvif, sta->addr,
++ &sta->ht_cap);
+ if (ret) {
+- ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+- }
+
+- ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+- if (ret) {
+- ath10k_warn("failed to install peer wep keys for vdev %i: %d\n",
+- arvif->vdev_id, ret);
+- return ret;
+- }
++ ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
++ if (ret) {
++ ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
++ sta->addr, arvif->vdev_id, ret);
++ return ret;
++ }
+
+- ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
+- if (ret) {
+- ath10k_warn("failed to set qos params for STA %pM for vdev %i: %d\n",
+- sta->addr, arvif->vdev_id, ret);
+- return ret;
++ if (!sta->wme) {
++ arvif->num_legacy_stations++;
++ ret = ath10k_recalc_rtscts_prot(arvif);
++ if (ret) {
++ ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++ }
++
++ /* Plumb cached keys only for static WEP */
++ if (arvif->def_wep_key_idx != -1) {
++ ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
++ if (ret) {
++ ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++ }
+ }
+
+ return ret;
+ }
+
+-static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
++static int ath10k_station_disassoc(struct ath10k *ar,
++ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+ {
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ int ret = 0;
+
+ lockdep_assert_held(&ar->conf_mutex);
+@@ -1639,7 +2073,7 @@ static int ath10k_station_disassoc(struc
+ arvif->num_legacy_stations--;
+ ret = ath10k_recalc_rtscts_prot(arvif);
+ if (ret) {
+- ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -1647,7 +2081,7 @@ static int ath10k_station_disassoc(struc
+
+ ret = ath10k_clear_peer_keys(arvif, sta->addr);
+ if (ret) {
+- ath10k_warn("failed to clear all peer wep keys for vdev %i: %d\n",
++ ath10k_warn(ar, "failed to clear all peer wep keys for vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+@@ -1722,6 +2156,7 @@ static int ath10k_update_channel_list(st
+ ch->passive = passive;
+
+ ch->freq = channel->center_freq;
++ ch->band_center_freq1 = channel->center_freq;
+ ch->min_power = 0;
+ ch->max_power = channel->max_power * 2;
+ ch->max_reg_power = channel->max_reg_power * 2;
+@@ -1739,7 +2174,7 @@ static int ath10k_update_channel_list(st
+ if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN))
+ continue;
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "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,
+@@ -1782,7 +2217,7 @@ static void ath10k_regd_update(struct at
+
+ ret = ath10k_update_channel_list(ar);
+ if (ret)
+- ath10k_warn("failed to update channel list: %d\n", ret);
++ ath10k_warn(ar, "failed to update channel list: %d\n", ret);
+
+ regpair = ar->ath_common.regulatory.regpair;
+
+@@ -1803,7 +2238,7 @@ static void ath10k_regd_update(struct at
+ regpair->reg_5ghz_ctl,
+ wmi_dfs_reg);
+ if (ret)
+- ath10k_warn("failed to set pdev regdomain: %d\n", ret);
++ ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret);
+ }
+
+ static void ath10k_reg_notifier(struct wiphy *wiphy,
+@@ -1816,12 +2251,12 @@ static void ath10k_reg_notifier(struct w
+ ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+ if (config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
+- ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs region 0x%x\n",
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs region 0x%x\n",
+ request->dfs_region);
+ result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
+ request->dfs_region);
+ if (!result)
+- ath10k_warn("DFS region 0x%X not supported, will trigger radar for every pulse\n",
++ ath10k_warn(ar, "DFS region 0x%X not supported, will trigger radar for every pulse\n",
+ request->dfs_region);
+ }
+
+@@ -1849,28 +2284,25 @@ static u8 ath10k_tx_h_get_tid(struct iee
+ return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
+ }
+
+-static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar,
+- struct ieee80211_tx_info *info)
++static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif)
+ {
+- if (info->control.vif)
+- return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
++ if (vif)
++ return ath10k_vif_to_arvif(vif)->vdev_id;
+
+ if (ar->monitor_started)
+ return ar->monitor_vdev_id;
+
+- ath10k_warn("failed to resolve vdev id\n");
++ ath10k_warn(ar, "failed to resolve vdev id\n");
+ return 0;
+ }
+
+-/*
+- * Frames sent to the FW have to be in "Native Wifi" format.
+- * Strip the QoS field from the 802.11 header.
++/* HTT Tx uses Native Wifi tx mode which expects 802.11 frames without QoS
++ * Control in the header.
+ */
+-static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
+- struct ieee80211_tx_control *control,
+- struct sk_buff *skb)
++static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
+ {
+ struct ieee80211_hdr *hdr = (void *)skb->data;
++ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+ u8 *qos_ctl;
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+@@ -1880,68 +2312,24 @@ static void ath10k_tx_h_qos_workaround(s
+ 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_wep_key_work(struct work_struct *work)
+-{
+- struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+- wep_key_work);
+- int ret, keyidx = arvif->def_wep_key_newidx;
+-
+- if (arvif->def_wep_key_idx == keyidx)
+- return;
+-
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+- arvif->vdev_id, keyidx);
+
+- ret = ath10k_wmi_vdev_set_param(arvif->ar,
+- arvif->vdev_id,
+- arvif->ar->wmi.vdev_param->def_keyid,
+- keyidx);
+- if (ret) {
+- ath10k_warn("failed to update wep key index for vdev %d: %d\n",
+- arvif->vdev_id,
+- ret);
+- return;
++ /* Fw/Hw generates a corrupted QoS Control Field for QoS NullFunc
++ * frames. Powersave is handled by the fw/hw so QoS NyllFunc frames are
++ * used only for CQM purposes (e.g. hostapd station keepalive ping) so
++ * it is safe to downgrade to NullFunc.
++ */
++ hdr = (void *)skb->data;
++ if (ieee80211_is_qos_nullfunc(hdr->frame_control)) {
++ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
++ cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+ }
+-
+- arvif->def_wep_key_idx = keyidx;
+ }
+
+-static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
+-{
+- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+- struct ieee80211_vif *vif = info->control.vif;
+- struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+- struct ath10k *ar = arvif->ar;
+- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+- struct ieee80211_key_conf *key = info->control.hw_key;
+-
+- if (!ieee80211_has_protected(hdr->frame_control))
+- return;
+-
+- if (!key)
+- return;
+-
+- if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+- key->cipher != WLAN_CIPHER_SUITE_WEP104)
+- return;
+-
+- if (key->keyidx == arvif->def_wep_key_idx)
+- return;
+-
+- /* FIXME: Most likely a few frames will be TXed with an old key. Simply
+- * queueing frames until key index is updated is not an option because
+- * sk_buff may need more processing to be done, e.g. offchannel */
+- arvif->def_wep_key_newidx = key->keyidx;
+- ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
+-}
+-
+-static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
++static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
++ struct ieee80211_vif *vif,
++ struct sk_buff *skb)
+ {
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+- struct ieee80211_vif *vif = info->control.vif;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+ /* This is case only for P2P_GO */
+@@ -1961,6 +2349,18 @@ static void ath10k_tx_h_add_p2p_noa_ie(s
+ }
+ }
+
++static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
++{
++ /* FIXME: Not really sure since when the behaviour changed. At some
++ * point new firmware stopped requiring creation of peer entries for
++ * offchannel tx (and actually creating them causes issues with wmi-htc
++ * tx credit replenishment and reliability). Assuming it's at least 3.4
++ * because that's when the `freq` was introduced to TX_FRM HTT command.
++ */
++ return !(ar->htt.target_version_major >= 3 &&
++ ar->htt.target_version_minor >= 4);
++}
++
+ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
+ {
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+@@ -1977,7 +2377,7 @@ static void ath10k_tx_htt(struct ath10k
+ ar->fw_features)) {
+ if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >=
+ ATH10K_MAX_NUM_MGMT_PENDING) {
+- ath10k_warn("reached WMI management tranmist queue limit\n");
++ ath10k_warn(ar, "reached WMI management transmit queue limit\n");
+ ret = -EBUSY;
+ goto exit;
+ }
+@@ -2001,7 +2401,8 @@ static void ath10k_tx_htt(struct ath10k
+
+ exit:
+ if (ret) {
+- ath10k_warn("failed to transmit packet, dropping: %d\n", ret);
++ ath10k_warn(ar, "failed to transmit packet, dropping: %d\n",
++ ret);
+ ieee80211_free_txskb(ar->hw, skb);
+ }
+ }
+@@ -2043,7 +2444,7 @@ void ath10k_offchan_tx_work(struct work_
+
+ mutex_lock(&ar->conf_mutex);
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %p\n",
+ skb);
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+@@ -2056,13 +2457,13 @@ void ath10k_offchan_tx_work(struct work_
+
+ if (peer)
+ /* FIXME: should this use ath10k_warn()? */
+- ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
+ peer_addr, vdev_id);
+
+ if (!peer) {
+ ret = ath10k_peer_create(ar, vdev_id, peer_addr);
+ if (ret)
+- ath10k_warn("failed to create peer %pM on vdev %d: %d\n",
++ ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n",
+ peer_addr, vdev_id, ret);
+ }
+
+@@ -2075,14 +2476,14 @@ void ath10k_offchan_tx_work(struct work_
+
+ ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
+ 3 * HZ);
+- if (ret <= 0)
+- ath10k_warn("timed out waiting for offchannel skb %p\n",
++ if (ret == 0)
++ ath10k_warn(ar, "timed out waiting for offchannel skb %p\n",
+ skb);
+
+ if (!peer) {
+ ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
+ if (ret)
+- ath10k_warn("failed to delete peer %pM on vdev %d: %d\n",
++ ath10k_warn(ar, "failed to delete peer %pM on vdev %d: %d\n",
+ peer_addr, vdev_id, ret);
+ }
+
+@@ -2116,7 +2517,7 @@ void ath10k_mgmt_over_wmi_tx_work(struct
+
+ ret = ath10k_wmi_mgmt_tx(ar, skb);
+ if (ret) {
+- ath10k_warn("failed to transmit management frame via WMI: %d\n",
++ ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n",
+ ret);
+ ieee80211_free_txskb(ar->hw, skb);
+ }
+@@ -2127,34 +2528,41 @@ void ath10k_mgmt_over_wmi_tx_work(struct
+ /* Scanning */
+ /************/
+
+-/*
+- * This gets called if we dont get a heart-beat during scan.
+- * This may indicate the FW has hung and we need to abort the
+- * scan manually to prevent cancel_hw_scan() from deadlocking
+- */
+-void ath10k_reset_scan(unsigned long ptr)
++void __ath10k_scan_finish(struct ath10k *ar)
+ {
+- struct ath10k *ar = (struct ath10k *)ptr;
+-
+- spin_lock_bh(&ar->data_lock);
+- if (!ar->scan.in_progress) {
+- spin_unlock_bh(&ar->data_lock);
+- return;
+- }
++ lockdep_assert_held(&ar->data_lock);
+
+- ath10k_warn("scan timed out, firmware problem?\n");
+-
+- if (ar->scan.is_roc)
+- ieee80211_remain_on_channel_expired(ar->hw);
+- else
+- ieee80211_scan_completed(ar->hw, 1 /* aborted */);
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ break;
++ case ATH10K_SCAN_RUNNING:
++ if (ar->scan.is_roc)
++ ieee80211_remain_on_channel_expired(ar->hw);
++ /* fall through */
++ case ATH10K_SCAN_ABORTING:
++ if (!ar->scan.is_roc)
++ ieee80211_scan_completed(ar->hw,
++ (ar->scan.state ==
++ ATH10K_SCAN_ABORTING));
++ /* fall through */
++ case ATH10K_SCAN_STARTING:
++ ar->scan.state = ATH10K_SCAN_IDLE;
++ ar->scan_channel = NULL;
++ ath10k_offchan_tx_purge(ar);
++ cancel_delayed_work(&ar->scan.timeout);
++ complete_all(&ar->scan.completed);
++ break;
++ }
++}
+
+- ar->scan.in_progress = false;
+- complete_all(&ar->scan.completed);
++void ath10k_scan_finish(struct ath10k *ar)
++{
++ spin_lock_bh(&ar->data_lock);
++ __ath10k_scan_finish(ar);
+ spin_unlock_bh(&ar->data_lock);
+ }
+
+-static int ath10k_abort_scan(struct ath10k *ar)
++static int ath10k_scan_stop(struct ath10k *ar)
+ {
+ struct wmi_stop_scan_arg arg = {
+ .req_id = 1, /* FIXME */
+@@ -2165,47 +2573,79 @@ static int ath10k_abort_scan(struct ath1
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- del_timer_sync(&ar->scan.timeout);
++ ret = ath10k_wmi_stop_scan(ar, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to stop wmi scan: %d\n", ret);
++ goto out;
++ }
+
+- spin_lock_bh(&ar->data_lock);
+- if (!ar->scan.in_progress) {
+- spin_unlock_bh(&ar->data_lock);
+- return 0;
++ 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;
++ } else if (ret > 0) {
++ ret = 0;
+ }
+
+- ar->scan.aborting = true;
++out:
++ /* Scan state should be updated upon scan completion but in case
++ * firmware fails to deliver the event (for whatever reason) it is
++ * desired to clean up scan state anyway. Firmware may have just
++ * dropped the scan completion event delivery due to transport pipe
++ * being overflown with data and/or it can recover on its own before
++ * next scan request is submitted.
++ */
++ spin_lock_bh(&ar->data_lock);
++ if (ar->scan.state != ATH10K_SCAN_IDLE)
++ __ath10k_scan_finish(ar);
+ spin_unlock_bh(&ar->data_lock);
+
+- ret = ath10k_wmi_stop_scan(ar, &arg);
+- if (ret) {
+- ath10k_warn("failed to stop wmi scan: %d\n", ret);
+- spin_lock_bh(&ar->data_lock);
+- ar->scan.in_progress = false;
+- ath10k_offchan_tx_purge(ar);
+- spin_unlock_bh(&ar->data_lock);
+- return -EIO;
+- }
++ return ret;
++}
+
+- ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
+- if (ret == 0)
+- ath10k_warn("timed out while waiting for scan to stop\n");
++static void ath10k_scan_abort(struct ath10k *ar)
++{
++ int ret;
+
+- /* scan completion may be done right after we timeout here, so let's
+- * check the in_progress and tell mac80211 scan is completed. if we
+- * don't do that and FW fails to send us scan completion indication
+- * then userspace won't be able to scan anymore */
+- ret = 0;
++ lockdep_assert_held(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+- if (ar->scan.in_progress) {
+- ath10k_warn("failed to stop scan, it's still in progress\n");
+- ar->scan.in_progress = false;
+- ath10k_offchan_tx_purge(ar);
+- ret = -ETIMEDOUT;
++
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ /* This can happen if timeout worker kicked in and called
++ * abortion while scan completion was being processed.
++ */
++ break;
++ case ATH10K_SCAN_STARTING:
++ case ATH10K_SCAN_ABORTING:
++ ath10k_warn(ar, "refusing scan abortion due to invalid scan state: %s (%d)\n",
++ ath10k_scan_state_str(ar->scan.state),
++ ar->scan.state);
++ break;
++ case ATH10K_SCAN_RUNNING:
++ ar->scan.state = ATH10K_SCAN_ABORTING;
++ spin_unlock_bh(&ar->data_lock);
++
++ ret = ath10k_scan_stop(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to abort scan: %d\n", ret);
++
++ spin_lock_bh(&ar->data_lock);
++ break;
+ }
++
+ spin_unlock_bh(&ar->data_lock);
++}
+
+- return ret;
++void ath10k_scan_timeout_work(struct work_struct *work)
++{
++ struct ath10k *ar = container_of(work, struct ath10k,
++ scan.timeout.work);
++
++ mutex_lock(&ar->conf_mutex);
++ ath10k_scan_abort(ar);
++ mutex_unlock(&ar->conf_mutex);
+ }
+
+ static int ath10k_start_scan(struct ath10k *ar,
+@@ -2221,17 +2661,27 @@ static int ath10k_start_scan(struct ath1
+
+ ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
+ if (ret == 0) {
+- ath10k_abort_scan(ar);
+- return ret;
++ ret = ath10k_scan_stop(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to stop scan: %d\n", ret);
++
++ return -ETIMEDOUT;
++ }
++
++ /* If we failed to start the scan, return error code at
++ * this point. This is probably due to some issue in the
++ * firmware, but no need to wedge the driver due to that...
++ */
++ spin_lock_bh(&ar->data_lock);
++ if (ar->scan.state == ATH10K_SCAN_IDLE) {
++ spin_unlock_bh(&ar->data_lock);
++ return -EINVAL;
+ }
++ spin_unlock_bh(&ar->data_lock);
+
+- /* the scan can complete earlier, before we even
+- * start the timer. in that case the timer handler
+- * checks ar->scan.in_progress and bails out if its
+- * false. Add a 200ms margin to account event/command
+- * processing. */
+- mod_timer(&ar->scan.timeout, jiffies +
+- msecs_to_jiffies(arg->max_scan_time+200));
++ /* Add a 200ms margin to account for event/command processing */
++ ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
++ msecs_to_jiffies(arg->max_scan_time+200));
+ return 0;
+ }
+
+@@ -2243,90 +2693,163 @@ static void ath10k_tx(struct ieee80211_h
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+ {
++ struct ath10k *ar = hw->priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ struct ieee80211_vif *vif = info->control.vif;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+- struct ath10k *ar = hw->priv;
+- u8 tid, vdev_id;
+
+ /* We should disable CCK RATE due to P2P */
+ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+- ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+- /* we must calculate tid before we apply qos workaround
+- * as we'd lose the qos control field */
+- tid = ath10k_tx_h_get_tid(hdr);
+- vdev_id = ath10k_tx_h_get_vdev_id(ar, info);
++ ATH10K_SKB_CB(skb)->htt.is_offchan = false;
++ ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr);
++ ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif);
+
+ /* 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);
++ if (vif && vif->type != NL80211_IFTYPE_MONITOR) {
++ ath10k_tx_h_nwifi(hw, skb);
++ ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
++ ath10k_tx_h_seq_no(vif, skb);
+ }
+
+- ATH10K_SKB_CB(skb)->vdev_id = vdev_id;
+- ATH10K_SKB_CB(skb)->htt.is_offchan = false;
+- ATH10K_SKB_CB(skb)->htt.tid = tid;
+-
+ if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+ spin_lock_bh(&ar->data_lock);
+- ATH10K_SKB_CB(skb)->htt.is_offchan = true;
++ ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq;
+ ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
+ spin_unlock_bh(&ar->data_lock);
+
+- ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
++ if (ath10k_mac_need_offchan_tx_work(ar)) {
++ ATH10K_SKB_CB(skb)->htt.freq = 0;
++ ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+
+- skb_queue_tail(&ar->offchan_tx_queue, skb);
+- ieee80211_queue_work(hw, &ar->offchan_tx_work);
+- return;
++ 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;
++ }
+ }
+
+ ath10k_tx_htt(ar, skb);
+ }
+
+-/*
+- * Initialize various parameters with default vaules.
+- */
++/* Must not be called with conf_mutex held as workers can use that also. */
++void ath10k_drain_tx(struct ath10k *ar)
++{
++ /* make sure rcu-protected mac80211 tx path itself is drained */
++ synchronize_net();
++
++ ath10k_offchan_tx_purge(ar);
++ ath10k_mgmt_over_wmi_tx_purge(ar);
++
++ cancel_work_sync(&ar->offchan_tx_work);
++ cancel_work_sync(&ar->wmi_mgmt_tx_work);
++}
++
+ void ath10k_halt(struct ath10k *ar)
+ {
+ struct ath10k_vif *arvif;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- if (ath10k_monitor_is_enabled(ar)) {
+- clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+- ar->promisc = false;
+- ar->monitor = false;
++ clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
++ ar->filter_flags = 0;
++ ar->monitor = false;
++
++ if (ar->monitor_started)
+ ath10k_monitor_stop(ar);
+- }
+
+- del_timer_sync(&ar->scan.timeout);
+- ath10k_offchan_tx_purge(ar);
+- ath10k_mgmt_over_wmi_tx_purge(ar);
++ ar->monitor_started = false;
++
++ ath10k_scan_finish(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);
++ list_for_each_entry(arvif, &ar->arvifs, list)
++ ath10k_mac_vif_beacon_cleanup(arvif);
++ spin_unlock_bh(&ar->data_lock);
++}
++
++static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
++{
++ struct ath10k *ar = hw->priv;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->cfg_tx_chainmask) {
++ *tx_ant = ar->cfg_tx_chainmask;
++ *rx_ant = ar->cfg_rx_chainmask;
++ } else {
++ *tx_ant = ar->supp_tx_chainmask;
++ *rx_ant = ar->supp_rx_chainmask;
+ }
+
+- list_for_each_entry(arvif, &ar->arvifs, list) {
+- if (!arvif->beacon)
+- continue;
++ mutex_unlock(&ar->conf_mutex);
+
+- dma_unmap_single(arvif->ar->dev,
+- ATH10K_SKB_CB(arvif->beacon)->paddr,
+- arvif->beacon->len, DMA_TO_DEVICE);
+- dev_kfree_skb_any(arvif->beacon);
+- arvif->beacon = NULL;
++ return 0;
++}
++
++static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
++{
++ /* It is not clear that allowing gaps in chainmask
++ * is helpful. Probably it will not do what user
++ * is hoping for, so warn in that case.
++ */
++ if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0)
++ return;
++
++ ath10k_warn(ar, "mac %s antenna chainmask may be invalid: 0x%x. Suggested values: 15, 7, 3, 1 or 0.\n",
++ dbg, cm);
++}
++
++static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
++{
++ int ret;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ ath10k_check_chain_mask(ar, tx_ant, "tx");
++ ath10k_check_chain_mask(ar, rx_ant, "rx");
++
++ ar->cfg_tx_chainmask = tx_ant;
++ ar->cfg_rx_chainmask = rx_ant;
++
++ if ((ar->state != ATH10K_STATE_ON) &&
++ (ar->state != ATH10K_STATE_RESTARTED))
++ return 0;
++
++ ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask,
++ tx_ant);
++ if (ret) {
++ ath10k_warn(ar, "failed to set tx-chainmask: %d, req 0x%x\n",
++ ret, tx_ant);
++ return ret;
+ }
+- spin_unlock_bh(&ar->data_lock);
++
++ ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask,
++ rx_ant);
++ if (ret) {
++ ath10k_warn(ar, "failed to set rx-chainmask: %d, req 0x%x\n",
++ ret, rx_ant);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
++{
++ struct ath10k *ar = hw->priv;
++ int ret;
++
++ mutex_lock(&ar->conf_mutex);
++ ret = __ath10k_set_antenna(ar, tx_ant, rx_ant);
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
+ }
+
+ static int ath10k_start(struct ieee80211_hw *hw)
+@@ -2334,41 +2857,61 @@ static int ath10k_start(struct ieee80211
+ struct ath10k *ar = hw->priv;
+ int ret = 0;
+
++ /*
++ * This makes sense only when restarting hw. It is harmless to call
++ * uncoditionally. This is necessary to make sure no HTT/WMI tx
++ * commands will be submitted while restarting.
++ */
++ ath10k_drain_tx(ar);
++
+ mutex_lock(&ar->conf_mutex);
+
+- if (ar->state != ATH10K_STATE_OFF &&
+- ar->state != ATH10K_STATE_RESTARTING) {
++ switch (ar->state) {
++ case ATH10K_STATE_OFF:
++ ar->state = ATH10K_STATE_ON;
++ break;
++ case ATH10K_STATE_RESTARTING:
++ ath10k_halt(ar);
++ ar->state = ATH10K_STATE_RESTARTED;
++ break;
++ case ATH10K_STATE_ON:
++ case ATH10K_STATE_RESTARTED:
++ case ATH10K_STATE_WEDGED:
++ WARN_ON(1);
+ ret = -EINVAL;
+- goto exit;
++ goto err;
++ case ATH10K_STATE_UTF:
++ ret = -EBUSY;
++ goto err;
+ }
+
+ ret = ath10k_hif_power_up(ar);
+ if (ret) {
+- ath10k_err("Could not init hif: %d\n", ret);
+- ar->state = ATH10K_STATE_OFF;
+- goto exit;
++ ath10k_err(ar, "Could not init hif: %d\n", ret);
++ goto err_off;
+ }
+
+- ret = ath10k_core_start(ar);
++ ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
+ if (ret) {
+- ath10k_err("Could not init core: %d\n", ret);
+- ath10k_hif_power_down(ar);
+- ar->state = ATH10K_STATE_OFF;
+- goto exit;
++ ath10k_err(ar, "Could not init core: %d\n", ret);
++ goto err_power_down;
+ }
+
+- 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, ar->wmi.pdev_param->pmf_qos, 1);
+- if (ret)
+- ath10k_warn("failed to enable PMF QOS: %d\n", ret);
++ if (ret) {
++ ath10k_warn(ar, "failed to enable PMF QOS: %d\n", ret);
++ goto err_core_stop;
++ }
+
+ ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1);
+- if (ret)
+- ath10k_warn("failed to enable dynamic BW: %d\n", ret);
++ if (ret) {
++ ath10k_warn(ar, "failed to enable dynamic BW: %d\n", ret);
++ goto err_core_stop;
++ }
++
++ if (ar->cfg_tx_chainmask)
++ __ath10k_set_antenna(ar, ar->cfg_tx_chainmask,
++ ar->cfg_rx_chainmask);
+
+ /*
+ * By default FW set ARP frames ac to voice (6). In that case ARP
+@@ -2382,16 +2925,29 @@ static int ath10k_start(struct ieee80211
+ ret = ath10k_wmi_pdev_set_param(ar,
+ ar->wmi.pdev_param->arp_ac_override, 0);
+ if (ret) {
+- ath10k_warn("failed to set arp ac override parameter: %d\n",
++ ath10k_warn(ar, "failed to set arp ac override parameter: %d\n",
+ ret);
+- goto exit;
++ goto err_core_stop;
+ }
+
+ ar->num_started_vdevs = 0;
+ ath10k_regd_update(ar);
+- ret = 0;
+
+-exit:
++ ath10k_spectral_start(ar);
++
++ mutex_unlock(&ar->conf_mutex);
++ return 0;
++
++err_core_stop:
++ ath10k_core_stop(ar);
++
++err_power_down:
++ ath10k_hif_power_down(ar);
++
++err_off:
++ ar->state = ATH10K_STATE_OFF;
++
++err:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+ }
+@@ -2400,19 +2956,16 @@ static void ath10k_stop(struct ieee80211
+ {
+ struct ath10k *ar = hw->priv;
+
++ ath10k_drain_tx(ar);
++
+ mutex_lock(&ar->conf_mutex);
+- if (ar->state == ATH10K_STATE_ON ||
+- ar->state == ATH10K_STATE_RESTARTED ||
+- ar->state == ATH10K_STATE_WEDGED)
++ if (ar->state != ATH10K_STATE_OFF) {
+ ath10k_halt(ar);
+-
+- ar->state = ATH10K_STATE_OFF;
++ ar->state = ATH10K_STATE_OFF;
++ }
+ mutex_unlock(&ar->conf_mutex);
+
+- ath10k_mgmt_over_wmi_tx_purge(ar);
+-
+- cancel_work_sync(&ar->offchan_tx_work);
+- cancel_work_sync(&ar->wmi_mgmt_tx_work);
++ cancel_delayed_work_sync(&ar->scan.timeout);
+ cancel_work_sync(&ar->restart_work);
+ }
+
+@@ -2426,7 +2979,7 @@ static int ath10k_config_ps(struct ath10
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ ret = ath10k_mac_vif_setup_ps(arvif);
+ if (ret) {
+- ath10k_warn("failed to setup powersave: %d\n", ret);
++ ath10k_warn(ar, "failed to setup powersave: %d\n", ret);
+ break;
+ }
+ }
+@@ -2464,7 +3017,7 @@ static void ath10k_config_chan(struct at
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n",
+ ar->chandef.chan->center_freq,
+ ar->chandef.center_freq1,
+@@ -2474,24 +3027,27 @@ static void ath10k_config_chan(struct at
+ /* First stop monitor interface. Some FW versions crash if there's a
+ * lone monitor interface. */
+ if (ar->monitor_started)
+- ath10k_monitor_vdev_stop(ar);
++ ath10k_monitor_stop(ar);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (!arvif->is_started)
+ continue;
+
++ if (!arvif->is_up)
++ continue;
++
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+ continue;
+
+- ret = ath10k_vdev_stop(arvif);
++ ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+ if (ret) {
+- ath10k_warn("failed to stop vdev %d: %d\n",
++ ath10k_warn(ar, "failed to down vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ continue;
+ }
+ }
+
+- /* all vdevs are now stopped - now attempt to restart them */
++ /* all vdevs are downed now - attempt to restart and re-up them */
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (!arvif->is_started)
+@@ -2500,9 +3056,9 @@ static void ath10k_config_chan(struct at
+ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+ continue;
+
+- ret = ath10k_vdev_start(arvif);
++ ret = ath10k_vdev_restart(arvif);
+ if (ret) {
+- ath10k_warn("failed to start vdev %d: %d\n",
++ ath10k_warn(ar, "failed to restart vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ continue;
+ }
+@@ -2513,14 +3069,70 @@ static void ath10k_config_chan(struct at
+ ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+ arvif->bssid);
+ if (ret) {
+- ath10k_warn("failed to bring vdev up %d: %d\n",
++ ath10k_warn(ar, "failed to bring vdev up %d: %d\n",
+ arvif->vdev_id, ret);
+ continue;
+ }
+ }
+
+- if (ath10k_monitor_is_enabled(ar))
+- ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
++ ath10k_monitor_recalc(ar);
++}
++
++static int ath10k_mac_txpower_setup(struct ath10k *ar, int txpower)
++{
++ int ret;
++ u32 param;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac txpower %d\n", txpower);
++
++ param = ar->wmi.pdev_param->txpower_limit2g;
++ ret = ath10k_wmi_pdev_set_param(ar, param, txpower * 2);
++ if (ret) {
++ ath10k_warn(ar, "failed to set 2g txpower %d: %d\n",
++ txpower, ret);
++ return ret;
++ }
++
++ param = ar->wmi.pdev_param->txpower_limit5g;
++ ret = ath10k_wmi_pdev_set_param(ar, param, txpower * 2);
++ if (ret) {
++ ath10k_warn(ar, "failed to set 5g txpower %d: %d\n",
++ txpower, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int ath10k_mac_txpower_recalc(struct ath10k *ar)
++{
++ struct ath10k_vif *arvif;
++ int ret, txpower = -1;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ list_for_each_entry(arvif, &ar->arvifs, list) {
++ WARN_ON(arvif->txpower < 0);
++
++ if (txpower == -1)
++ txpower = arvif->txpower;
++ else
++ txpower = min(txpower, arvif->txpower);
++ }
++
++ if (WARN_ON(txpower == -1))
++ return -EINVAL;
++
++ ret = ath10k_mac_txpower_setup(ar, txpower);
++ if (ret) {
++ ath10k_warn(ar, "failed to setup tx power %d: %d\n",
++ txpower, ret);
++ return ret;
++ }
++
++ return 0;
+ }
+
+ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+@@ -2528,12 +3140,11 @@ static int ath10k_config(struct ieee8021
+ struct ath10k *ar = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ int ret = 0;
+- u32 param;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac config channel %dMHz flags 0x%x radar %d\n",
+ conf->chandef.chan->center_freq,
+ conf->chandef.chan->flags,
+@@ -2552,48 +3163,31 @@ static int ath10k_config(struct ieee8021
+ }
+ }
+
+- if (changed & IEEE80211_CONF_CHANGE_POWER) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac config power %d\n",
+- hw->conf.power_level);
+-
+- param = ar->wmi.pdev_param->txpower_limit2g;
+- ret = ath10k_wmi_pdev_set_param(ar, param,
+- hw->conf.power_level * 2);
+- if (ret)
+- ath10k_warn("failed to set 2g txpower %d: %d\n",
+- hw->conf.power_level, ret);
+-
+- param = ar->wmi.pdev_param->txpower_limit5g;
+- ret = ath10k_wmi_pdev_set_param(ar, param,
+- hw->conf.power_level * 2);
+- if (ret)
+- ath10k_warn("failed to set 5g txpower %d: %d\n",
+- hw->conf.power_level, ret);
+- }
+-
+ if (changed & IEEE80211_CONF_CHANGE_PS)
+ ath10k_config_ps(ar);
+
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+- if (conf->flags & IEEE80211_CONF_MONITOR && !ar->monitor) {
+- ar->monitor = true;
+- ret = ath10k_monitor_start(ar);
+- if (ret) {
+- ath10k_warn("failed to start monitor (config): %d\n",
+- ret);
+- ar->monitor = false;
+- }
+- } else if (!(conf->flags & IEEE80211_CONF_MONITOR) &&
+- ar->monitor) {
+- ar->monitor = false;
+- ath10k_monitor_stop(ar);
+- }
++ ar->monitor = conf->flags & IEEE80211_CONF_MONITOR;
++ ret = ath10k_monitor_recalc(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+ }
+
++static u32 get_nss_from_chainmask(u16 chain_mask)
++{
++ if ((chain_mask & 0x15) == 0x15)
++ return 4;
++ else if ((chain_mask & 0x7) == 0x7)
++ return 3;
++ else if ((chain_mask & 0x3) == 0x3)
++ return 2;
++ return 1;
++}
++
+ /*
+ * TODO:
+ * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
+@@ -2619,22 +3213,26 @@ static int ath10k_add_interface(struct i
+ arvif->ar = ar;
+ arvif->vif = vif;
+
+- INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
+ INIT_LIST_HEAD(&arvif->list);
+
+- bit = ffs(ar->free_vdev_map);
+- if (bit == 0) {
++ if (ar->free_vdev_map == 0) {
++ ath10k_warn(ar, "Free vdev map is empty, no more interfaces allowed.\n");
+ ret = -EBUSY;
+ goto err;
+ }
++ bit = __ffs64(ar->free_vdev_map);
+
+- arvif->vdev_id = bit - 1;
+- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac create vdev %i map %llx\n",
++ bit, ar->free_vdev_map);
+
+- if (ar->p2p)
+- arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
++ arvif->vdev_id = bit;
++ arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
+
+ switch (vif->type) {
++ case NL80211_IFTYPE_P2P_DEVICE:
++ arvif->vdev_type = WMI_VDEV_TYPE_STA;
++ arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
++ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ arvif->vdev_type = WMI_VDEV_TYPE_STA;
+@@ -2658,50 +3256,98 @@ static int ath10k_add_interface(struct i
+ break;
+ }
+
+- 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);
++ /* Some firmware revisions don't wait for beacon tx completion before
++ * sending another SWBA event. This could lead to hardware using old
++ * (freed) beacon data in some cases, e.g. tx credit starvation
++ * combined with missed TBTT. This is very very rare.
++ *
++ * On non-IOMMU-enabled hosts this could be a possible security issue
++ * because hw could beacon some random data on the air. On
++ * IOMMU-enabled hosts DMAR faults would occur in most cases and target
++ * device would crash.
++ *
++ * Since there are no beacon tx completions (implicit nor explicit)
++ * propagated to host the only workaround for this is to allocate a
++ * DMA-coherent buffer for a lifetime of a vif and use it for all
++ * beacon tx commands. Worst case for this approach is some beacons may
++ * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap.
++ */
++ if (vif->type == NL80211_IFTYPE_ADHOC ||
++ vif->type == NL80211_IFTYPE_AP) {
++ arvif->beacon_buf = dma_zalloc_coherent(ar->dev,
++ IEEE80211_MAX_FRAME_LEN,
++ &arvif->beacon_paddr,
++ GFP_ATOMIC);
++ if (!arvif->beacon_buf) {
++ ret = -ENOMEM;
++ ath10k_warn(ar, "failed to allocate beacon buffer: %d\n",
++ ret);
++ goto err;
++ }
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d bcnmode %s\n",
++ arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype,
++ arvif->beacon_buf ? "single-buf" : "per-skb");
+
+ ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
+ arvif->vdev_subtype, vif->addr);
+ if (ret) {
+- ath10k_warn("failed to create WMI vdev %i: %d\n",
++ ath10k_warn(ar, "failed to create WMI vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ goto err;
+ }
+
+- ar->free_vdev_map &= ~BIT(arvif->vdev_id);
++ ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
+ list_add(&arvif->list, &ar->arvifs);
+
+- vdev_param = ar->wmi.vdev_param->def_keyid;
+- ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
+- arvif->def_wep_key_idx);
++ /* It makes no sense to have firmware do keepalives. mac80211 already
++ * takes care of this with idle connection polling.
++ */
++ ret = ath10k_mac_vif_disable_keepalive(arvif);
+ if (ret) {
+- ath10k_warn("failed to set vdev %i default key id: %d\n",
++ ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ goto err_vdev_delete;
+ }
+
++ arvif->def_wep_key_idx = -1;
++
+ vdev_param = ar->wmi.vdev_param->tx_encap_type;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ ATH10K_HW_TXRX_NATIVE_WIFI);
+ /* 10.X firmware does not support this VDEV parameter. Do not warn */
+ if (ret && ret != -EOPNOTSUPP) {
+- ath10k_warn("failed to set vdev %i TX encapsulation: %d\n",
++ ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n",
+ arvif->vdev_id, ret);
+ goto err_vdev_delete;
+ }
+
++ if (ar->cfg_tx_chainmask) {
++ u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
++
++ vdev_param = ar->wmi.vdev_param->nss;
++ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
++ nss);
++ if (ret) {
++ ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
++ arvif->vdev_id, ar->cfg_tx_chainmask, nss,
++ ret);
++ goto err_vdev_delete;
++ }
++ }
++
+ if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+ ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
+ if (ret) {
+- ath10k_warn("failed to create vdev %i peer for AP: %d\n",
++ ath10k_warn(ar, "failed to create vdev %i peer for AP: %d\n",
+ arvif->vdev_id, ret);
+ goto err_vdev_delete;
+ }
+
+ ret = ath10k_mac_set_kickout(arvif);
+ if (ret) {
+- ath10k_warn("failed to set vdev %i kickout parameters: %d\n",
++ ath10k_warn(ar, "failed to set vdev %i kickout parameters: %d\n",
+ arvif->vdev_id, ret);
+ goto err_peer_delete;
+ }
+@@ -2713,27 +3359,21 @@ static int ath10k_add_interface(struct i
+ ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+ param, value);
+ if (ret) {
+- ath10k_warn("failed to set vdev %i RX wake policy: %d\n",
++ ath10k_warn(ar, "failed to set vdev %i RX wake policy: %d\n",
+ arvif->vdev_id, ret);
+ goto err_peer_delete;
+ }
+
+- param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
+- value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+- ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+- param, value);
++ ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
+ if (ret) {
+- ath10k_warn("failed to set vdev %i TX wake thresh: %d\n",
++ ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ goto err_peer_delete;
+ }
+
+- param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
+- value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
+- ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+- param, value);
++ ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
+ if (ret) {
+- ath10k_warn("failed to set vdev %i PSPOLL count: %d\n",
++ ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ goto err_peer_delete;
+ }
+@@ -2741,15 +3381,22 @@ static int ath10k_add_interface(struct i
+
+ ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
+ if (ret) {
+- ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n",
++ arvif->vdev_id, ret);
++ goto err_peer_delete;
++ }
++
++ ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
++ if (ret) {
++ ath10k_warn(ar, "failed to set frag threshold for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ goto err_peer_delete;
+ }
+
+- ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
++ arvif->txpower = vif->bss_conf.txpower;
++ ret = ath10k_mac_txpower_recalc(ar);
+ if (ret) {
+- ath10k_warn("failed to set frag threshold for vdev %d: %d\n",
+- arvif->vdev_id, ret);
++ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
+ goto err_peer_delete;
+ }
+
+@@ -2762,10 +3409,16 @@ err_peer_delete:
+
+ err_vdev_delete:
+ ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+- ar->free_vdev_map &= ~BIT(arvif->vdev_id);
++ ar->free_vdev_map |= 1LL << arvif->vdev_id;
+ list_del(&arvif->list);
+
+ err:
++ if (arvif->beacon_buf) {
++ dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
++ arvif->beacon_buf, arvif->beacon_paddr);
++ arvif->beacon_buf = NULL;
++ }
++
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+@@ -2780,38 +3433,51 @@ static void ath10k_remove_interface(stru
+
+ mutex_lock(&ar->conf_mutex);
+
+- cancel_work_sync(&arvif->wep_key_work);
+-
+ spin_lock_bh(&ar->data_lock);
+- if (arvif->beacon) {
+- dma_unmap_single(arvif->ar->dev,
+- ATH10K_SKB_CB(arvif->beacon)->paddr,
+- arvif->beacon->len, DMA_TO_DEVICE);
+- dev_kfree_skb_any(arvif->beacon);
+- arvif->beacon = NULL;
+- }
++ ath10k_mac_vif_beacon_cleanup(arvif);
+ spin_unlock_bh(&ar->data_lock);
+
+- ar->free_vdev_map |= 1 << (arvif->vdev_id);
++ ret = ath10k_spectral_vif_stop(arvif);
++ if (ret)
++ ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
++ arvif->vdev_id, ret);
++
++ ar->free_vdev_map |= 1LL << arvif->vdev_id;
+ list_del(&arvif->list);
+
+ if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+- ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
++ ret = ath10k_wmi_peer_delete(arvif->ar, arvif->vdev_id,
++ vif->addr);
+ if (ret)
+- ath10k_warn("failed to remove peer for AP vdev %i: %d\n",
++ ath10k_warn(ar, "failed to submit AP self-peer removal on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+
+ kfree(arvif->u.ap.noa_data);
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n",
+ arvif->vdev_id);
+
+ ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+ if (ret)
+- ath10k_warn("failed to delete WMI vdev %i: %d\n",
++ ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
+ arvif->vdev_id, ret);
+
++ /* Some firmware revisions don't notify host about self-peer removal
++ * until after associated vdev is deleted.
++ */
++ if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
++ ret = ath10k_wait_for_peer_deleted(ar, arvif->vdev_id,
++ vif->addr);
++ if (ret)
++ ath10k_warn(ar, "failed to remove AP self-peer on vdev %i: %d\n",
++ arvif->vdev_id, ret);
++
++ spin_lock_bh(&ar->data_lock);
++ ar->num_peers--;
++ spin_unlock_bh(&ar->data_lock);
++ }
++
+ ath10k_peer_cleanup(ar, arvif->vdev_id);
+
+ mutex_unlock(&ar->conf_mutex);
+@@ -2844,18 +3510,9 @@ static void ath10k_configure_filter(stru
+ *total_flags &= SUPPORTED_FILTERS;
+ ar->filter_flags = *total_flags;
+
+- if (ar->filter_flags & FIF_PROMISC_IN_BSS && !ar->promisc) {
+- ar->promisc = true;
+- ret = ath10k_monitor_start(ar);
+- if (ret) {
+- ath10k_warn("failed to start monitor (promisc): %d\n",
+- ret);
+- ar->promisc = false;
+- }
+- } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && ar->promisc) {
+- ar->promisc = false;
+- ath10k_monitor_stop(ar);
+- }
++ ret = ath10k_monitor_recalc(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to recalc montior: %d\n", ret);
+
+ mutex_unlock(&ar->conf_mutex);
+ }
+@@ -2868,7 +3525,7 @@ static void ath10k_bss_info_changed(stru
+ struct ath10k *ar = hw->priv;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ int ret = 0;
+- u32 vdev_param, pdev_param;
++ u32 vdev_param, pdev_param, slottime, preamble;
+
+ mutex_lock(&ar->conf_mutex);
+
+@@ -2880,17 +3537,17 @@ static void ath10k_bss_info_changed(stru
+ vdev_param = ar->wmi.vdev_param->beacon_interval;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ arvif->beacon_interval);
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, 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: %i\n",
++ ath10k_warn(ar, "failed to set beacon interval for vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ }
+
+ if (changed & BSS_CHANGED_BEACON) {
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "vdev %d set beacon tx mode to staggered\n",
+ arvif->vdev_id);
+
+@@ -2898,14 +3555,26 @@ static void ath10k_bss_info_changed(stru
+ ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
+ WMI_BEACON_STAGGERED_MODE);
+ if (ret)
+- ath10k_warn("failed to set beacon mode for vdev %d: %i\n",
++ ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n",
++ arvif->vdev_id, ret);
++
++ ret = ath10k_mac_setup_bcn_tmpl(arvif);
++ if (ret)
++ ath10k_warn(ar, "failed to update beacon template: %d\n",
++ ret);
++ }
++
++ if (changed & BSS_CHANGED_AP_PROBE_RESP) {
++ ret = ath10k_mac_setup_prb_tmpl(arvif);
++ if (ret)
++ ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ }
+
+- if (changed & BSS_CHANGED_BEACON_INFO) {
++ if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
+ arvif->dtim_period = info->dtim_period;
+
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d dtim_period %d\n",
+ arvif->vdev_id, arvif->dtim_period);
+
+@@ -2913,7 +3582,7 @@ static void ath10k_bss_info_changed(stru
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ arvif->dtim_period);
+ if (ret)
+- ath10k_warn("failed to set dtim period for vdev %d: %i\n",
++ ath10k_warn(ar, "failed to set dtim period for vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ }
+
+@@ -2925,91 +3594,48 @@ static void ath10k_bss_info_changed(stru
+ arvif->u.ap.hidden_ssid = info->hidden_ssid;
+ }
+
+- 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 when changing bssid: %i\n",
+- info->bssid, arvif->vdev_id, ret);
+-
+- if (vif->type == NL80211_IFTYPE_STATION) {
+- /*
+- * this is never erased as we it for crypto key
+- * clearing; this is FW requirement
+- */
+- memcpy(arvif->bssid, info->bssid, ETH_ALEN);
+-
+- ath10k_dbg(ATH10K_DBG_MAC,
+- "mac vdev %d start %pM\n",
+- arvif->vdev_id, info->bssid);
+-
+- ret = ath10k_vdev_start(arvif);
+- if (ret) {
+- ath10k_warn("failed to start vdev %i: %d\n",
+- arvif->vdev_id, ret);
+- goto exit;
+- }
+-
+- arvif->is_started = true;
+- }
+-
+- /*
+- * Mac80211 does not keep IBSS bssid when leaving IBSS,
+- * so driver need to store it. It is needed when leaving
+- * IBSS in order to remove BSSID peer.
+- */
+- if (vif->type == NL80211_IFTYPE_ADHOC)
+- memcpy(arvif->bssid, info->bssid,
+- ETH_ALEN);
+- }
+- }
++ if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid))
++ ether_addr_copy(arvif->bssid, info->bssid);
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED)
+ ath10k_control_beaconing(arvif, info);
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ arvif->use_cts_prot = info->use_cts_prot;
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
+ arvif->vdev_id, info->use_cts_prot);
+
+ ret = ath10k_recalc_rtscts_prot(arvif);
+ if (ret)
+- ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+- u32 slottime;
+ if (info->use_short_slot)
+ slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
+
+ else
+ slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n",
+ arvif->vdev_id, slottime);
+
+ vdev_param = ar->wmi.vdev_param->slot_time;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ slottime);
+ if (ret)
+- ath10k_warn("failed to set erp slot for vdev %d: %i\n",
++ ath10k_warn(ar, "failed to set erp slot for vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ }
+
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+- u32 preamble;
+ if (info->use_short_preamble)
+ preamble = WMI_VDEV_PREAMBLE_SHORT;
+ else
+ preamble = WMI_VDEV_PREAMBLE_LONG;
+
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d preamble %dn",
+ arvif->vdev_id, preamble);
+
+@@ -3017,16 +3643,44 @@ static void ath10k_bss_info_changed(stru
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ preamble);
+ if (ret)
+- ath10k_warn("failed to set preamble for vdev %d: %i\n",
++ ath10k_warn(ar, "failed to set preamble for vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ }
+
+ if (changed & BSS_CHANGED_ASSOC) {
+- if (info->assoc)
++ if (info->assoc) {
++ /* Workaround: Make sure monitor vdev is not running
++ * when associating to prevent some firmware revisions
++ * (e.g. 10.1 and 10.2) from crashing.
++ */
++ if (ar->monitor_started)
++ ath10k_monitor_stop(ar);
+ ath10k_bss_assoc(hw, vif, info);
++ ath10k_monitor_recalc(ar);
++ } else {
++ ath10k_bss_disassoc(hw, vif);
++ }
++ }
++
++ if (changed & BSS_CHANGED_TXPOWER) {
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev_id %i txpower %d\n",
++ arvif->vdev_id, info->txpower);
++
++ arvif->txpower = info->txpower;
++ ret = ath10k_mac_txpower_recalc(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
++ }
++
++ if (changed & BSS_CHANGED_PS) {
++ arvif->ps = vif->bss_conf.ps;
++
++ ret = ath10k_config_ps(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
++ arvif->vdev_id, ret);
+ }
+
+-exit:
+ mutex_unlock(&ar->conf_mutex);
+ }
+
+@@ -3043,20 +3697,26 @@ static int ath10k_hw_scan(struct ieee802
+ mutex_lock(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+- if (ar->scan.in_progress) {
+- spin_unlock_bh(&ar->data_lock);
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ reinit_completion(&ar->scan.started);
++ reinit_completion(&ar->scan.completed);
++ ar->scan.state = ATH10K_SCAN_STARTING;
++ ar->scan.is_roc = false;
++ ar->scan.vdev_id = arvif->vdev_id;
++ ret = 0;
++ break;
++ case ATH10K_SCAN_STARTING:
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
+ ret = -EBUSY;
+- goto exit;
++ break;
+ }
+-
+- reinit_completion(&ar->scan.started);
+- reinit_completion(&ar->scan.completed);
+- ar->scan.in_progress = true;
+- ar->scan.aborting = false;
+- ar->scan.is_roc = false;
+- ar->scan.vdev_id = arvif->vdev_id;
+ spin_unlock_bh(&ar->data_lock);
+
++ if (ret)
++ goto exit;
++
+ memset(&arg, 0, sizeof(arg));
+ ath10k_wmi_start_scan_init(ar, &arg);
+ arg.vdev_id = arvif->vdev_id;
+@@ -3088,9 +3748,9 @@ static int ath10k_hw_scan(struct ieee802
+
+ ret = ath10k_start_scan(ar, &arg);
+ if (ret) {
+- ath10k_warn("failed to start hw scan: %d\n", ret);
++ ath10k_warn(ar, "failed to start hw scan: %d\n", ret);
+ spin_lock_bh(&ar->data_lock);
+- ar->scan.in_progress = false;
++ ar->scan.state = ATH10K_SCAN_IDLE;
+ spin_unlock_bh(&ar->data_lock);
+ }
+
+@@ -3103,15 +3763,12 @@ static void ath10k_cancel_hw_scan(struct
+ struct ieee80211_vif *vif)
+ {
+ struct ath10k *ar = hw->priv;
+- int ret;
+
+ mutex_lock(&ar->conf_mutex);
+- ret = ath10k_abort_scan(ar);
+- if (ret) {
+- ath10k_warn("failed to abort scan: %d\n", ret);
+- ieee80211_scan_completed(hw, 1 /* aborted */);
+- }
++ ath10k_scan_abort(ar);
+ mutex_unlock(&ar->conf_mutex);
++
++ cancel_delayed_work_sync(&ar->scan.timeout);
+ }
+
+ static void ath10k_set_key_h_def_keyidx(struct ath10k *ar,
+@@ -3148,7 +3805,7 @@ static void ath10k_set_key_h_def_keyidx(
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ key->keyidx);
+ if (ret)
+- ath10k_warn("failed to set vdev %i group key as default key: %d\n",
++ ath10k_warn(ar, "failed to set vdev %i group key as default key: %d\n",
+ arvif->vdev_id, ret);
+ }
+
+@@ -3162,6 +3819,7 @@ static int ath10k_set_key(struct ieee802
+ const u8 *peer_addr;
+ bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104;
++ bool def_idx = false;
+ int ret = 0;
+
+ if (key->keyidx > WMI_MAX_KEY_INDEX)
+@@ -3186,7 +3844,7 @@ static int ath10k_set_key(struct ieee802
+
+ if (!peer) {
+ if (cmd == SET_KEY) {
+- ath10k_warn("failed to install key for non-existent peer %pM\n",
++ ath10k_warn(ar, "failed to install key for non-existent peer %pM\n",
+ peer_addr);
+ ret = -EOPNOTSUPP;
+ goto exit;
+@@ -3207,9 +3865,16 @@ static int ath10k_set_key(struct ieee802
+ ath10k_clear_vdev_key(arvif, key);
+ }
+
+- ret = ath10k_install_key(arvif, key, cmd, peer_addr);
++ /* set TX_USAGE flag for all the keys incase of dot1x-WEP. For
++ * static WEP, do not set this flag for the keys whose key id
++ * is greater than default key id.
++ */
++ if (arvif->def_wep_key_idx == -1)
++ def_idx = true;
++
++ ret = ath10k_install_key(arvif, key, cmd, peer_addr, def_idx);
+ if (ret) {
+- ath10k_warn("failed to install key for vdev %i peer %pM: %d\n",
++ ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
+ arvif->vdev_id, peer_addr, ret);
+ goto exit;
+ }
+@@ -3224,7 +3889,7 @@ static int ath10k_set_key(struct ieee802
+ peer->keys[key->keyidx] = NULL;
+ else if (peer == NULL)
+ /* impossible unless FW goes crazy */
+- ath10k_warn("Peer %pM disappeared!\n", peer_addr);
++ ath10k_warn(ar, "Peer %pM disappeared!\n", peer_addr);
+ spin_unlock_bh(&ar->data_lock);
+
+ exit:
+@@ -3232,6 +3897,39 @@ exit:
+ return ret;
+ }
+
++static void ath10k_set_default_unicast_key(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ int keyidx)
++{
++ struct ath10k *ar = hw->priv;
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++ int ret;
++
++ mutex_lock(&arvif->ar->conf_mutex);
++
++ if (arvif->ar->state != ATH10K_STATE_ON)
++ goto unlock;
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
++ arvif->vdev_id, keyidx);
++
++ ret = ath10k_wmi_vdev_set_param(arvif->ar,
++ arvif->vdev_id,
++ arvif->ar->wmi.vdev_param->def_keyid,
++ keyidx);
++
++ if (ret) {
++ ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n",
++ arvif->vdev_id,
++ ret);
++ goto unlock;
++ }
++
++ arvif->def_wep_key_idx = keyidx;
++unlock:
++ mutex_unlock(&arvif->ar->conf_mutex);
++}
++
+ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
+ {
+ struct ath10k *ar;
+@@ -3260,51 +3958,83 @@ static void ath10k_sta_rc_update_wk(stru
+ mutex_lock(&ar->conf_mutex);
+
+ if (changed & IEEE80211_RC_BW_CHANGED) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n",
+ sta->addr, bw);
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_CHAN_WIDTH, bw);
+ if (err)
+- ath10k_warn("failed to update STA %pM peer bw %d: %d\n",
++ ath10k_warn(ar, "failed to update STA %pM peer bw %d: %d\n",
+ sta->addr, bw, err);
+ }
+
+ if (changed & IEEE80211_RC_NSS_CHANGED) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM nss %d\n",
+ sta->addr, nss);
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_NSS, nss);
+ if (err)
+- ath10k_warn("failed to update STA %pM nss %d: %d\n",
++ ath10k_warn(ar, "failed to update STA %pM nss %d: %d\n",
+ sta->addr, nss, err);
+ }
+
+ if (changed & IEEE80211_RC_SMPS_CHANGED) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM smps %d\n",
+ sta->addr, smps);
+
+ err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+ WMI_PEER_SMPS_STATE, smps);
+ if (err)
+- ath10k_warn("failed to update STA %pM smps %d: %d\n",
++ ath10k_warn(ar, "failed to update STA %pM smps %d: %d\n",
+ sta->addr, smps, err);
+ }
+
+- if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
++ if (changed & IEEE80211_RC_SUPP_RATES_CHANGED ||
++ changed & IEEE80211_RC_NSS_CHANGED) {
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n",
+ sta->addr);
+
+- err = ath10k_station_assoc(ar, arvif, sta, true);
++ err = ath10k_station_assoc(ar, arvif->vif, sta, true);
+ if (err)
+- ath10k_warn("failed to reassociate station: %pM\n",
++ ath10k_warn(ar, "failed to reassociate station: %pM\n",
+ sta->addr);
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+ }
+
++static int ath10k_mac_inc_num_stations(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
++ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
++ return 0;
++
++ if (ar->num_stations >= ar->max_num_stations)
++ return -ENOBUFS;
++
++ ar->num_stations++;
++
++ return 0;
++}
++
++static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif)
++{
++ struct ath10k *ar = arvif->ar;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
++ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
++ return;
++
++ ar->num_stations--;
++}
++
+ static int ath10k_sta_state(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+@@ -3314,7 +4044,6 @@ static int ath10k_sta_state(struct ieee8
+ struct ath10k *ar = hw->priv;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+- int max_num_peers;
+ int ret = 0;
+
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+@@ -3332,46 +4061,72 @@ static int ath10k_sta_state(struct ieee8
+ mutex_lock(&ar->conf_mutex);
+
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+- new_state == IEEE80211_STA_NONE &&
+- vif->type != NL80211_IFTYPE_STATION) {
++ new_state == IEEE80211_STA_NONE) {
+ /*
+ * New station addition.
+ */
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+- max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
+- else
+- max_num_peers = TARGET_NUM_PEERS;
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
++ "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
++ arvif->vdev_id, sta->addr,
++ ar->num_stations + 1, ar->max_num_stations,
++ ar->num_peers + 1, ar->max_num_peers);
+
+- if (ar->num_peers >= max_num_peers) {
+- ath10k_warn("number of peers exceeded: peers number %d (max peers %d)\n",
+- ar->num_peers, max_num_peers);
+- ret = -ENOBUFS;
++ ret = ath10k_mac_inc_num_stations(arvif);
++ if (ret) {
++ ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
++ ar->max_num_stations);
+ goto exit;
+ }
+
+- ath10k_dbg(ATH10K_DBG_MAC,
+- "mac vdev %d peer create %pM (new sta) num_peers %d\n",
+- arvif->vdev_id, sta->addr, ar->num_peers);
+-
+ ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
+- if (ret)
+- ath10k_warn("failed to add peer %pM for vdev %d when adding a new sta: %i\n",
++ 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);
++ ath10k_mac_dec_num_stations(arvif);
++ goto exit;
++ }
++
++ if (vif->type == NL80211_IFTYPE_STATION) {
++ WARN_ON(arvif->is_started);
++
++ ret = ath10k_vdev_start(arvif);
++ if (ret) {
++ ath10k_warn(ar, "failed to start vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ WARN_ON(ath10k_peer_delete(ar, arvif->vdev_id,
++ sta->addr));
++ ath10k_mac_dec_num_stations(arvif);
++ goto exit;
++ }
++
++ arvif->is_started = true;
++ }
+ } else if ((old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST)) {
+ /*
+ * Existing station deletion.
+ */
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d peer delete %pM (sta gone)\n",
+ arvif->vdev_id, sta->addr);
++
++ if (vif->type == NL80211_IFTYPE_STATION) {
++ WARN_ON(!arvif->is_started);
++
++ ret = ath10k_vdev_stop(arvif);
++ if (ret)
++ ath10k_warn(ar, "failed to stop vdev %i: %d\n",
++ arvif->vdev_id, ret);
++
++ arvif->is_started = false;
++ }
++
+ ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+ if (ret)
+- ath10k_warn("failed to delete peer %pM for vdev %d: %i\n",
++ ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
+ sta->addr, arvif->vdev_id, ret);
+
+- if (vif->type == NL80211_IFTYPE_STATION)
+- ath10k_bss_disassoc(hw, vif);
++ ath10k_mac_dec_num_stations(arvif);
+ } else if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC &&
+ (vif->type == NL80211_IFTYPE_AP ||
+@@ -3379,12 +4134,12 @@ static int ath10k_sta_state(struct ieee8
+ /*
+ * New association.
+ */
+- ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM associated\n",
+ sta->addr);
+
+- ret = ath10k_station_assoc(ar, arvif, sta, false);
++ ret = ath10k_station_assoc(ar, vif, sta, false);
+ if (ret)
+- ath10k_warn("failed to associate station %pM for vdev %i: %i\n",
++ ath10k_warn(ar, "failed to associate station %pM for vdev %i: %i\n",
+ sta->addr, arvif->vdev_id, ret);
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH &&
+@@ -3393,12 +4148,12 @@ static int ath10k_sta_state(struct ieee8
+ /*
+ * Disassociation.
+ */
+- ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
+ sta->addr);
+
+- ret = ath10k_station_disassoc(ar, arvif, sta);
++ ret = ath10k_station_disassoc(ar, vif, sta);
+ if (ret)
+- ath10k_warn("failed to disassociate station: %pM vdev %i: %i\n",
++ ath10k_warn(ar, "failed to disassociate station: %pM vdev %i: %i\n",
+ sta->addr, arvif->vdev_id, ret);
+ }
+ exit:
+@@ -3407,9 +4162,11 @@ exit:
+ }
+
+ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
+- u16 ac, bool enable)
++ u16 ac, bool enable)
+ {
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++ struct wmi_sta_uapsd_auto_trig_arg arg = {};
++ u32 prio = 0, acc = 0;
+ u32 value = 0;
+ int ret = 0;
+
+@@ -3422,18 +4179,26 @@ static int ath10k_conf_tx_uapsd(struct a
+ case IEEE80211_AC_VO:
+ value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
+ WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
++ prio = 7;
++ acc = 3;
+ break;
+ case IEEE80211_AC_VI:
+ value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
+ WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
++ prio = 5;
++ acc = 2;
+ break;
+ case IEEE80211_AC_BE:
+ value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
+ WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
++ prio = 2;
++ acc = 1;
+ break;
+ case IEEE80211_AC_BK:
+ value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
+ WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
++ prio = 0;
++ acc = 0;
+ break;
+ }
+
+@@ -3446,7 +4211,7 @@ static int ath10k_conf_tx_uapsd(struct a
+ WMI_STA_PS_PARAM_UAPSD,
+ arvif->u.sta.uapsd);
+ if (ret) {
+- ath10k_warn("failed to set uapsd params: %d\n", ret);
++ ath10k_warn(ar, "failed to set uapsd params: %d\n", ret);
+ goto exit;
+ }
+
+@@ -3459,7 +4224,44 @@ static int ath10k_conf_tx_uapsd(struct a
+ WMI_STA_PS_PARAM_RX_WAKE_POLICY,
+ value);
+ if (ret)
+- ath10k_warn("failed to set rx wake param: %d\n", ret);
++ ath10k_warn(ar, "failed to set rx wake param: %d\n", ret);
++
++ ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
++ if (ret) {
++ ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++
++ ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
++ if (ret) {
++ ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ return ret;
++ }
++
++ if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) ||
++ test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) {
++ /* Only userspace can make an educated decision when to send
++ * trigger frame. The following effectively disables u-UAPSD
++ * autotrigger in firmware (which is enabled by default
++ * provided the autotrigger service is available).
++ */
++
++ arg.wmm_ac = acc;
++ arg.user_priority = prio;
++ arg.service_interval = 0;
++ arg.suspend_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
++ arg.delay_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC;
++
++ ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id,
++ arvif->bssid, &arg, 1);
++ if (ret) {
++ ath10k_warn(ar, "failed to set uapsd auto trigger %d\n",
++ ret);
++ return ret;
++ }
++ }
+
+ exit:
+ return ret;
+@@ -3470,6 +4272,7 @@ static int ath10k_conf_tx(struct ieee802
+ const struct ieee80211_tx_queue_params *params)
+ {
+ struct ath10k *ar = hw->priv;
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct wmi_wmm_params_arg *p = NULL;
+ int ret;
+
+@@ -3477,16 +4280,16 @@ static int ath10k_conf_tx(struct ieee802
+
+ switch (ac) {
+ case IEEE80211_AC_VO:
+- p = &ar->wmm_params.ac_vo;
++ p = &arvif->wmm_params.ac_vo;
+ break;
+ case IEEE80211_AC_VI:
+- p = &ar->wmm_params.ac_vi;
++ p = &arvif->wmm_params.ac_vi;
+ break;
+ case IEEE80211_AC_BE:
+- p = &ar->wmm_params.ac_be;
++ p = &arvif->wmm_params.ac_be;
+ break;
+ case IEEE80211_AC_BK:
+- p = &ar->wmm_params.ac_bk;
++ p = &arvif->wmm_params.ac_bk;
+ break;
+ }
+
+@@ -3506,16 +4309,28 @@ static int ath10k_conf_tx(struct ieee802
+ */
+ p->txop = params->txop * 32;
+
+- /* FIXME: FW accepts wmm params per hw, not per vif */
+- ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
+- if (ret) {
+- ath10k_warn("failed to set wmm params: %d\n", ret);
+- goto exit;
++ if (ar->wmi.ops->gen_vdev_wmm_conf) {
++ ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id,
++ &arvif->wmm_params);
++ if (ret) {
++ ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n",
++ arvif->vdev_id, ret);
++ goto exit;
++ }
++ } else {
++ /* This won't work well with multi-interface cases but it's
++ * better than nothing.
++ */
++ ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params);
++ if (ret) {
++ ath10k_warn(ar, "failed to set wmm params: %d\n", ret);
++ goto exit;
++ }
+ }
+
+ ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
+ if (ret)
+- ath10k_warn("failed to set sta uapsd: %d\n", ret);
++ ath10k_warn(ar, "failed to set sta uapsd: %d\n", ret);
+
+ exit:
+ mutex_unlock(&ar->conf_mutex);
+@@ -3533,27 +4348,35 @@ static int ath10k_remain_on_channel(stru
+ struct ath10k *ar = hw->priv;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct wmi_start_scan_arg arg;
+- int ret;
++ int ret = 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+- if (ar->scan.in_progress) {
+- spin_unlock_bh(&ar->data_lock);
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ reinit_completion(&ar->scan.started);
++ reinit_completion(&ar->scan.completed);
++ reinit_completion(&ar->scan.on_channel);
++ ar->scan.state = ATH10K_SCAN_STARTING;
++ ar->scan.is_roc = true;
++ ar->scan.vdev_id = arvif->vdev_id;
++ ar->scan.roc_freq = chan->center_freq;
++ ret = 0;
++ break;
++ case ATH10K_SCAN_STARTING:
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
+ ret = -EBUSY;
+- goto exit;
++ break;
+ }
+-
+- reinit_completion(&ar->scan.started);
+- reinit_completion(&ar->scan.completed);
+- reinit_completion(&ar->scan.on_channel);
+- ar->scan.in_progress = true;
+- ar->scan.aborting = false;
+- ar->scan.is_roc = true;
+- ar->scan.vdev_id = arvif->vdev_id;
+- ar->scan.roc_freq = chan->center_freq;
+ spin_unlock_bh(&ar->data_lock);
+
++ if (ret)
++ goto exit;
++
++ duration = max(duration, WMI_SCAN_CHAN_MIN_TIME_MSEC);
++
+ memset(&arg, 0, sizeof(arg));
+ ath10k_wmi_start_scan_init(ar, &arg);
+ arg.vdev_id = arvif->vdev_id;
+@@ -3568,17 +4391,21 @@ static int ath10k_remain_on_channel(stru
+
+ ret = ath10k_start_scan(ar, &arg);
+ if (ret) {
+- ath10k_warn("failed to start roc scan: %d\n", ret);
++ ath10k_warn(ar, "failed to start roc scan: %d\n", ret);
+ spin_lock_bh(&ar->data_lock);
+- ar->scan.in_progress = false;
++ ar->scan.state = ATH10K_SCAN_IDLE;
+ spin_unlock_bh(&ar->data_lock);
+ goto exit;
+ }
+
+ ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
+ if (ret == 0) {
+- ath10k_warn("failed to switch to channel for roc scan\n");
+- ath10k_abort_scan(ar);
++ ath10k_warn(ar, "failed to switch to channel for roc scan\n");
++
++ ret = ath10k_scan_stop(ar);
++ if (ret)
++ ath10k_warn(ar, "failed to stop scan: %d\n", ret);
++
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+@@ -3594,9 +4421,11 @@ static int ath10k_cancel_remain_on_chann
+ struct ath10k *ar = hw->priv;
+
+ mutex_lock(&ar->conf_mutex);
+- ath10k_abort_scan(ar);
++ ath10k_scan_abort(ar);
+ mutex_unlock(&ar->conf_mutex);
+
++ cancel_delayed_work_sync(&ar->scan.timeout);
++
+ return 0;
+ }
+
+@@ -3613,35 +4442,12 @@ static int ath10k_set_rts_threshold(stru
+
+ mutex_lock(&ar->conf_mutex);
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n",
+- arvif->vdev_id, value);
+-
+- ret = ath10k_mac_set_rts(arvif, value);
+- if (ret) {
+- ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
+- arvif->vdev_id, ret);
+- break;
+- }
+- }
+- mutex_unlock(&ar->conf_mutex);
+-
+- return ret;
+-}
+-
+-static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+-{
+- struct ath10k *ar = hw->priv;
+- struct ath10k_vif *arvif;
+- int ret = 0;
+-
+- mutex_lock(&ar->conf_mutex);
+- list_for_each_entry(arvif, &ar->arvifs, list) {
+- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n",
+ arvif->vdev_id, value);
+
+ ret = ath10k_mac_set_rts(arvif, value);
+ if (ret) {
+- ath10k_warn("failed to set fragmentation threshold for vdev %d: %d\n",
++ ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ break;
+ }
+@@ -3675,13 +4481,15 @@ static void ath10k_flush(struct ieee8021
+ empty = (ar->htt.num_pending_tx == 0);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+- skip = (ar->state == ATH10K_STATE_WEDGED);
++ skip = (ar->state == ATH10K_STATE_WEDGED) ||
++ test_bit(ATH10K_FLAG_CRASH_FLUSH,
++ &ar->dev_flags);
+
+ (empty || skip);
+ }), ATH10K_FLUSH_TIMEOUT_HZ);
+
+ if (ret <= 0 || skip)
+- ath10k_warn("failed to flush transmit queue (skip %i ar-state %i): %i\n",
++ ath10k_warn(ar, "failed to flush transmit queue (skip %i ar-state %i): %i\n",
+ skip, ar->state, ret);
+
+ skip:
+@@ -3716,7 +4524,7 @@ static int ath10k_suspend(struct ieee802
+
+ ret = ath10k_hif_suspend(ar);
+ if (ret) {
+- ath10k_warn("failed to suspend hif: %d\n", ret);
++ ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
+ goto resume;
+ }
+
+@@ -3725,7 +4533,7 @@ static int ath10k_suspend(struct ieee802
+ resume:
+ ret = ath10k_wmi_pdev_resume_target(ar);
+ if (ret)
+- ath10k_warn("failed to resume target: %d\n", ret);
++ ath10k_warn(ar, "failed to resume target: %d\n", ret);
+
+ ret = 1;
+ exit:
+@@ -3742,14 +4550,14 @@ static int ath10k_resume(struct ieee8021
+
+ ret = ath10k_hif_resume(ar);
+ if (ret) {
+- ath10k_warn("failed to resume hif: %d\n", ret);
++ ath10k_warn(ar, "failed to resume hif: %d\n", ret);
+ ret = 1;
+ goto exit;
+ }
+
+ ret = ath10k_wmi_pdev_resume_target(ar);
+ if (ret) {
+- ath10k_warn("failed to resume target: %d\n", ret);
++ ath10k_warn(ar, "failed to resume target: %d\n", ret);
+ ret = 1;
+ goto exit;
+ }
+@@ -3770,8 +4578,9 @@ static void ath10k_restart_complete(stru
+ /* 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");
++ ath10k_info(ar, "device successfully recovered\n");
+ ar->state = ATH10K_STATE_ON;
++ ieee80211_wake_queues(ar->hw);
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+@@ -3807,6 +4616,9 @@ static int ath10k_get_survey(struct ieee
+
+ survey->channel = &sband->channels[idx];
+
++ if (ar->rx_channel == survey->channel)
++ survey->filled |= SURVEY_INFO_IN_USE;
++
+ exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+@@ -3854,6 +4666,10 @@ ath10k_default_bitrate_mask(struct ath10
+ u32 legacy = 0x00ff;
+ u8 ht = 0xff, i;
+ u16 vht = 0x3ff;
++ u16 nrf = ar->num_rf_chains;
++
++ if (ar->cfg_tx_chainmask)
++ nrf = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
+ switch (band) {
+ case IEEE80211_BAND_2GHZ:
+@@ -3869,11 +4685,11 @@ ath10k_default_bitrate_mask(struct ath10
+ if (mask->control[band].legacy != legacy)
+ return false;
+
+- for (i = 0; i < ar->num_rf_chains; i++)
++ for (i = 0; i < nrf; i++)
+ if (mask->control[band].ht_mcs[i] != ht)
+ return false;
+
+- for (i = 0; i < ar->num_rf_chains; i++)
++ for (i = 0; i < nrf; i++)
+ if (mask->control[band].vht_mcs[i] != vht)
+ return false;
+
+@@ -3897,8 +4713,8 @@ ath10k_bitrate_mask_nss(const struct cfg
+ continue;
+ else if (mask->control[band].ht_mcs[i] == 0x00)
+ break;
+- else
+- return false;
++
++ return false;
+ }
+
+ ht_nss = i;
+@@ -3909,8 +4725,8 @@ ath10k_bitrate_mask_nss(const struct cfg
+ continue;
+ else if (mask->control[band].vht_mcs[i] == 0x0000)
+ break;
+- else
+- return false;
++
++ return false;
+ }
+
+ vht_nss = i;
+@@ -3967,7 +4783,8 @@ ath10k_bitrate_mask_correct(const struct
+ }
+
+ static bool
+-ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask,
++ath10k_bitrate_mask_rate(struct ath10k *ar,
++ const struct cfg80211_bitrate_mask *mask,
+ enum ieee80211_band band,
+ u8 *fixed_rate,
+ u8 *fixed_nss)
+@@ -4025,7 +4842,7 @@ ath10k_bitrate_mask_rate(const struct cf
+ nss <<= 4;
+ pream <<= 6;
+
+- ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n",
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n",
+ pream, nss, rate);
+
+ *fixed_rate = pream | nss | rate;
+@@ -4033,7 +4850,8 @@ ath10k_bitrate_mask_rate(const struct cf
+ return true;
+ }
+
+-static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask,
++static bool ath10k_get_fixed_rate_nss(struct ath10k *ar,
++ const struct cfg80211_bitrate_mask *mask,
+ enum ieee80211_band band,
+ u8 *fixed_rate,
+ u8 *fixed_nss)
+@@ -4043,7 +4861,7 @@ static bool ath10k_get_fixed_rate_nss(co
+ return true;
+
+ /* Next Check single rate is set */
+- return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss);
++ return ath10k_bitrate_mask_rate(ar, mask, band, fixed_rate, fixed_nss);
+ }
+
+ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
+@@ -4063,16 +4881,16 @@ static int ath10k_set_fixed_rate_param(s
+ goto exit;
+
+ if (fixed_rate == WMI_FIXED_RATE_NONE)
+- ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n");
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n");
+
+ if (force_sgi)
+- ath10k_dbg(ATH10K_DBG_MAC, "mac force sgi\n");
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac force sgi\n");
+
+ vdev_param = ar->wmi.vdev_param->fixed_rate;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+ vdev_param, fixed_rate);
+ if (ret) {
+- ath10k_warn("failed to set fixed rate param 0x%02x: %d\n",
++ ath10k_warn(ar, "failed to set fixed rate param 0x%02x: %d\n",
+ fixed_rate, ret);
+ ret = -EINVAL;
+ goto exit;
+@@ -4085,7 +4903,7 @@ static int ath10k_set_fixed_rate_param(s
+ vdev_param, fixed_nss);
+
+ if (ret) {
+- ath10k_warn("failed to set fixed nss param %d: %d\n",
++ ath10k_warn(ar, "failed to set fixed nss param %d: %d\n",
+ fixed_nss, ret);
+ ret = -EINVAL;
+ goto exit;
+@@ -4098,7 +4916,7 @@ static int ath10k_set_fixed_rate_param(s
+ force_sgi);
+
+ if (ret) {
+- ath10k_warn("failed to set sgi param %d: %d\n",
++ ath10k_warn(ar, "failed to set sgi param %d: %d\n",
+ force_sgi, ret);
+ ret = -EINVAL;
+ goto exit;
+@@ -4122,19 +4940,22 @@ static int ath10k_set_bitrate_mask(struc
+ u8 fixed_nss = ar->num_rf_chains;
+ u8 force_sgi;
+
++ if (ar->cfg_tx_chainmask)
++ fixed_nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
++
+ force_sgi = mask->control[band].gi;
+ if (force_sgi == NL80211_TXRATE_FORCE_LGI)
+ return -EINVAL;
+
+ if (!ath10k_default_bitrate_mask(ar, band, mask)) {
+- if (!ath10k_get_fixed_rate_nss(mask, band,
++ if (!ath10k_get_fixed_rate_nss(ar, mask, band,
+ &fixed_rate,
+ &fixed_nss))
+ return -EINVAL;
+ }
+
+ if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) {
+- ath10k_warn("failed to force SGI usage for default rate settings\n");
++ ath10k_warn(ar, "failed to force SGI usage for default rate settings\n");
+ return -EINVAL;
+ }
+
+@@ -4153,7 +4974,7 @@ static void ath10k_sta_rc_update(struct
+
+ spin_lock_bh(&ar->data_lock);
+
+- ath10k_dbg(ATH10K_DBG_MAC,
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
+ sta->addr, changed, sta->bandwidth, sta->rx_nss,
+ sta->smps_mode);
+@@ -4172,7 +4993,7 @@ static void ath10k_sta_rc_update(struct
+ bw = WMI_PEER_CHWIDTH_80MHZ;
+ break;
+ case IEEE80211_STA_RX_BW_160:
+- ath10k_warn("Invalid bandwith %d in rc update for %pM\n",
++ ath10k_warn(ar, "Invalid bandwith %d in rc update for %pM\n",
+ sta->bandwidth, sta->addr);
+ bw = WMI_PEER_CHWIDTH_20MHZ;
+ break;
+@@ -4199,7 +5020,7 @@ static void ath10k_sta_rc_update(struct
+ smps = WMI_PEER_SMPS_DYNAMIC;
+ break;
+ case IEEE80211_SMPS_NUM_MODES:
+- ath10k_warn("Invalid smps %d in sta rc update for %pM\n",
++ ath10k_warn(ar, "Invalid smps %d in sta rc update for %pM\n",
+ sta->smps_mode, sta->addr);
+ smps = WMI_PEER_SMPS_PS_NONE;
+ break;
+@@ -4225,6 +5046,39 @@ static u64 ath10k_get_tsf(struct ieee802
+ return 0;
+ }
+
++static int ath10k_ampdu_action(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ enum ieee80211_ampdu_mlme_action action,
++ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
++ u8 buf_size)
++{
++ struct ath10k *ar = hw->priv;
++ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
++
++ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n",
++ arvif->vdev_id, sta->addr, tid, action);
++
++ switch (action) {
++ case IEEE80211_AMPDU_RX_START:
++ case IEEE80211_AMPDU_RX_STOP:
++ /* HTT AddBa/DelBa events trigger mac80211 Rx BA session
++ * creation/removal. Do we need to verify this?
++ */
++ return 0;
++ case IEEE80211_AMPDU_TX_START:
++ case IEEE80211_AMPDU_TX_STOP_CONT:
++ case IEEE80211_AMPDU_TX_STOP_FLUSH:
++ case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
++ case IEEE80211_AMPDU_TX_OPERATIONAL:
++ /* Firmware offloads Tx aggregation entirely so deny mac80211
++ * Tx aggregation requests.
++ */
++ return -EOPNOTSUPP;
++ }
++
++ return -EINVAL;
++}
++
+ static const struct ieee80211_ops ath10k_ops = {
+ .tx = ath10k_tx,
+ .start = ath10k_start,
+@@ -4237,23 +5091,35 @@ static const struct ieee80211_ops ath10k
+ .hw_scan = ath10k_hw_scan,
+ .cancel_hw_scan = ath10k_cancel_hw_scan,
+ .set_key = ath10k_set_key,
++ .set_default_unicast_key = ath10k_set_default_unicast_key,
+ .sta_state = ath10k_sta_state,
+ .conf_tx = ath10k_conf_tx,
+ .remain_on_channel = ath10k_remain_on_channel,
+ .cancel_remain_on_channel = ath10k_cancel_remain_on_channel,
+ .set_rts_threshold = ath10k_set_rts_threshold,
+- .set_frag_threshold = ath10k_set_frag_threshold,
+ .flush = ath10k_flush,
+ .tx_last_beacon = ath10k_tx_last_beacon,
++ .set_antenna = ath10k_set_antenna,
++ .get_antenna = ath10k_get_antenna,
+ .restart_complete = ath10k_restart_complete,
+ .get_survey = ath10k_get_survey,
+ .set_bitrate_mask = ath10k_set_bitrate_mask,
+ .sta_rc_update = ath10k_sta_rc_update,
+ .get_tsf = ath10k_get_tsf,
++ .ampdu_action = ath10k_ampdu_action,
++ .get_et_sset_count = ath10k_debug_get_et_sset_count,
++ .get_et_stats = ath10k_debug_get_et_stats,
++ .get_et_strings = ath10k_debug_get_et_strings,
++
++ CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
++
+ #ifdef CONFIG_PM
+ .suspend = ath10k_suspend,
+ .resume = ath10k_resume,
+ #endif
++#ifdef CPTCFG_MAC80211_DEBUGFS
++ .sta_add_debugfs = ath10k_sta_add_debugfs,
++#endif
+ };
+
+ #define RATETAB_ENT(_rate, _rateid, _flags) { \
+@@ -4324,6 +5190,9 @@ static const struct ieee80211_channel at
+ CHAN5G(165, 5825, 0),
+ };
+
++/* Note: Be careful if you re-order these. There is code which depends on this
++ * ordering.
++ */
+ static struct ieee80211_rate ath10k_rates[] = {
+ /* CCK */
+ RATETAB_ENT(10, 0x82, 0),
+@@ -4346,12 +5215,12 @@ static struct ieee80211_rate ath10k_rate
+ #define ath10k_g_rates (ath10k_rates + 0)
+ #define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))
+
+-struct ath10k *ath10k_mac_create(void)
++struct ath10k *ath10k_mac_create(size_t priv_size)
+ {
+ struct ieee80211_hw *hw;
+ struct ath10k *ar;
+
+- hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops);
++ hw = ieee80211_alloc_hw(sizeof(struct ath10k) + priv_size, &ath10k_ops);
+ if (!hw)
+ return NULL;
+
+@@ -4377,6 +5246,10 @@ static const struct ieee80211_iface_limi
+ .types = BIT(NL80211_IFTYPE_P2P_GO)
+ },
+ {
++ .max = 1,
++ .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
++ },
++ {
+ .max = 7,
+ .types = BIT(NL80211_IFTYPE_AP)
+ },
+@@ -4501,7 +5374,6 @@ static struct ieee80211_sta_ht_cap ath10
+ return ht_cap;
+ }
+
+-
+ static void ath10k_get_arvif_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+ {
+@@ -4526,7 +5398,7 @@ struct ath10k_vif *ath10k_get_arvif(stru
+ ath10k_get_arvif_iter,
+ &arvif_iter);
+ if (!arvif_iter.arvif) {
+- ath10k_warn("No VIF found for vdev %d\n", vdev_id);
++ ath10k_warn(ar, "No VIF found for vdev %d\n", vdev_id);
+ return NULL;
+ }
+
+@@ -4564,7 +5436,8 @@ int ath10k_mac_register(struct ath10k *a
+ band->bitrates = ath10k_g_rates;
+ band->ht_cap = ht_cap;
+
+- /* vht is not supported in 2.4 GHz */
++ /* Enable the VHT support at 2.4 GHz */
++ band->vht_cap = vht_cap;
+
+ ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+ }
+@@ -4590,18 +5463,20 @@ int ath10k_mac_register(struct ath10k *a
+
+ ar->hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+- BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
+
++ ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask;
++ ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask;
++
+ if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
+ ar->hw->wiphy->interface_modes |=
++ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+
+ ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+- IEEE80211_HW_SUPPORTS_UAPSD |
+ IEEE80211_HW_MFP_CAPABLE |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_HAS_RATE_CONTROL |
+@@ -4609,10 +5484,6 @@ int ath10k_mac_register(struct ath10k *a
+ IEEE80211_HW_AP_LINK_PS |
+ IEEE80211_HW_SPECTRUM_MGMT;
+
+- /* MSDU can have HTT TX fragment pushed in front. The additional 4
+- * bytes is used for padding/alignment if necessary. */
+- ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
+-
+ if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+ ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
+
+@@ -4629,25 +5500,52 @@ int ath10k_mac_register(struct ath10k *a
+
+ ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
+
++ if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) {
++ ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
++
++ /* Firmware delivers WPS/P2P Probe Requests frames to driver so
++ * that userspace (e.g. wpa_supplicant/hostapd) can generate
++ * correct Probe Responses. This is more of a hack advert..
++ */
++ ar->hw->wiphy->probe_resp_offload |=
++ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
++ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
++ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
++ }
++
+ ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+ 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;
++
+ /*
+ * on LL hardware queues are managed entirely by the FW
+ * so we only advertise to mac we can do the queues thing
+ */
+ ar->hw->queues = 4;
+
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+- ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
+- ar->hw->wiphy->n_iface_combinations =
+- ARRAY_SIZE(ath10k_10x_if_comb);
+- } else {
++ switch (ar->wmi.op_version) {
++ case ATH10K_FW_WMI_OP_VERSION_MAIN:
++ case ATH10K_FW_WMI_OP_VERSION_TLV:
+ ar->hw->wiphy->iface_combinations = ath10k_if_comb;
+ ar->hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(ath10k_if_comb);
++ ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_10_1:
++ case ATH10K_FW_WMI_OP_VERSION_10_2:
++ case ATH10K_FW_WMI_OP_VERSION_10_2_4:
++ ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
++ ar->hw->wiphy->n_iface_combinations =
++ ARRAY_SIZE(ath10k_10x_if_comb);
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_UNSET:
++ case ATH10K_FW_WMI_OP_VERSION_MAX:
++ WARN_ON(1);
++ ret = -EINVAL;
++ goto err_free;
+ }
+
+ ar->hw->netdev_features = NETIF_F_HW_CSUM;
+@@ -4659,19 +5557,19 @@ int ath10k_mac_register(struct ath10k *a
+ NL80211_DFS_UNSET);
+
+ if (!ar->dfs_detector)
+- ath10k_warn("failed to initialise DFS pattern detector\n");
++ ath10k_warn(ar, "failed to initialise DFS pattern detector\n");
+ }
+
+ ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
+ ath10k_reg_notifier);
+ if (ret) {
+- ath10k_err("failed to initialise regulatory: %i\n", ret);
++ ath10k_err(ar, "failed to initialise regulatory: %i\n", ret);
+ goto err_free;
+ }
+
+ ret = ieee80211_register_hw(ar->hw);
+ if (ret) {
+- ath10k_err("failed to register ieee80211: %d\n", ret);
++ ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
+ goto err_free;
+ }
+
+--- a/drivers/net/wireless/ath/ath10k/mac.h
++++ b/drivers/net/wireless/ath/ath10k/mac.h
+@@ -21,33 +21,41 @@
+ #include <net/mac80211.h>
+ #include "core.h"
+
++#define WEP_KEYID_SHIFT 6
++
+ struct ath10k_generic_iter {
+ struct ath10k *ar;
+ int ret;
+ };
+
+-struct ath10k *ath10k_mac_create(void);
++struct ath10k *ath10k_mac_create(size_t priv_size);
+ void ath10k_mac_destroy(struct ath10k *ar);
+ int ath10k_mac_register(struct ath10k *ar);
+ void ath10k_mac_unregister(struct ath10k *ar);
+ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
+-void ath10k_reset_scan(unsigned long ptr);
++void __ath10k_scan_finish(struct ath10k *ar);
++void ath10k_scan_finish(struct ath10k *ar);
++void ath10k_scan_timeout_work(struct work_struct *work);
+ void ath10k_offchan_tx_purge(struct ath10k *ar);
+ void ath10k_offchan_tx_work(struct work_struct *work);
+ void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
+ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
+ void ath10k_halt(struct ath10k *ar);
++void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif);
++void ath10k_drain_tx(struct ath10k *ar);
++bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
++ u8 keyidx);
+
+ static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
+ {
+ return (struct ath10k_vif *)vif->drv_priv;
+ }
+
+-static inline void ath10k_tx_h_seq_no(struct sk_buff *skb)
++static inline void ath10k_tx_h_seq_no(struct ieee80211_vif *vif,
++ struct sk_buff *skb)
+ {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+- struct ieee80211_vif *vif = info->control.vif;
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+--- a/drivers/net/wireless/ath/ath10k/pci.c
++++ b/drivers/net/wireless/ath/ath10k/pci.c
+@@ -44,13 +44,9 @@ enum ath10k_pci_reset_mode {
+ ATH10K_PCI_RESET_WARM_ONLY = 1,
+ };
+
+-static unsigned int ath10k_pci_target_ps;
+ static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO;
+ static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO;
+
+-module_param_named(target_ps, ath10k_pci_target_ps, uint, 0644);
+-MODULE_PARM_DESC(target_ps, "Enable ath10k Target (SoC) PS option");
+-
+ module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644);
+ MODULE_PARM_DESC(irq_mode, "0: auto, 1: legacy, 2: msi (default: 0)");
+
+@@ -59,21 +55,31 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1
+
+ /* how long wait to wait for target to initialise, in ms */
+ #define ATH10K_PCI_TARGET_WAIT 3000
++#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3
+
+ #define QCA988X_2_0_DEVICE_ID (0x003c)
++#define QCA6174_2_1_DEVICE_ID (0x003e)
+
+-static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
++static const struct pci_device_id ath10k_pci_id_table[] = {
+ { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
++ { PCI_VDEVICE(ATHEROS, QCA6174_2_1_DEVICE_ID) }, /* PCI-E QCA6174 V2.1 */
+ {0}
+ };
+
+-static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+- u32 *data);
++static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = {
++ /* QCA988X pre 2.0 chips are not supported because they need some nasty
++ * hacks. ath10k doesn't have them and these devices crash horribly
++ * because of that.
++ */
++ { QCA988X_2_0_DEVICE_ID, QCA988X_HW_2_0_CHIP_ID_REV },
++ { QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_1_CHIP_ID_REV },
++ { QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_2_CHIP_ID_REV },
++ { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_0_CHIP_ID_REV },
++ { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_1_CHIP_ID_REV },
++ { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV },
++};
+
+-static int ath10k_pci_post_rx(struct ath10k *ar);
+-static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
+- int num);
+-static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info);
++static void ath10k_pci_buffer_cleanup(struct ath10k *ar);
+ static int ath10k_pci_cold_reset(struct ath10k *ar);
+ static int ath10k_pci_warm_reset(struct ath10k *ar);
+ static int ath10k_pci_wait_for_target_init(struct ath10k *ar);
+@@ -98,7 +104,7 @@ static const struct ce_attr host_ce_conf
+ {
+ .flags = CE_ATTR_FLAGS,
+ .src_nentries = 0,
+- .src_sz_max = 512,
++ .src_sz_max = 2048,
+ .dest_nentries = 512,
+ },
+
+@@ -155,79 +161,175 @@ static const struct ce_attr host_ce_conf
+ static const struct ce_pipe_config target_ce_config_wlan[] = {
+ /* CE0: host->target HTC control and raw streams */
+ {
+- .pipenum = 0,
+- .pipedir = PIPEDIR_OUT,
+- .nentries = 32,
+- .nbytes_max = 256,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(0),
++ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
++ .nentries = __cpu_to_le32(32),
++ .nbytes_max = __cpu_to_le32(256),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* CE1: target->host HTT + HTC control */
+ {
+- .pipenum = 1,
+- .pipedir = PIPEDIR_IN,
+- .nentries = 32,
+- .nbytes_max = 512,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(1),
++ .pipedir = __cpu_to_le32(PIPEDIR_IN),
++ .nentries = __cpu_to_le32(32),
++ .nbytes_max = __cpu_to_le32(2048),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* CE2: target->host WMI */
+ {
+- .pipenum = 2,
+- .pipedir = PIPEDIR_IN,
+- .nentries = 32,
+- .nbytes_max = 2048,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(2),
++ .pipedir = __cpu_to_le32(PIPEDIR_IN),
++ .nentries = __cpu_to_le32(32),
++ .nbytes_max = __cpu_to_le32(2048),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* CE3: host->target WMI */
+ {
+- .pipenum = 3,
+- .pipedir = PIPEDIR_OUT,
+- .nentries = 32,
+- .nbytes_max = 2048,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(3),
++ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
++ .nentries = __cpu_to_le32(32),
++ .nbytes_max = __cpu_to_le32(2048),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* CE4: host->target HTT */
+ {
+- .pipenum = 4,
+- .pipedir = PIPEDIR_OUT,
+- .nentries = 256,
+- .nbytes_max = 256,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(4),
++ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
++ .nentries = __cpu_to_le32(256),
++ .nbytes_max = __cpu_to_le32(256),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* NB: 50% of src nentries, since tx has 2 frags */
+
+ /* CE5: unused */
+ {
+- .pipenum = 5,
+- .pipedir = PIPEDIR_OUT,
+- .nentries = 32,
+- .nbytes_max = 2048,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(5),
++ .pipedir = __cpu_to_le32(PIPEDIR_OUT),
++ .nentries = __cpu_to_le32(32),
++ .nbytes_max = __cpu_to_le32(2048),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* CE6: Reserved for target autonomous hif_memcpy */
+ {
+- .pipenum = 6,
+- .pipedir = PIPEDIR_INOUT,
+- .nentries = 32,
+- .nbytes_max = 4096,
+- .flags = CE_ATTR_FLAGS,
+- .reserved = 0,
++ .pipenum = __cpu_to_le32(6),
++ .pipedir = __cpu_to_le32(PIPEDIR_INOUT),
++ .nentries = __cpu_to_le32(32),
++ .nbytes_max = __cpu_to_le32(4096),
++ .flags = __cpu_to_le32(CE_ATTR_FLAGS),
++ .reserved = __cpu_to_le32(0),
+ },
+
+ /* CE7 used only by Host */
+ };
+
++/*
++ * Map from service/endpoint to Copy Engine.
++ * This table is derived from the CE_PCI TABLE, above.
++ * It is passed to the Target at startup for use by firmware.
++ */
++static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(3),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(2),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(3),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(2),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(3),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(2),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(3),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(2),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(3),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(2),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(0),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(1),
++ },
++ { /* not used */
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(0),
++ },
++ { /* not used */
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(1),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG),
++ __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
++ __cpu_to_le32(4),
++ },
++ {
++ __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG),
++ __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */
++ __cpu_to_le32(1),
++ },
++
++ /* (Additions here) */
++
++ { /* must be last */
++ __cpu_to_le32(0),
++ __cpu_to_le32(0),
++ __cpu_to_le32(0),
++ },
++};
++
+ static bool ath10k_pci_irq_pending(struct ath10k *ar)
+ {
+ u32 cause;
+@@ -253,8 +355,8 @@ static void ath10k_pci_disable_and_clear
+
+ /* IMPORTANT: this extra read transaction is required to
+ * flush the posted write buffer. */
+- (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+- PCIE_INTR_ENABLE_ADDRESS);
++ (void)ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
++ PCIE_INTR_ENABLE_ADDRESS);
+ }
+
+ static void ath10k_pci_enable_legacy_irq(struct ath10k *ar)
+@@ -265,48 +367,116 @@ static void ath10k_pci_enable_legacy_irq
+
+ /* IMPORTANT: this extra read transaction is required to
+ * flush the posted write buffer. */
+- (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+- PCIE_INTR_ENABLE_ADDRESS);
++ (void)ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
++ PCIE_INTR_ENABLE_ADDRESS);
+ }
+
+-static irqreturn_t ath10k_pci_early_irq_handler(int irq, void *arg)
++static inline const char *ath10k_pci_get_irq_method(struct ath10k *ar)
+ {
+- struct ath10k *ar = arg;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+- if (ar_pci->num_msi_intrs == 0) {
+- if (!ath10k_pci_irq_pending(ar))
+- return IRQ_NONE;
+-
+- ath10k_pci_disable_and_clear_legacy_irq(ar);
+- }
++ if (ar_pci->num_msi_intrs > 1)
++ return "msi-x";
+
+- tasklet_schedule(&ar_pci->early_irq_tasklet);
++ if (ar_pci->num_msi_intrs == 1)
++ return "msi";
+
+- return IRQ_HANDLED;
++ return "legacy";
+ }
+
+-static int ath10k_pci_request_early_irq(struct ath10k *ar)
++static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
+ {
++ struct ath10k *ar = pipe->hif_ce_state;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl;
++ struct sk_buff *skb;
++ dma_addr_t paddr;
+ int ret;
+
+- /* Regardless whether MSI-X/MSI/legacy irqs have been set up the first
+- * interrupt from irq vector is triggered in all cases for FW
+- * indication/errors */
+- ret = request_irq(ar_pci->pdev->irq, ath10k_pci_early_irq_handler,
+- IRQF_SHARED, "ath10k_pci (early)", ar);
++ lockdep_assert_held(&ar_pci->ce_lock);
++
++ skb = dev_alloc_skb(pipe->buf_sz);
++ if (!skb)
++ return -ENOMEM;
++
++ WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
++
++ paddr = dma_map_single(ar->dev, skb->data,
++ skb->len + skb_tailroom(skb),
++ DMA_FROM_DEVICE);
++ if (unlikely(dma_mapping_error(ar->dev, paddr))) {
++ ath10k_warn(ar, "failed to dma map pci rx buf\n");
++ dev_kfree_skb_any(skb);
++ return -EIO;
++ }
++
++ ATH10K_SKB_RXCB(skb)->paddr = paddr;
++
++ ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
+ if (ret) {
+- ath10k_warn("failed to request early irq: %d\n", ret);
++ ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret);
++ dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
++ DMA_FROM_DEVICE);
++ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ return 0;
+ }
+
+-static void ath10k_pci_free_early_irq(struct ath10k *ar)
++static void __ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
++{
++ struct ath10k *ar = pipe->hif_ce_state;
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl;
++ int ret, num;
++
++ lockdep_assert_held(&ar_pci->ce_lock);
++
++ if (pipe->buf_sz == 0)
++ return;
++
++ if (!ce_pipe->dest_ring)
++ return;
++
++ num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
++ while (num--) {
++ ret = __ath10k_pci_rx_post_buf(pipe);
++ if (ret) {
++ ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret);
++ mod_timer(&ar_pci->rx_post_retry, jiffies +
++ ATH10K_PCI_RX_POST_RETRY_MS);
++ break;
++ }
++ }
++}
++
++static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
++{
++ struct ath10k *ar = pipe->hif_ce_state;
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++
++ spin_lock_bh(&ar_pci->ce_lock);
++ __ath10k_pci_rx_post_pipe(pipe);
++ spin_unlock_bh(&ar_pci->ce_lock);
++}
++
++static void ath10k_pci_rx_post(struct ath10k *ar)
++{
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ int i;
++
++ spin_lock_bh(&ar_pci->ce_lock);
++ for (i = 0; i < CE_COUNT; i++)
++ __ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]);
++ spin_unlock_bh(&ar_pci->ce_lock);
++}
++
++static void ath10k_pci_rx_replenish_retry(unsigned long ptr)
+ {
+- free_irq(ath10k_pci_priv(ar)->pdev->irq, ar);
++ struct ath10k *ar = (void *)ptr;
++
++ ath10k_pci_rx_post(ar);
+ }
+
+ /*
+@@ -330,24 +500,7 @@ static int ath10k_pci_diag_read_mem(stru
+ void *data_buf = NULL;
+ int i;
+
+- /*
+- * This code cannot handle reads to non-memory space. Redirect to the
+- * register read fn but preserve the multi word read capability of
+- * this fn
+- */
+- if (address < DRAM_BASE_ADDRESS) {
+- if (!IS_ALIGNED(address, 4) ||
+- !IS_ALIGNED((unsigned long)data, 4))
+- return -EIO;
+-
+- while ((nbytes >= 4) && ((ret = ath10k_pci_diag_read_access(
+- ar, address, (u32 *)data)) == 0)) {
+- nbytes -= sizeof(u32);
+- address += sizeof(u32);
+- data += sizeof(u32);
+- }
+- return ret;
+- }
++ spin_lock_bh(&ar_pci->ce_lock);
+
+ ce_diag = ar_pci->ce_diag;
+
+@@ -375,7 +528,7 @@ static int ath10k_pci_diag_read_mem(stru
+ nbytes = min_t(unsigned int, remaining_bytes,
+ DIAG_TRANSFER_LIMIT);
+
+- ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data);
++ ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data);
+ if (ret != 0)
+ goto done;
+
+@@ -388,20 +541,18 @@ static int ath10k_pci_diag_read_mem(stru
+ * convert it from Target CPU virtual address space
+ * to CE address space
+ */
+- ath10k_pci_wake(ar);
+ address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
+ address);
+- ath10k_pci_sleep(ar);
+
+- ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
+- 0);
++ ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)address, nbytes, 0,
++ 0);
+ if (ret)
+ goto done;
+
+ i = 0;
+- while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+- &completed_nbytes,
+- &id) != 0) {
++ while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
++ &completed_nbytes,
++ &id) != 0) {
+ mdelay(1);
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+ ret = -EBUSY;
+@@ -414,15 +565,15 @@ static int ath10k_pci_diag_read_mem(stru
+ goto done;
+ }
+
+- if (buf != (u32) address) {
++ if (buf != (u32)address) {
+ ret = -EIO;
+ goto done;
+ }
+
+ i = 0;
+- while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+- &completed_nbytes,
+- &id, &flags) != 0) {
++ while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
++ &completed_nbytes,
++ &id, &flags) != 0) {
+ mdelay(1);
+
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+@@ -447,38 +598,60 @@ static int ath10k_pci_diag_read_mem(stru
+ }
+
+ done:
+- if (ret == 0) {
+- /* Copy data from allocated DMA buf to caller's buf */
+- WARN_ON_ONCE(orig_nbytes & 3);
+- for (i = 0; i < orig_nbytes / sizeof(__le32); i++) {
+- ((u32 *)data)[i] =
+- __le32_to_cpu(((__le32 *)data_buf)[i]);
+- }
+- } else
+- ath10k_warn("failed to read diag value at 0x%x: %d\n",
++ if (ret == 0)
++ memcpy(data, data_buf, orig_nbytes);
++ else
++ ath10k_warn(ar, "failed to read diag value at 0x%x: %d\n",
+ address, ret);
+
+ if (data_buf)
+ dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+ ce_data_base);
+
++ spin_unlock_bh(&ar_pci->ce_lock);
++
+ return ret;
+ }
+
+-/* Read 4-byte aligned data from Target memory or register */
+-static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+- u32 *data)
+-{
+- /* Assume range doesn't cross this boundary */
+- if (address >= DRAM_BASE_ADDRESS)
+- return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32));
++static int ath10k_pci_diag_read32(struct ath10k *ar, u32 address, u32 *value)
++{
++ __le32 val = 0;
++ int ret;
++
++ ret = ath10k_pci_diag_read_mem(ar, address, &val, sizeof(val));
++ *value = __le32_to_cpu(val);
++
++ return ret;
++}
++
++static int __ath10k_pci_diag_read_hi(struct ath10k *ar, void *dest,
++ u32 src, u32 len)
++{
++ u32 host_addr, addr;
++ int ret;
++
++ host_addr = host_interest_item_address(src);
++
++ ret = ath10k_pci_diag_read32(ar, host_addr, &addr);
++ if (ret != 0) {
++ ath10k_warn(ar, "failed to get memcpy hi address for firmware address %d: %d\n",
++ src, ret);
++ return ret;
++ }
++
++ ret = ath10k_pci_diag_read_mem(ar, addr, dest, len);
++ if (ret != 0) {
++ ath10k_warn(ar, "failed to memcpy firmware memory from %d (%d B): %d\n",
++ addr, len, ret);
++ return ret;
++ }
+
+- ath10k_pci_wake(ar);
+- *data = ath10k_pci_read32(ar, address);
+- ath10k_pci_sleep(ar);
+ return 0;
+ }
+
++#define ath10k_pci_diag_read_hi(ar, dest, src, len) \
++ __ath10k_pci_diag_read_hi(ar, dest, HI_ITEM(src), len)
++
+ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+ const void *data, int nbytes)
+ {
+@@ -494,6 +667,8 @@ static int ath10k_pci_diag_write_mem(str
+ dma_addr_t ce_data_base = 0;
+ int i;
+
++ spin_lock_bh(&ar_pci->ce_lock);
++
+ ce_diag = ar_pci->ce_diag;
+
+ /*
+@@ -513,9 +688,7 @@ static int ath10k_pci_diag_write_mem(str
+ }
+
+ /* Copy caller's data to allocated DMA buf */
+- WARN_ON_ONCE(orig_nbytes & 3);
+- for (i = 0; i < orig_nbytes / sizeof(__le32); i++)
+- ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]);
++ memcpy(data_buf, data, orig_nbytes);
+
+ /*
+ * The address supplied by the caller is in the
+@@ -527,9 +700,7 @@ static int ath10k_pci_diag_write_mem(str
+ * to
+ * CE address space
+ */
+- ath10k_pci_wake(ar);
+ address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address);
+- ath10k_pci_sleep(ar);
+
+ remaining_bytes = orig_nbytes;
+ ce_data = ce_data_base;
+@@ -538,7 +709,7 @@ static int ath10k_pci_diag_write_mem(str
+ nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
+
+ /* Set up to receive directly into Target(!) address */
+- ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address);
++ ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, address);
+ if (ret != 0)
+ goto done;
+
+@@ -546,15 +717,15 @@ static int ath10k_pci_diag_write_mem(str
+ * Request CE to send caller-supplied data that
+ * was copied to bounce buffer to Target(!) address.
+ */
+- ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data,
+- nbytes, 0, 0);
++ ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)ce_data,
++ nbytes, 0, 0);
+ if (ret != 0)
+ goto done;
+
+ i = 0;
+- while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+- &completed_nbytes,
+- &id) != 0) {
++ while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
++ &completed_nbytes,
++ &id) != 0) {
+ mdelay(1);
+
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+@@ -574,9 +745,9 @@ static int ath10k_pci_diag_write_mem(str
+ }
+
+ i = 0;
+- while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+- &completed_nbytes,
+- &id, &flags) != 0) {
++ while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
++ &completed_nbytes,
++ &id, &flags) != 0) {
+ mdelay(1);
+
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+@@ -607,66 +778,36 @@ done:
+ }
+
+ if (ret != 0)
+- ath10k_warn("failed to write diag value at 0x%x: %d\n",
++ ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n",
+ address, ret);
+
++ spin_unlock_bh(&ar_pci->ce_lock);
++
+ return ret;
+ }
+
+-/* Write 4B data to Target memory or register */
+-static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address,
+- u32 data)
+-{
+- /* Assume range doesn't cross this boundary */
+- if (address >= DRAM_BASE_ADDRESS)
+- return ath10k_pci_diag_write_mem(ar, address, &data,
+- sizeof(u32));
++static int ath10k_pci_diag_write32(struct ath10k *ar, u32 address, u32 value)
++{
++ __le32 val = __cpu_to_le32(value);
+
+- ath10k_pci_wake(ar);
+- ath10k_pci_write32(ar, address, data);
+- ath10k_pci_sleep(ar);
+- return 0;
++ return ath10k_pci_diag_write_mem(ar, address, &val, sizeof(val));
+ }
+
+-static bool ath10k_pci_target_is_awake(struct ath10k *ar)
++static bool ath10k_pci_is_awake(struct ath10k *ar)
+ {
+- void __iomem *mem = ath10k_pci_priv(ar)->mem;
+- u32 val;
+- val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS +
+- RTC_STATE_ADDRESS);
+- return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON);
++ u32 val = ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS);
++
++ return RTC_STATE_V_GET(val) == RTC_STATE_V_ON;
+ }
+
+-int ath10k_do_pci_wake(struct ath10k *ar)
++static int ath10k_pci_wake_wait(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- void __iomem *pci_addr = ar_pci->mem;
+ int tot_delay = 0;
+ int curr_delay = 5;
+
+- if (atomic_read(&ar_pci->keep_awake_count) == 0) {
+- /* Force AWAKE */
+- iowrite32(PCIE_SOC_WAKE_V_MASK,
+- pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+- PCIE_SOC_WAKE_ADDRESS);
+- }
+- atomic_inc(&ar_pci->keep_awake_count);
+-
+- if (ar_pci->verified_awake)
+- return 0;
+-
+- for (;;) {
+- if (ath10k_pci_target_is_awake(ar)) {
+- ar_pci->verified_awake = true;
++ while (tot_delay < PCIE_WAKE_TIMEOUT) {
++ if (ath10k_pci_is_awake(ar))
+ return 0;
+- }
+-
+- if (tot_delay > PCIE_WAKE_TIMEOUT) {
+- ath10k_warn("target took longer %d us to wake up (awake count %d)\n",
+- PCIE_WAKE_TIMEOUT,
+- atomic_read(&ar_pci->keep_awake_count));
+- return -ETIMEDOUT;
+- }
+
+ udelay(curr_delay);
+ tot_delay += curr_delay;
+@@ -674,20 +815,21 @@ int ath10k_do_pci_wake(struct ath10k *ar
+ if (curr_delay < 50)
+ curr_delay += 5;
+ }
++
++ return -ETIMEDOUT;
+ }
+
+-void ath10k_do_pci_sleep(struct ath10k *ar)
++static int ath10k_pci_wake(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- void __iomem *pci_addr = ar_pci->mem;
++ ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS,
++ PCIE_SOC_WAKE_V_MASK);
++ return ath10k_pci_wake_wait(ar);
++}
+
+- if (atomic_dec_and_test(&ar_pci->keep_awake_count)) {
+- /* Allow sleep */
+- ar_pci->verified_awake = false;
+- iowrite32(PCIE_SOC_WAKE_RESET,
+- pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+- PCIE_SOC_WAKE_ADDRESS);
+- }
++static void ath10k_pci_sleep(struct ath10k *ar)
++{
++ ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS,
++ PCIE_SOC_WAKE_RESET);
+ }
+
+ /* Called by lower (CE) layer when a send to Target completes. */
+@@ -696,20 +838,24 @@ static void ath10k_pci_ce_send_done(stru
+ struct ath10k *ar = ce_state->ar;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+- void *transfer_context;
++ struct sk_buff_head list;
++ struct sk_buff *skb;
+ u32 ce_data;
+ unsigned int nbytes;
+ unsigned int transfer_id;
+
+- while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
+- &ce_data, &nbytes,
+- &transfer_id) == 0) {
++ __skb_queue_head_init(&list);
++ while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data,
++ &nbytes, &transfer_id) == 0) {
+ /* no need to call tx completion for NULL pointers */
+- if (transfer_context == NULL)
++ if (skb == NULL)
+ continue;
+
+- cb->tx_completion(ar, transfer_context, transfer_id);
++ __skb_queue_tail(&list, skb);
+ }
++
++ while ((skb = __skb_dequeue(&list)))
++ cb->tx_completion(ar, skb);
+ }
+
+ /* Called by lower (CE) layer when data is received from the Target. */
+@@ -720,38 +866,43 @@ static void ath10k_pci_ce_recv_data(stru
+ struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
+ struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+ struct sk_buff *skb;
++ struct sk_buff_head list;
+ void *transfer_context;
+ u32 ce_data;
+ unsigned int nbytes, max_nbytes;
+ unsigned int transfer_id;
+ unsigned int flags;
+- int err;
+
++ __skb_queue_head_init(&list);
+ while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
+ &ce_data, &nbytes, &transfer_id,
+ &flags) == 0) {
+- err = ath10k_pci_post_rx_pipe(pipe_info, 1);
+- if (unlikely(err)) {
+- /* FIXME: retry */
+- ath10k_warn("failed to replenish CE rx ring %d: %d\n",
+- pipe_info->pipe_num, err);
+- }
+-
+ skb = transfer_context;
+ max_nbytes = skb->len + skb_tailroom(skb);
+- dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
++ dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
+ max_nbytes, DMA_FROM_DEVICE);
+
+ if (unlikely(max_nbytes < nbytes)) {
+- ath10k_warn("rxed more than expected (nbytes %d, max %d)",
++ ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)",
+ nbytes, max_nbytes);
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ skb_put(skb, nbytes);
+- cb->rx_completion(ar, skb, pipe_info->pipe_num);
++ __skb_queue_tail(&list, skb);
++ }
++
++ while ((skb = __skb_dequeue(&list))) {
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci rx ce pipe %d len %d\n",
++ ce_state->id, skb->len);
++ ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
++ skb->data, skb->len);
++
++ cb->rx_completion(ar, skb);
+ }
++
++ ath10k_pci_rx_post_pipe(pipe_info);
+ }
+
+ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+@@ -761,24 +912,28 @@ static int ath10k_pci_hif_tx_sg(struct a
+ struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id];
+ struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl;
+ struct ath10k_ce_ring *src_ring = ce_pipe->src_ring;
+- unsigned int nentries_mask = src_ring->nentries_mask;
+- unsigned int sw_index = src_ring->sw_index;
+- unsigned int write_index = src_ring->write_index;
+- int err, i;
++ unsigned int nentries_mask;
++ unsigned int sw_index;
++ unsigned int write_index;
++ int err, i = 0;
+
+ spin_lock_bh(&ar_pci->ce_lock);
+
++ nentries_mask = src_ring->nentries_mask;
++ sw_index = src_ring->sw_index;
++ write_index = src_ring->write_index;
++
+ if (unlikely(CE_RING_DELTA(nentries_mask,
+ write_index, sw_index - 1) < n_items)) {
+ err = -ENOBUFS;
+- goto unlock;
++ goto err;
+ }
+
+ for (i = 0; i < n_items - 1; i++) {
+- ath10k_dbg(ATH10K_DBG_PCI,
++ ath10k_dbg(ar, ATH10K_DBG_PCI,
+ "pci tx item %d paddr 0x%08x len %d n_items %d\n",
+ i, items[i].paddr, items[i].len, n_items);
+- ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ",
++ ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci tx data: ",
+ items[i].vaddr, items[i].len);
+
+ err = ath10k_ce_send_nolock(ce_pipe,
+@@ -788,15 +943,15 @@ static int ath10k_pci_hif_tx_sg(struct a
+ items[i].transfer_id,
+ CE_SEND_FLAG_GATHER);
+ if (err)
+- goto unlock;
++ goto err;
+ }
+
+ /* `i` is equal to `n_items -1` after for() */
+
+- ath10k_dbg(ATH10K_DBG_PCI,
++ ath10k_dbg(ar, ATH10K_DBG_PCI,
+ "pci tx item %d paddr 0x%08x len %d n_items %d\n",
+ i, items[i].paddr, items[i].len, n_items);
+- ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ",
++ ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci tx data: ",
+ items[i].vaddr, items[i].len);
+
+ err = ath10k_ce_send_nolock(ce_pipe,
+@@ -806,64 +961,89 @@ static int ath10k_pci_hif_tx_sg(struct a
+ items[i].transfer_id,
+ 0);
+ if (err)
+- goto unlock;
++ goto err;
++
++ spin_unlock_bh(&ar_pci->ce_lock);
++ return 0;
++
++err:
++ for (; i > 0; i--)
++ __ath10k_ce_send_revert(ce_pipe);
+
+- err = 0;
+-unlock:
+ spin_unlock_bh(&ar_pci->ce_lock);
+ return err;
+ }
+
++static int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
++ size_t buf_len)
++{
++ return ath10k_pci_diag_read_mem(ar, address, buf, buf_len);
++}
++
+ static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+- ath10k_dbg(ATH10K_DBG_PCI, "pci hif get free queue number\n");
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get free queue number\n");
+
+ return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl);
+ }
+
+-static void ath10k_pci_hif_dump_area(struct ath10k *ar)
++static void ath10k_pci_dump_registers(struct ath10k *ar,
++ struct ath10k_fw_crash_data *crash_data)
+ {
+- u32 reg_dump_area = 0;
+- u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
+- u32 host_addr;
+- int ret;
+- u32 i;
+-
+- ath10k_err("firmware crashed!\n");
+- ath10k_err("hardware name %s version 0x%x\n",
+- ar->hw_params.name, ar->target_version);
+- ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version);
+-
+- host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
+- ret = ath10k_pci_diag_read_mem(ar, host_addr,
+- &reg_dump_area, sizeof(u32));
+- if (ret) {
+- ath10k_err("failed to read FW dump area address: %d\n", ret);
+- return;
+- }
++ __le32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
++ int i, ret;
+
+- ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area);
++ lockdep_assert_held(&ar->data_lock);
+
+- ret = ath10k_pci_diag_read_mem(ar, reg_dump_area,
+- &reg_dump_values[0],
+- REG_DUMP_COUNT_QCA988X * sizeof(u32));
+- if (ret != 0) {
+- ath10k_err("failed to read FW dump area: %d\n", ret);
++ ret = ath10k_pci_diag_read_hi(ar, &reg_dump_values[0],
++ hi_failure_state,
++ REG_DUMP_COUNT_QCA988X * sizeof(__le32));
++ if (ret) {
++ ath10k_err(ar, "failed to read firmware dump area: %d\n", ret);
+ return;
+ }
+
+ BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4);
+
+- ath10k_err("target Register Dump\n");
++ ath10k_err(ar, "firmware register dump:\n");
+ for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4)
+- ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
++ ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
+ i,
+- reg_dump_values[i],
+- reg_dump_values[i + 1],
+- reg_dump_values[i + 2],
+- reg_dump_values[i + 3]);
++ __le32_to_cpu(reg_dump_values[i]),
++ __le32_to_cpu(reg_dump_values[i + 1]),
++ __le32_to_cpu(reg_dump_values[i + 2]),
++ __le32_to_cpu(reg_dump_values[i + 3]));
++
++ if (!crash_data)
++ return;
++
++ for (i = 0; i < REG_DUMP_COUNT_QCA988X; i++)
++ crash_data->registers[i] = reg_dump_values[i];
++}
++
++static void ath10k_pci_fw_crashed_dump(struct ath10k *ar)
++{
++ struct ath10k_fw_crash_data *crash_data;
++ char uuid[50];
++
++ spin_lock_bh(&ar->data_lock);
++
++ ar->stats.fw_crash_counter++;
++
++ crash_data = ath10k_debug_get_new_fw_crash_data(ar);
++
++ if (crash_data)
++ scnprintf(uuid, sizeof(uuid), "%pUl", &crash_data->uuid);
++ else
++ scnprintf(uuid, sizeof(uuid), "n/a");
++
++ ath10k_err(ar, "firmware crashed! (uuid %s)\n", uuid);
++ ath10k_print_driver_info(ar);
++ ath10k_pci_dump_registers(ar, crash_data);
++
++ spin_unlock_bh(&ar->data_lock);
+
+ queue_work(ar->workqueue, &ar->restart_work);
+ }
+@@ -871,7 +1051,7 @@ static void ath10k_pci_hif_dump_area(str
+ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+ int force)
+ {
+- ath10k_dbg(ATH10K_DBG_PCI, "pci hif send complete check\n");
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif send complete check\n");
+
+ if (!force) {
+ int resources;
+@@ -899,43 +1079,12 @@ static void ath10k_pci_hif_set_callbacks
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+- ath10k_dbg(ATH10K_DBG_PCI, "pci hif set callbacks\n");
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif set callbacks\n");
+
+ memcpy(&ar_pci->msg_callbacks_current, callbacks,
+ sizeof(ar_pci->msg_callbacks_current));
+ }
+
+-static int ath10k_pci_setup_ce_irq(struct ath10k *ar)
+-{
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- const struct ce_attr *attr;
+- struct ath10k_pci_pipe *pipe_info;
+- int pipe_num, disable_interrupts;
+-
+- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
+- pipe_info = &ar_pci->pipe_info[pipe_num];
+-
+- /* Handle Diagnostic CE specially */
+- if (pipe_info->ce_hdl == ar_pci->ce_diag)
+- continue;
+-
+- attr = &host_ce_config_wlan[pipe_num];
+-
+- if (attr->src_nentries) {
+- disable_interrupts = attr->flags & CE_ATTR_DIS_INTR;
+- ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+- ath10k_pci_ce_send_done,
+- disable_interrupts);
+- }
+-
+- if (attr->dest_nentries)
+- ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+- ath10k_pci_ce_recv_data);
+- }
+-
+- return 0;
+-}
+-
+ static void ath10k_pci_kill_tasklet(struct ath10k *ar)
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+@@ -943,82 +1092,72 @@ static void ath10k_pci_kill_tasklet(stru
+
+ tasklet_kill(&ar_pci->intr_tq);
+ tasklet_kill(&ar_pci->msi_fw_err);
+- tasklet_kill(&ar_pci->early_irq_tasklet);
+
+ for (i = 0; i < CE_COUNT; i++)
+ tasklet_kill(&ar_pci->pipe_info[i].intr);
++
++ del_timer_sync(&ar_pci->rx_post_retry);
+ }
+
+-/* TODO - temporary mapping while we have too few CE's */
+ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
+ u16 service_id, u8 *ul_pipe,
+ u8 *dl_pipe, int *ul_is_polled,
+ int *dl_is_polled)
+ {
+- int ret = 0;
++ const struct service_to_pipe *entry;
++ bool ul_set = false, dl_set = false;
++ int i;
+
+- ath10k_dbg(ATH10K_DBG_PCI, "pci hif map service\n");
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif map service\n");
+
+ /* polling for received messages not supported */
+ *dl_is_polled = 0;
+
+- switch (service_id) {
+- case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+- /*
+- * Host->target HTT gets its own pipe, so it can be polled
+- * while other pipes are interrupt driven.
+- */
+- *ul_pipe = 4;
+- /*
+- * Use the same target->host pipe for HTC ctrl, HTC raw
+- * streams, and HTT.
+- */
+- *dl_pipe = 1;
+- break;
++ for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) {
++ entry = &target_service_to_ce_map_wlan[i];
+
+- case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+- case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+- /*
+- * Note: HTC_RAW_STREAMS_SVC is currently unused, and
+- * HTC_CTRL_RSVD_SVC could share the same pipe as the
+- * WMI services. So, if another CE is needed, change
+- * this to *ul_pipe = 3, which frees up CE 0.
+- */
+- /* *ul_pipe = 3; */
+- *ul_pipe = 0;
+- *dl_pipe = 1;
+- break;
++ if (__le32_to_cpu(entry->service_id) != service_id)
++ continue;
+
+- case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+- case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+- case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+- case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+-
+- case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+- *ul_pipe = 3;
+- *dl_pipe = 2;
+- break;
++ switch (__le32_to_cpu(entry->pipedir)) {
++ case PIPEDIR_NONE:
++ break;
++ case PIPEDIR_IN:
++ WARN_ON(dl_set);
++ *dl_pipe = __le32_to_cpu(entry->pipenum);
++ dl_set = true;
++ break;
++ case PIPEDIR_OUT:
++ WARN_ON(ul_set);
++ *ul_pipe = __le32_to_cpu(entry->pipenum);
++ ul_set = true;
++ break;
++ case PIPEDIR_INOUT:
++ WARN_ON(dl_set);
++ WARN_ON(ul_set);
++ *dl_pipe = __le32_to_cpu(entry->pipenum);
++ *ul_pipe = __le32_to_cpu(entry->pipenum);
++ dl_set = true;
++ ul_set = true;
++ break;
++ }
++ }
+
+- /* pipe 5 unused */
+- /* pipe 6 reserved */
+- /* pipe 7 reserved */
++ if (WARN_ON(!ul_set || !dl_set))
++ return -ENOENT;
+
+- default:
+- ret = -1;
+- break;
+- }
+ *ul_is_polled =
+ (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0;
+
+- return ret;
++ return 0;
+ }
+
+ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
+- u8 *ul_pipe, u8 *dl_pipe)
++ u8 *ul_pipe, u8 *dl_pipe)
+ {
+ int ul_is_polled, dl_is_polled;
+
+- ath10k_dbg(ATH10K_DBG_PCI, "pci hif get default pipe\n");
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get default pipe\n");
+
+ (void)ath10k_pci_hif_map_service_to_pipe(ar,
+ ATH10K_HTC_SVC_ID_RSVD_CTRL,
+@@ -1028,209 +1167,127 @@ static void ath10k_pci_hif_get_default_p
+ &dl_is_polled);
+ }
+
+-static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
+- int num)
++static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
+ {
+- struct ath10k *ar = pipe_info->hif_ce_state;
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- struct ath10k_ce_pipe *ce_state = pipe_info->ce_hdl;
+- struct sk_buff *skb;
+- dma_addr_t ce_data;
+- int i, ret = 0;
++ u32 val;
+
+- if (pipe_info->buf_sz == 0)
+- return 0;
++ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS);
++ val &= ~CORE_CTRL_PCIE_REG_31_MASK;
+
+- for (i = 0; i < num; i++) {
+- skb = dev_alloc_skb(pipe_info->buf_sz);
+- if (!skb) {
+- ath10k_warn("failed to allocate skbuff for pipe %d\n",
+- num);
+- ret = -ENOMEM;
+- goto err;
+- }
++ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS, val);
++}
+
+- WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
++static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar)
++{
++ u32 val;
+
+- ce_data = dma_map_single(ar->dev, skb->data,
+- skb->len + skb_tailroom(skb),
+- DMA_FROM_DEVICE);
++ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS);
++ val |= CORE_CTRL_PCIE_REG_31_MASK;
+
+- if (unlikely(dma_mapping_error(ar->dev, ce_data))) {
+- ath10k_warn("failed to DMA map sk_buff\n");
+- dev_kfree_skb_any(skb);
+- ret = -EIO;
+- goto err;
+- }
++ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS, val);
++}
+
+- ATH10K_SKB_CB(skb)->paddr = ce_data;
++static void ath10k_pci_irq_disable(struct ath10k *ar)
++{
++ ath10k_ce_disable_interrupts(ar);
++ ath10k_pci_disable_and_clear_legacy_irq(ar);
++ ath10k_pci_irq_msi_fw_mask(ar);
++}
+
+- pci_dma_sync_single_for_device(ar_pci->pdev, ce_data,
+- pipe_info->buf_sz,
+- PCI_DMA_FROMDEVICE);
+-
+- ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb,
+- ce_data);
+- if (ret) {
+- ath10k_warn("failed to enqueue to pipe %d: %d\n",
+- num, ret);
+- goto err;
+- }
+- }
+-
+- return ret;
+-
+-err:
+- ath10k_pci_rx_pipe_cleanup(pipe_info);
+- return ret;
+-}
+-
+-static int ath10k_pci_post_rx(struct ath10k *ar)
++static void ath10k_pci_irq_sync(struct ath10k *ar)
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- struct ath10k_pci_pipe *pipe_info;
+- const struct ce_attr *attr;
+- int pipe_num, ret = 0;
+-
+- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
+- pipe_info = &ar_pci->pipe_info[pipe_num];
+- attr = &host_ce_config_wlan[pipe_num];
+-
+- if (attr->dest_nentries == 0)
+- continue;
+-
+- ret = ath10k_pci_post_rx_pipe(pipe_info,
+- attr->dest_nentries - 1);
+- if (ret) {
+- ath10k_warn("failed to post RX buffer for pipe %d: %d\n",
+- pipe_num, ret);
++ int i;
+
+- for (; pipe_num >= 0; pipe_num--) {
+- pipe_info = &ar_pci->pipe_info[pipe_num];
+- ath10k_pci_rx_pipe_cleanup(pipe_info);
+- }
+- return ret;
+- }
+- }
++ for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
++ synchronize_irq(ar_pci->pdev->irq + i);
++}
+
+- return 0;
++static void ath10k_pci_irq_enable(struct ath10k *ar)
++{
++ ath10k_ce_enable_interrupts(ar);
++ ath10k_pci_enable_legacy_irq(ar);
++ ath10k_pci_irq_msi_fw_unmask(ar);
+ }
+
+ static int ath10k_pci_hif_start(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- int ret, ret_early;
+-
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n");
+-
+- ath10k_pci_free_early_irq(ar);
+- ath10k_pci_kill_tasklet(ar);
+-
+- ret = ath10k_pci_request_irq(ar);
+- if (ret) {
+- ath10k_warn("failed to post RX buffers for all pipes: %d\n",
+- ret);
+- goto err_early_irq;
+- }
+-
+- ret = ath10k_pci_setup_ce_irq(ar);
+- if (ret) {
+- ath10k_warn("failed to setup CE interrupts: %d\n", ret);
+- goto err_stop;
+- }
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
+
+- /* Post buffers once to start things off. */
+- ret = ath10k_pci_post_rx(ar);
+- if (ret) {
+- ath10k_warn("failed to post RX buffers for all pipes: %d\n",
+- ret);
+- goto err_stop;
+- }
++ ath10k_pci_irq_enable(ar);
++ ath10k_pci_rx_post(ar);
+
+- ar_pci->started = 1;
+ return 0;
+-
+-err_stop:
+- ath10k_ce_disable_interrupts(ar);
+- ath10k_pci_free_irq(ar);
+- ath10k_pci_kill_tasklet(ar);
+-err_early_irq:
+- /* Though there should be no interrupts (device was reset)
+- * power_down() expects the early IRQ to be installed as per the
+- * driver lifecycle. */
+- ret_early = ath10k_pci_request_early_irq(ar);
+- if (ret_early)
+- ath10k_warn("failed to re-enable early irq: %d\n", ret_early);
+-
+- return ret;
+ }
+
+-static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
++static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
+ {
+ struct ath10k *ar;
+- struct ath10k_pci *ar_pci;
+- struct ath10k_ce_pipe *ce_hdl;
+- u32 buf_sz;
+- struct sk_buff *netbuf;
+- u32 ce_data;
++ struct ath10k_ce_pipe *ce_pipe;
++ struct ath10k_ce_ring *ce_ring;
++ struct sk_buff *skb;
++ int i;
+
+- buf_sz = pipe_info->buf_sz;
++ ar = pci_pipe->hif_ce_state;
++ ce_pipe = pci_pipe->ce_hdl;
++ ce_ring = ce_pipe->dest_ring;
+
+- /* Unused Copy Engine */
+- if (buf_sz == 0)
++ if (!ce_ring)
+ return;
+
+- ar = pipe_info->hif_ce_state;
+- ar_pci = ath10k_pci_priv(ar);
+-
+- if (!ar_pci->started)
++ if (!pci_pipe->buf_sz)
+ return;
+
+- ce_hdl = pipe_info->ce_hdl;
++ for (i = 0; i < ce_ring->nentries; i++) {
++ skb = ce_ring->per_transfer_context[i];
++ if (!skb)
++ continue;
++
++ ce_ring->per_transfer_context[i] = NULL;
+
+- while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
+- &ce_data) == 0) {
+- dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
+- netbuf->len + skb_tailroom(netbuf),
++ dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
++ skb->len + skb_tailroom(skb),
+ DMA_FROM_DEVICE);
+- dev_kfree_skb_any(netbuf);
++ dev_kfree_skb_any(skb);
+ }
+ }
+
+-static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
++static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
+ {
+ struct ath10k *ar;
+ struct ath10k_pci *ar_pci;
+- struct ath10k_ce_pipe *ce_hdl;
+- struct sk_buff *netbuf;
+- u32 ce_data;
+- unsigned int nbytes;
++ struct ath10k_ce_pipe *ce_pipe;
++ struct ath10k_ce_ring *ce_ring;
++ struct ce_desc *ce_desc;
++ struct sk_buff *skb;
+ unsigned int id;
+- u32 buf_sz;
++ int i;
+
+- buf_sz = pipe_info->buf_sz;
++ ar = pci_pipe->hif_ce_state;
++ ar_pci = ath10k_pci_priv(ar);
++ ce_pipe = pci_pipe->ce_hdl;
++ ce_ring = ce_pipe->src_ring;
+
+- /* Unused Copy Engine */
+- if (buf_sz == 0)
++ if (!ce_ring)
+ return;
+
+- ar = pipe_info->hif_ce_state;
+- ar_pci = ath10k_pci_priv(ar);
+-
+- if (!ar_pci->started)
++ if (!pci_pipe->buf_sz)
+ return;
+
+- ce_hdl = pipe_info->ce_hdl;
++ ce_desc = ce_ring->shadow_base;
++ if (WARN_ON(!ce_desc))
++ return;
+
+- while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
+- &ce_data, &nbytes, &id) == 0) {
+- /* no need to call tx completion for NULL pointers */
+- if (!netbuf)
++ for (i = 0; i < ce_ring->nentries; i++) {
++ skb = ce_ring->per_transfer_context[i];
++ if (!skb)
+ continue;
+
+- ar_pci->msg_callbacks_current.tx_completion(ar,
+- netbuf,
+- id);
++ ce_ring->per_transfer_context[i] = NULL;
++ id = MS(__le16_to_cpu(ce_desc[i].flags),
++ CE_DESC_FLAGS_META_DATA);
++
++ ar_pci->msg_callbacks_current.tx_completion(ar, skb);
+ }
+ }
+
+@@ -1264,38 +1321,32 @@ static void ath10k_pci_ce_deinit(struct
+ ath10k_ce_deinit_pipe(ar, i);
+ }
+
+-static void ath10k_pci_hif_stop(struct ath10k *ar)
++static void ath10k_pci_flush(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- int ret;
+-
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n");
+-
+- ret = ath10k_ce_disable_interrupts(ar);
+- if (ret)
+- ath10k_warn("failed to disable CE interrupts: %d\n", ret);
+-
+- ath10k_pci_free_irq(ar);
+ ath10k_pci_kill_tasklet(ar);
+-
+- ret = ath10k_pci_request_early_irq(ar);
+- if (ret)
+- ath10k_warn("failed to re-enable early irq: %d\n", ret);
+-
+- /* At this point, asynchronous threads are stopped, the target should
+- * not DMA nor interrupt. We process the leftovers and then free
+- * everything else up. */
+-
+ ath10k_pci_buffer_cleanup(ar);
++}
+
+- /* Make the sure the device won't access any structures on the host by
+- * resetting it. The device was fed with PCI CE ringbuffer
+- * configuration during init. If ringbuffers are freed and the device
+- * were to access them this could lead to memory corruption on the
+- * host. */
++static void ath10k_pci_hif_stop(struct ath10k *ar)
++{
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
++
++ /* Most likely the device has HTT Rx ring configured. The only way to
++ * prevent the device from accessing (and possible corrupting) host
++ * memory is to reset the chip now.
++ *
++ * There's also no known way of masking MSI interrupts on the device.
++ * For ranged MSI the CE-related interrupts can be masked. However
++ * regardless how many MSI interrupts are assigned the first one
++ * is always used for firmware indications (crashes) and cannot be
++ * masked. To prevent the device from asserting the interrupt reset it
++ * before proceeding with cleanup.
++ */
+ ath10k_pci_warm_reset(ar);
+
+- ar_pci->started = 0;
++ ath10k_pci_irq_disable(ar);
++ ath10k_pci_irq_sync(ar);
++ ath10k_pci_flush(ar);
+ }
+
+ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
+@@ -1346,11 +1397,9 @@ static int ath10k_pci_hif_exchange_bmi_m
+ xfer.wait_for_resp = true;
+ xfer.resp_len = 0;
+
+- ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
++ ath10k_ce_rx_post_buf(ce_rx, &xfer, resp_paddr);
+ }
+
+- init_completion(&xfer.done);
+-
+ ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
+ if (ret)
+ goto err_resp;
+@@ -1401,14 +1450,12 @@ static void ath10k_pci_bmi_send_done(str
+ &nbytes, &transfer_id))
+ return;
+
+- if (xfer->wait_for_resp)
+- return;
+-
+- complete(&xfer->done);
++ xfer->tx_done = true;
+ }
+
+ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
+ {
++ struct ath10k *ar = ce_state->ar;
+ struct bmi_xfer *xfer;
+ u32 ce_data;
+ unsigned int nbytes;
+@@ -1419,13 +1466,16 @@ static void ath10k_pci_bmi_recv_data(str
+ &nbytes, &transfer_id, &flags))
+ return;
+
++ if (WARN_ON_ONCE(!xfer))
++ return;
++
+ if (!xfer->wait_for_resp) {
+- ath10k_warn("unexpected: BMI data received; ignoring\n");
++ ath10k_warn(ar, "unexpected: BMI data received; ignoring\n");
+ return;
+ }
+
+ xfer->resp_len = nbytes;
+- complete(&xfer->done);
++ xfer->rx_done = true;
+ }
+
+ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
+@@ -1438,7 +1488,7 @@ static int ath10k_pci_bmi_wait(struct at
+ ath10k_pci_bmi_send_done(tx_pipe);
+ ath10k_pci_bmi_recv_data(rx_pipe);
+
+- if (completion_done(&xfer->done))
++ if (xfer->tx_done && (xfer->rx_done == xfer->wait_for_resp))
+ return 0;
+
+ schedule();
+@@ -1448,131 +1498,48 @@ static int ath10k_pci_bmi_wait(struct at
+ }
+
+ /*
+- * Map from service/endpoint to Copy Engine.
+- * This table is derived from the CE_PCI TABLE, above.
+- * It is passed to the Target at startup for use by firmware.
+- */
+-static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 3,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 2,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 3,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 2,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 3,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 2,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 3,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 2,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_CONTROL,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 3,
+- },
+- {
+- ATH10K_HTC_SVC_ID_WMI_CONTROL,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 2,
+- },
+- {
+- ATH10K_HTC_SVC_ID_RSVD_CTRL,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 0, /* could be moved to 3 (share with WMI) */
+- },
+- {
+- ATH10K_HTC_SVC_ID_RSVD_CTRL,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 1,
+- },
+- {
+- ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 0,
+- },
+- {
+- ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 1,
+- },
+- {
+- ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+- PIPEDIR_OUT, /* out = UL = host -> target */
+- 4,
+- },
+- {
+- ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+- PIPEDIR_IN, /* in = DL = target -> host */
+- 1,
+- },
+-
+- /* (Additions here) */
+-
+- { /* Must be last */
+- 0,
+- 0,
+- 0,
+- },
+-};
+-
+-/*
+ * Send an interrupt to the device to wake up the Target CPU
+ * so it has an opportunity to notice any changed state.
+ */
+ static int ath10k_pci_wake_target_cpu(struct ath10k *ar)
+ {
+- int ret;
+- u32 core_ctrl;
++ u32 addr, val;
+
+- ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS |
+- CORE_CTRL_ADDRESS,
+- &core_ctrl);
+- if (ret) {
+- ath10k_warn("failed to read core_ctrl: %d\n", ret);
+- return ret;
+- }
++ addr = SOC_CORE_BASE_ADDRESS | CORE_CTRL_ADDRESS;
++ val = ath10k_pci_read32(ar, addr);
++ val |= CORE_CTRL_CPU_INTR_MASK;
++ ath10k_pci_write32(ar, addr, val);
+
+- /* A_INUM_FIRMWARE interrupt to Target CPU */
+- core_ctrl |= CORE_CTRL_CPU_INTR_MASK;
++ return 0;
++}
+
+- ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS |
+- CORE_CTRL_ADDRESS,
+- core_ctrl);
+- if (ret) {
+- ath10k_warn("failed to set target CPU interrupt mask: %d\n",
+- ret);
+- return ret;
++static int ath10k_pci_get_num_banks(struct ath10k *ar)
++{
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++
++ switch (ar_pci->pdev->device) {
++ case QCA988X_2_0_DEVICE_ID:
++ return 1;
++ case QCA6174_2_1_DEVICE_ID:
++ switch (MS(ar->chip_id, SOC_CHIP_ID_REV)) {
++ case QCA6174_HW_1_0_CHIP_ID_REV:
++ case QCA6174_HW_1_1_CHIP_ID_REV:
++ return 3;
++ case QCA6174_HW_1_3_CHIP_ID_REV:
++ return 2;
++ case QCA6174_HW_2_1_CHIP_ID_REV:
++ case QCA6174_HW_2_2_CHIP_ID_REV:
++ return 6;
++ case QCA6174_HW_3_0_CHIP_ID_REV:
++ case QCA6174_HW_3_1_CHIP_ID_REV:
++ case QCA6174_HW_3_2_CHIP_ID_REV:
++ return 9;
++ }
++ break;
+ }
+
+- return 0;
++ ath10k_warn(ar, "unknown number of banks, assuming 1\n");
++ return 1;
+ }
+
+ static int ath10k_pci_init_config(struct ath10k *ar)
+@@ -1593,144 +1560,162 @@ static int ath10k_pci_init_config(struct
+ host_interest_item_address(HI_ITEM(hi_interconnect_state));
+
+ /* Supply Target-side CE configuration */
+- ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr,
+- &pcie_state_targ_addr);
++ ret = ath10k_pci_diag_read32(ar, interconnect_targ_addr,
++ &pcie_state_targ_addr);
+ if (ret != 0) {
+- ath10k_err("Failed to get pcie state addr: %d\n", ret);
++ ath10k_err(ar, "Failed to get pcie state addr: %d\n", ret);
+ return ret;
+ }
+
+ if (pcie_state_targ_addr == 0) {
+ ret = -EIO;
+- ath10k_err("Invalid pcie state addr\n");
++ ath10k_err(ar, "Invalid pcie state addr\n");
+ return ret;
+ }
+
+- ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
++ ret = ath10k_pci_diag_read32(ar, (pcie_state_targ_addr +
+ offsetof(struct pcie_state,
+- pipe_cfg_addr),
+- &pipe_cfg_targ_addr);
++ pipe_cfg_addr)),
++ &pipe_cfg_targ_addr);
+ if (ret != 0) {
+- ath10k_err("Failed to get pipe cfg addr: %d\n", ret);
++ ath10k_err(ar, "Failed to get pipe cfg addr: %d\n", ret);
+ return ret;
+ }
+
+ if (pipe_cfg_targ_addr == 0) {
+ ret = -EIO;
+- ath10k_err("Invalid pipe cfg addr\n");
++ ath10k_err(ar, "Invalid pipe cfg addr\n");
+ return ret;
+ }
+
+ ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr,
+- target_ce_config_wlan,
+- sizeof(target_ce_config_wlan));
++ target_ce_config_wlan,
++ sizeof(target_ce_config_wlan));
+
+ if (ret != 0) {
+- ath10k_err("Failed to write pipe cfg: %d\n", ret);
++ ath10k_err(ar, "Failed to write pipe cfg: %d\n", ret);
+ return ret;
+ }
+
+- ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
++ ret = ath10k_pci_diag_read32(ar, (pcie_state_targ_addr +
+ offsetof(struct pcie_state,
+- svc_to_pipe_map),
+- &svc_to_pipe_map);
++ svc_to_pipe_map)),
++ &svc_to_pipe_map);
+ if (ret != 0) {
+- ath10k_err("Failed to get svc/pipe map: %d\n", ret);
++ ath10k_err(ar, "Failed to get svc/pipe map: %d\n", ret);
+ return ret;
+ }
+
+ if (svc_to_pipe_map == 0) {
+ ret = -EIO;
+- ath10k_err("Invalid svc_to_pipe map\n");
++ ath10k_err(ar, "Invalid svc_to_pipe map\n");
+ return ret;
+ }
+
+ ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map,
+- target_service_to_ce_map_wlan,
+- sizeof(target_service_to_ce_map_wlan));
++ target_service_to_ce_map_wlan,
++ sizeof(target_service_to_ce_map_wlan));
+ if (ret != 0) {
+- ath10k_err("Failed to write svc/pipe map: %d\n", ret);
++ ath10k_err(ar, "Failed to write svc/pipe map: %d\n", ret);
+ return ret;
+ }
+
+- ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
++ ret = ath10k_pci_diag_read32(ar, (pcie_state_targ_addr +
+ offsetof(struct pcie_state,
+- config_flags),
+- &pcie_config_flags);
++ config_flags)),
++ &pcie_config_flags);
+ if (ret != 0) {
+- ath10k_err("Failed to get pcie config_flags: %d\n", ret);
++ ath10k_err(ar, "Failed to get pcie config_flags: %d\n", ret);
+ return ret;
+ }
+
+ pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
+
+- ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr +
+- offsetof(struct pcie_state, config_flags),
+- &pcie_config_flags,
+- sizeof(pcie_config_flags));
++ ret = ath10k_pci_diag_write32(ar, (pcie_state_targ_addr +
++ offsetof(struct pcie_state,
++ config_flags)),
++ pcie_config_flags);
+ if (ret != 0) {
+- ath10k_err("Failed to write pcie config_flags: %d\n", ret);
++ ath10k_err(ar, "Failed to write pcie config_flags: %d\n", ret);
+ return ret;
+ }
+
+ /* configure early allocation */
+ ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc));
+
+- ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value);
++ ret = ath10k_pci_diag_read32(ar, ealloc_targ_addr, &ealloc_value);
+ if (ret != 0) {
+- ath10k_err("Faile to get early alloc val: %d\n", ret);
++ ath10k_err(ar, "Faile to get early alloc val: %d\n", ret);
+ return ret;
+ }
+
+ /* first bank is switched to IRAM */
+ ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
+ HI_EARLY_ALLOC_MAGIC_MASK);
+- ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
++ ealloc_value |= ((ath10k_pci_get_num_banks(ar) <<
++ HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
+ HI_EARLY_ALLOC_IRAM_BANKS_MASK);
+
+- ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value);
++ ret = ath10k_pci_diag_write32(ar, ealloc_targ_addr, ealloc_value);
+ if (ret != 0) {
+- ath10k_err("Failed to set early alloc val: %d\n", ret);
++ ath10k_err(ar, "Failed to set early alloc val: %d\n", ret);
+ return ret;
+ }
+
+ /* Tell Target to proceed with initialization */
+ flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2));
+
+- ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value);
++ ret = ath10k_pci_diag_read32(ar, flag2_targ_addr, &flag2_value);
+ if (ret != 0) {
+- ath10k_err("Failed to get option val: %d\n", ret);
++ ath10k_err(ar, "Failed to get option val: %d\n", ret);
+ return ret;
+ }
+
+ flag2_value |= HI_OPTION_EARLY_CFG_DONE;
+
+- ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value);
++ ret = ath10k_pci_diag_write32(ar, flag2_targ_addr, flag2_value);
+ if (ret != 0) {
+- ath10k_err("Failed to set option val: %d\n", ret);
++ ath10k_err(ar, "Failed to set option val: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+-static int ath10k_pci_alloc_ce(struct ath10k *ar)
++static int ath10k_pci_alloc_pipes(struct ath10k *ar)
+ {
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct ath10k_pci_pipe *pipe;
+ int i, ret;
+
+ for (i = 0; i < CE_COUNT; i++) {
+- ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
++ pipe = &ar_pci->pipe_info[i];
++ pipe->ce_hdl = &ar_pci->ce_states[i];
++ pipe->pipe_num = i;
++ pipe->hif_ce_state = ar;
++
++ ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i],
++ ath10k_pci_ce_send_done,
++ ath10k_pci_ce_recv_data);
+ if (ret) {
+- ath10k_err("failed to allocate copy engine pipe %d: %d\n",
++ ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n",
+ i, ret);
+ return ret;
+ }
++
++ /* Last CE is Diagnostic Window */
++ if (i == CE_COUNT - 1) {
++ ar_pci->ce_diag = pipe->ce_hdl;
++ continue;
++ }
++
++ pipe->buf_sz = (size_t)(host_ce_config_wlan[i].src_sz_max);
+ }
+
+ return 0;
+ }
+
+-static void ath10k_pci_free_ce(struct ath10k *ar)
++static void ath10k_pci_free_pipes(struct ath10k *ar)
+ {
+ int i;
+
+@@ -1738,305 +1723,319 @@ static void ath10k_pci_free_ce(struct at
+ ath10k_ce_free_pipe(ar, i);
+ }
+
+-static int ath10k_pci_ce_init(struct ath10k *ar)
++static int ath10k_pci_init_pipes(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- struct ath10k_pci_pipe *pipe_info;
+- const struct ce_attr *attr;
+- int pipe_num, ret;
+-
+- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
+- pipe_info = &ar_pci->pipe_info[pipe_num];
+- pipe_info->ce_hdl = &ar_pci->ce_states[pipe_num];
+- pipe_info->pipe_num = pipe_num;
+- pipe_info->hif_ce_state = ar;
+- attr = &host_ce_config_wlan[pipe_num];
++ int i, ret;
+
+- ret = ath10k_ce_init_pipe(ar, pipe_num, attr);
++ for (i = 0; i < CE_COUNT; i++) {
++ ret = ath10k_ce_init_pipe(ar, i, &host_ce_config_wlan[i]);
+ if (ret) {
+- ath10k_err("failed to initialize copy engine pipe %d: %d\n",
+- pipe_num, ret);
++ ath10k_err(ar, "failed to initialize copy engine pipe %d: %d\n",
++ i, ret);
+ return ret;
+ }
+-
+- if (pipe_num == CE_COUNT - 1) {
+- /*
+- * Reserve the ultimate CE for
+- * diagnostic Window support
+- */
+- ar_pci->ce_diag = pipe_info->ce_hdl;
+- continue;
+- }
+-
+- pipe_info->buf_sz = (size_t) (attr->src_sz_max);
+ }
+
+ return 0;
+ }
+
+-static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
++static bool ath10k_pci_has_fw_crashed(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- u32 fw_indicator;
+-
+- ath10k_pci_wake(ar);
+-
+- fw_indicator = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
+-
+- if (fw_indicator & FW_IND_EVENT_PENDING) {
+- /* ACK: clear Target-side pending event */
+- ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
+- fw_indicator & ~FW_IND_EVENT_PENDING);
+-
+- if (ar_pci->started) {
+- ath10k_pci_hif_dump_area(ar);
+- } else {
+- /*
+- * Probable Target failure before we're prepared
+- * to handle it. Generally unexpected.
+- */
+- ath10k_warn("early firmware event indicated\n");
+- }
+- }
+-
+- ath10k_pci_sleep(ar);
++ return ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS) &
++ FW_IND_EVENT_PENDING;
+ }
+
+-static int ath10k_pci_warm_reset(struct ath10k *ar)
++static void ath10k_pci_fw_crashed_clear(struct ath10k *ar)
+ {
+- int ret = 0;
+ u32 val;
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset\n");
++ val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
++ val &= ~FW_IND_EVENT_PENDING;
++ ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, val);
++}
+
+- ret = ath10k_do_pci_wake(ar);
+- if (ret) {
+- ath10k_err("failed to wake up target: %d\n", ret);
+- return ret;
+- }
++/* this function effectively clears target memory controller assert line */
++static void ath10k_pci_warm_reset_si0(struct ath10k *ar)
++{
++ u32 val;
+
+- /* debug */
+- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+- PCIE_INTR_CAUSE_ADDRESS);
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
++ val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
++ ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
++ val | SOC_RESET_CONTROL_SI0_RST_MASK);
++ val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+
+- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+- CPU_INTR_ADDRESS);
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+- val);
++ msleep(10);
+
+- /* disable pending irqs */
+- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+- PCIE_INTR_ENABLE_ADDRESS, 0);
++ val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
++ ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
++ val & ~SOC_RESET_CONTROL_SI0_RST_MASK);
++ val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+
+- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+- PCIE_INTR_CLR_ADDRESS, ~0);
++ msleep(10);
++}
+
+- msleep(100);
++static void ath10k_pci_warm_reset_cpu(struct ath10k *ar)
++{
++ u32 val;
+
+- /* clear fw indicator */
+ ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
+
+- /* clear target LF timer interrupts */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+- SOC_LF_TIMER_CONTROL0_ADDRESS);
+- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
+- SOC_LF_TIMER_CONTROL0_ADDRESS,
+- val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
++ SOC_RESET_CONTROL_ADDRESS);
++ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
++ val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
++}
++
++static void ath10k_pci_warm_reset_ce(struct ath10k *ar)
++{
++ u32 val;
+
+- /* reset CE */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
++
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CE_RST_MASK);
+- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+- SOC_RESET_CONTROL_ADDRESS);
+ msleep(10);
+-
+- /* unreset CE */
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+- SOC_RESET_CONTROL_ADDRESS);
+- msleep(10);
++}
+
+- /* debug */
+- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+- PCIE_INTR_CAUSE_ADDRESS);
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+-
+- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+- CPU_INTR_ADDRESS);
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+- val);
++static void ath10k_pci_warm_reset_clear_lf(struct ath10k *ar)
++{
++ u32 val;
+
+- /* CPU warm reset */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+- SOC_RESET_CONTROL_ADDRESS);
+- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+- val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
++ SOC_LF_TIMER_CONTROL0_ADDRESS);
++ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
++ SOC_LF_TIMER_CONTROL0_ADDRESS,
++ val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
++}
+
+- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+- SOC_RESET_CONTROL_ADDRESS);
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val);
++static int ath10k_pci_warm_reset(struct ath10k *ar)
++{
++ int ret;
+
+- msleep(100);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n");
++ spin_lock_bh(&ar->data_lock);
++ ar->stats.fw_warm_reset_counter++;
++ spin_unlock_bh(&ar->data_lock);
++
++ ath10k_pci_irq_disable(ar);
++
++ /* Make sure the target CPU is not doing anything dangerous, e.g. if it
++ * were to access copy engine while host performs copy engine reset
++ * then it is possible for the device to confuse pci-e controller to
++ * the point of bringing host system to a complete stop (i.e. hang).
++ */
++ ath10k_pci_warm_reset_si0(ar);
++ ath10k_pci_warm_reset_cpu(ar);
++ ath10k_pci_init_pipes(ar);
++ ath10k_pci_wait_for_target_init(ar);
++
++ ath10k_pci_warm_reset_clear_lf(ar);
++ ath10k_pci_warm_reset_ce(ar);
++ ath10k_pci_warm_reset_cpu(ar);
++ ath10k_pci_init_pipes(ar);
+
+- ath10k_do_pci_sleep(ar);
+- return ret;
++ ret = ath10k_pci_wait_for_target_init(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to wait for target init: %d\n", ret);
++ return ret;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset complete\n");
++
++ return 0;
+ }
+
+-static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
++static int ath10k_pci_qca988x_chip_reset(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- const char *irq_mode;
+- int ret;
++ int i, ret;
++ u32 val;
+
+- /*
+- * Bring the target up cleanly.
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot 988x chip reset\n");
++
++ /* Some hardware revisions (e.g. CUS223v2) has issues with cold reset.
++ * It is thus preferred to use warm reset which is safer but may not be
++ * able to recover the device from all possible fail scenarios.
+ *
+- * The target may be in an undefined state with an AUX-powered Target
+- * and a Host in WoW mode. If the Host crashes, loses power, or is
+- * restarted (without unloading the driver) then the Target is left
+- * (aux) powered and running. On a subsequent driver load, the Target
+- * is in an unexpected state. We try to catch that here in order to
+- * reset the Target and retry the probe.
++ * Warm reset doesn't always work on first try so attempt it a few
++ * times before giving up.
+ */
+- if (cold_reset)
+- ret = ath10k_pci_cold_reset(ar);
+- else
++ for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
+ ret = ath10k_pci_warm_reset(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to warm reset attempt %d of %d: %d\n",
++ i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS,
++ ret);
++ continue;
++ }
++
++ /* FIXME: Sometimes copy engine doesn't recover after warm
++ * reset. In most cases this needs cold reset. In some of these
++ * cases the device is in such a state that a cold reset may
++ * lock up the host.
++ *
++ * Reading any host interest register via copy engine is
++ * sufficient to verify if device is capable of booting
++ * firmware blob.
++ */
++ ret = ath10k_pci_init_pipes(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to init copy engine: %d\n",
++ ret);
++ continue;
++ }
++
++ ret = ath10k_pci_diag_read32(ar, QCA988X_HOST_INTEREST_ADDRESS,
++ &val);
++ if (ret) {
++ ath10k_warn(ar, "failed to poke copy engine: %d\n",
++ ret);
++ continue;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (warm)\n");
++ return 0;
++ }
+
++ if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY) {
++ ath10k_warn(ar, "refusing cold reset as requested\n");
++ return -EPERM;
++ }
++
++ ret = ath10k_pci_cold_reset(ar);
+ if (ret) {
+- ath10k_err("failed to reset target: %d\n", ret);
+- goto err;
++ ath10k_warn(ar, "failed to cold reset: %d\n", ret);
++ return ret;
++ }
++
++ ret = ath10k_pci_wait_for_target_init(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
++ ret);
++ return ret;
+ }
+
+- if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+- /* Force AWAKE forever */
+- ath10k_do_pci_wake(ar);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca988x chip reset complete (cold)\n");
+
+- ret = ath10k_pci_ce_init(ar);
++ return 0;
++}
++
++static int ath10k_pci_qca6174_chip_reset(struct ath10k *ar)
++{
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset\n");
++
++ /* FIXME: QCA6174 requires cold + warm reset to work. */
++
++ ret = ath10k_pci_cold_reset(ar);
+ if (ret) {
+- ath10k_err("failed to initialize CE: %d\n", ret);
+- goto err_ps;
++ ath10k_warn(ar, "failed to cold reset: %d\n", ret);
++ return ret;
+ }
+
+- ret = ath10k_ce_disable_interrupts(ar);
++ ret = ath10k_pci_wait_for_target_init(ar);
+ if (ret) {
+- ath10k_err("failed to disable CE interrupts: %d\n", ret);
+- goto err_ce;
++ ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
++ ret);
++ return ret;
+ }
+
+- ret = ath10k_pci_init_irq(ar);
++ ret = ath10k_pci_warm_reset(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to warm reset: %d\n", ret);
++ return ret;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset complete (cold)\n");
++
++ return 0;
++}
++
++static int ath10k_pci_chip_reset(struct ath10k *ar)
++{
++ if (QCA_REV_988X(ar))
++ return ath10k_pci_qca988x_chip_reset(ar);
++ else if (QCA_REV_6174(ar))
++ return ath10k_pci_qca6174_chip_reset(ar);
++ else
++ return -ENOTSUPP;
++}
++
++static int ath10k_pci_hif_power_up(struct ath10k *ar)
++{
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n");
++
++ ret = ath10k_pci_wake(ar);
++ if (ret) {
++ ath10k_err(ar, "failed to wake up target: %d\n", ret);
++ return ret;
++ }
++
++ /*
++ * Bring the target up cleanly.
++ *
++ * The target may be in an undefined state with an AUX-powered Target
++ * and a Host in WoW mode. If the Host crashes, loses power, or is
++ * restarted (without unloading the driver) then the Target is left
++ * (aux) powered and running. On a subsequent driver load, the Target
++ * is in an unexpected state. We try to catch that here in order to
++ * reset the Target and retry the probe.
++ */
++ ret = ath10k_pci_chip_reset(ar);
+ if (ret) {
+- ath10k_err("failed to init irqs: %d\n", ret);
+- goto err_ce;
+- }
++ if (ath10k_pci_has_fw_crashed(ar)) {
++ ath10k_warn(ar, "firmware crashed during chip reset\n");
++ ath10k_pci_fw_crashed_clear(ar);
++ ath10k_pci_fw_crashed_dump(ar);
++ }
+
+- ret = ath10k_pci_request_early_irq(ar);
+- if (ret) {
+- ath10k_err("failed to request early irq: %d\n", ret);
+- goto err_deinit_irq;
++ ath10k_err(ar, "failed to reset chip: %d\n", ret);
++ goto err_sleep;
+ }
+
+- ret = ath10k_pci_wait_for_target_init(ar);
++ ret = ath10k_pci_init_pipes(ar);
+ if (ret) {
+- ath10k_err("failed to wait for target to init: %d\n", ret);
+- goto err_free_early_irq;
++ ath10k_err(ar, "failed to initialize CE: %d\n", ret);
++ goto err_sleep;
+ }
+
+ ret = ath10k_pci_init_config(ar);
+ if (ret) {
+- ath10k_err("failed to setup init config: %d\n", ret);
+- goto err_free_early_irq;
++ ath10k_err(ar, "failed to setup init config: %d\n", ret);
++ goto err_ce;
+ }
+
+ ret = ath10k_pci_wake_target_cpu(ar);
+ if (ret) {
+- ath10k_err("could not wake up target CPU: %d\n", ret);
+- goto err_free_early_irq;
++ ath10k_err(ar, "could not wake up target CPU: %d\n", ret);
++ goto err_ce;
+ }
+
+- if (ar_pci->num_msi_intrs > 1)
+- irq_mode = "MSI-X";
+- else if (ar_pci->num_msi_intrs == 1)
+- irq_mode = "MSI";
+- else
+- irq_mode = "legacy";
+-
+- if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+- ath10k_info("pci irq %s irq_mode %d reset_mode %d\n",
+- irq_mode, ath10k_pci_irq_mode,
+- ath10k_pci_reset_mode);
+-
+ return 0;
+
+-err_free_early_irq:
+- ath10k_pci_free_early_irq(ar);
+-err_deinit_irq:
+- ath10k_pci_deinit_irq(ar);
+ err_ce:
+ ath10k_pci_ce_deinit(ar);
+- ath10k_pci_warm_reset(ar);
+-err_ps:
+- if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+- ath10k_do_pci_sleep(ar);
+-err:
+- return ret;
+-}
+-
+-static int ath10k_pci_hif_power_up(struct ath10k *ar)
+-{
+- int ret;
+-
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power up\n");
+-
+- /*
+- * Hardware CUS232 version 2 has some issues with cold reset and the
+- * preferred (and safer) way to perform a device reset is through a
+- * warm reset.
+- *
+- * Warm reset doesn't always work though (notably after a firmware
+- * crash) so fall back to cold reset if necessary.
+- */
+- ret = __ath10k_pci_hif_power_up(ar, false);
+- if (ret) {
+- ath10k_warn("failed to power up target using warm reset: %d\n",
+- ret);
+-
+- if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY)
+- return ret;
+
+- ath10k_warn("trying cold reset\n");
+-
+- ret = __ath10k_pci_hif_power_up(ar, true);
+- if (ret) {
+- ath10k_err("failed to power up target using cold reset too (%d)\n",
+- ret);
+- return ret;
+- }
+- }
+-
+- return 0;
++err_sleep:
++ ath10k_pci_sleep(ar);
++ return ret;
+ }
+
+ static void ath10k_pci_hif_power_down(struct ath10k *ar)
+ {
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n");
+-
+- ath10k_pci_free_early_irq(ar);
+- ath10k_pci_kill_tasklet(ar);
+- ath10k_pci_deinit_irq(ar);
+- ath10k_pci_ce_deinit(ar);
+- ath10k_pci_warm_reset(ar);
++ /* Currently hif_power_up performs effectively a reset and hif_stop
++ * resets the chip as well so there's no point in resetting here.
++ */
+
+- if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+- ath10k_do_pci_sleep(ar);
++ ath10k_pci_sleep(ar);
+ }
+
+ #ifdef CONFIG_PM
+@@ -2090,6 +2089,8 @@ static int ath10k_pci_hif_resume(struct
+
+ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
+ .tx_sg = ath10k_pci_hif_tx_sg,
++ .diag_read = ath10k_pci_hif_diag_read,
++ .diag_write = ath10k_pci_diag_write_mem,
+ .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
+ .start = ath10k_pci_hif_start,
+ .stop = ath10k_pci_hif_stop,
+@@ -2100,6 +2101,8 @@ static const struct ath10k_hif_ops ath10
+ .get_free_queue_number = ath10k_pci_hif_get_free_queue_number,
+ .power_up = ath10k_pci_hif_power_up,
+ .power_down = ath10k_pci_hif_power_down,
++ .read32 = ath10k_pci_read32,
++ .write32 = ath10k_pci_write32,
+ #ifdef CONFIG_PM
+ .suspend = ath10k_pci_hif_suspend,
+ .resume = ath10k_pci_hif_resume,
+@@ -2118,7 +2121,14 @@ static void ath10k_msi_err_tasklet(unsig
+ {
+ struct ath10k *ar = (struct ath10k *)data;
+
+- ath10k_pci_fw_interrupt_handler(ar);
++ if (!ath10k_pci_has_fw_crashed(ar)) {
++ ath10k_warn(ar, "received unsolicited fw crash interrupt\n");
++ return;
++ }
++
++ ath10k_pci_irq_disable(ar);
++ ath10k_pci_fw_crashed_clear(ar);
++ ath10k_pci_fw_crashed_dump(ar);
+ }
+
+ /*
+@@ -2132,7 +2142,8 @@ static irqreturn_t ath10k_pci_per_engine
+ int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL;
+
+ if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_pci->pipe_info)) {
+- ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id);
++ ath10k_warn(ar, "unexpected/invalid irq %d ce_id %d\n", irq,
++ ce_id);
+ return IRQ_HANDLED;
+ }
+
+@@ -2179,39 +2190,18 @@ static irqreturn_t ath10k_pci_interrupt_
+ return IRQ_HANDLED;
+ }
+
+-static void ath10k_pci_early_irq_tasklet(unsigned long data)
++static void ath10k_pci_tasklet(unsigned long data)
+ {
+ struct ath10k *ar = (struct ath10k *)data;
+- u32 fw_ind;
+- int ret;
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+- ret = ath10k_pci_wake(ar);
+- if (ret) {
+- ath10k_warn("failed to wake target in early irq tasklet: %d\n",
+- ret);
++ if (ath10k_pci_has_fw_crashed(ar)) {
++ ath10k_pci_irq_disable(ar);
++ ath10k_pci_fw_crashed_clear(ar);
++ ath10k_pci_fw_crashed_dump(ar);
+ return;
+ }
+
+- fw_ind = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
+- if (fw_ind & FW_IND_EVENT_PENDING) {
+- ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
+- fw_ind & ~FW_IND_EVENT_PENDING);
+-
+- /* Some structures are unavailable during early boot or at
+- * driver teardown so just print that the device has crashed. */
+- ath10k_warn("device crashed - no diagnostics available\n");
+- }
+-
+- ath10k_pci_sleep(ar);
+- ath10k_pci_enable_legacy_irq(ar);
+-}
+-
+-static void ath10k_pci_tasklet(unsigned long data)
+-{
+- struct ath10k *ar = (struct ath10k *)data;
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+-
+- ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
+ ath10k_ce_per_engine_service_any(ar);
+
+ /* Re-enable legacy irq that was disabled in the irq handler */
+@@ -2228,7 +2218,7 @@ static int ath10k_pci_request_irq_msix(s
+ ath10k_pci_msi_fw_handler,
+ IRQF_SHARED, "ath10k_pci", ar);
+ if (ret) {
+- ath10k_warn("failed to request MSI-X fw irq %d: %d\n",
++ ath10k_warn(ar, "failed to request MSI-X fw irq %d: %d\n",
+ ar_pci->pdev->irq + MSI_ASSIGN_FW, ret);
+ return ret;
+ }
+@@ -2238,7 +2228,7 @@ static int ath10k_pci_request_irq_msix(s
+ ath10k_pci_per_engine_handler,
+ IRQF_SHARED, "ath10k_pci", ar);
+ if (ret) {
+- ath10k_warn("failed to request MSI-X ce irq %d: %d\n",
++ ath10k_warn(ar, "failed to request MSI-X ce irq %d: %d\n",
+ ar_pci->pdev->irq + i, ret);
+
+ for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--)
+@@ -2261,7 +2251,7 @@ static int ath10k_pci_request_irq_msi(st
+ ath10k_pci_interrupt_handler,
+ IRQF_SHARED, "ath10k_pci", ar);
+ if (ret) {
+- ath10k_warn("failed to request MSI irq %d: %d\n",
++ ath10k_warn(ar, "failed to request MSI irq %d: %d\n",
+ ar_pci->pdev->irq, ret);
+ return ret;
+ }
+@@ -2278,7 +2268,7 @@ static int ath10k_pci_request_irq_legacy
+ ath10k_pci_interrupt_handler,
+ IRQF_SHARED, "ath10k_pci", ar);
+ if (ret) {
+- ath10k_warn("failed to request legacy irq %d: %d\n",
++ ath10k_warn(ar, "failed to request legacy irq %d: %d\n",
+ ar_pci->pdev->irq, ret);
+ return ret;
+ }
+@@ -2299,7 +2289,7 @@ static int ath10k_pci_request_irq(struct
+ return ath10k_pci_request_irq_msix(ar);
+ }
+
+- ath10k_warn("unknown irq configuration upon request\n");
++ ath10k_warn(ar, "unknown irq configuration upon request\n");
+ return -EINVAL;
+ }
+
+@@ -2322,8 +2312,6 @@ static void ath10k_pci_init_irq_tasklets
+ tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar);
+ tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
+ (unsigned long)ar);
+- tasklet_init(&ar_pci->early_irq_tasklet, ath10k_pci_early_irq_tasklet,
+- (unsigned long)ar);
+
+ for (i = 0; i < CE_COUNT; i++) {
+ ar_pci->pipe_info[i].ar_pci = ar_pci;
+@@ -2335,21 +2323,19 @@ static void ath10k_pci_init_irq_tasklets
+ static int ath10k_pci_init_irq(struct ath10k *ar)
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+- bool msix_supported = test_bit(ATH10K_PCI_FEATURE_MSI_X,
+- ar_pci->features);
+ int ret;
+
+ ath10k_pci_init_irq_tasklets(ar);
+
+- if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO &&
+- !test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+- ath10k_info("limiting irq mode to: %d\n", ath10k_pci_irq_mode);
++ if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO)
++ ath10k_info(ar, "limiting irq mode to: %d\n",
++ ath10k_pci_irq_mode);
+
+ /* Try MSI-X */
+- if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) {
++ if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO) {
+ ar_pci->num_msi_intrs = MSI_NUM_REQUEST;
+ ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs,
+- ar_pci->num_msi_intrs);
++ ar_pci->num_msi_intrs);
+ if (ret > 0)
+ return 0;
+
+@@ -2376,34 +2362,16 @@ static int ath10k_pci_init_irq(struct at
+ * synchronization checking. */
+ ar_pci->num_msi_intrs = 0;
+
+- ret = ath10k_pci_wake(ar);
+- if (ret) {
+- ath10k_warn("failed to wake target: %d\n", ret);
+- return ret;
+- }
+-
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+ PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
+- ath10k_pci_sleep(ar);
+
+ return 0;
+ }
+
+-static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar)
++static void ath10k_pci_deinit_irq_legacy(struct ath10k *ar)
+ {
+- int ret;
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret) {
+- ath10k_warn("failed to wake target: %d\n", ret);
+- return ret;
+- }
+-
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+ 0);
+- ath10k_pci_sleep(ar);
+-
+- return 0;
+ }
+
+ static int ath10k_pci_deinit_irq(struct ath10k *ar)
+@@ -2412,7 +2380,8 @@ static int ath10k_pci_deinit_irq(struct
+
+ switch (ar_pci->num_msi_intrs) {
+ case 0:
+- return ath10k_pci_deinit_irq_legacy(ar);
++ ath10k_pci_deinit_irq_legacy(ar);
++ return 0;
+ case 1:
+ /* fall-through */
+ case MSI_NUM_REQUEST:
+@@ -2422,7 +2391,7 @@ static int ath10k_pci_deinit_irq(struct
+ pci_disable_msi(ar_pci->pdev);
+ }
+
+- ath10k_warn("unknown irq configuration upon deinit\n");
++ ath10k_warn(ar, "unknown irq configuration upon deinit\n");
+ return -EINVAL;
+ }
+
+@@ -2430,23 +2399,17 @@ static int ath10k_pci_wait_for_target_in
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ unsigned long timeout;
+- int ret;
+ u32 val;
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot waiting target to initialise\n");
+-
+- ret = ath10k_pci_wake(ar);
+- if (ret) {
+- ath10k_err("failed to wake up target for init: %d\n", ret);
+- return ret;
+- }
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot waiting target to initialise\n");
+
+ timeout = jiffies + msecs_to_jiffies(ATH10K_PCI_TARGET_WAIT);
+
+ do {
+ val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot target indicator %x\n", val);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target indicator %x\n",
++ val);
+
+ /* target should never return this */
+ if (val == 0xffffffff)
+@@ -2461,52 +2424,46 @@ static int ath10k_pci_wait_for_target_in
+
+ if (ar_pci->num_msi_intrs == 0)
+ /* Fix potential race by repeating CORE_BASE writes */
+- ath10k_pci_soc_write32(ar, PCIE_INTR_ENABLE_ADDRESS,
+- PCIE_INTR_FIRMWARE_MASK |
+- PCIE_INTR_CE_MASK_ALL);
++ ath10k_pci_enable_legacy_irq(ar);
+
+ mdelay(10);
+ } while (time_before(jiffies, timeout));
+
++ ath10k_pci_disable_and_clear_legacy_irq(ar);
++ ath10k_pci_irq_msi_fw_mask(ar);
++
+ if (val == 0xffffffff) {
+- ath10k_err("failed to read device register, device is gone\n");
+- ret = -EIO;
+- goto out;
++ ath10k_err(ar, "failed to read device register, device is gone\n");
++ return -EIO;
+ }
+
+ if (val & FW_IND_EVENT_PENDING) {
+- ath10k_warn("device has crashed during init\n");
+- ret = -ECOMM;
+- goto out;
++ ath10k_warn(ar, "device has crashed during init\n");
++ return -ECOMM;
+ }
+
+ if (!(val & FW_IND_INITIALIZED)) {
+- ath10k_err("failed to receive initialized event from target: %08x\n",
++ ath10k_err(ar, "failed to receive initialized event from target: %08x\n",
+ val);
+- ret = -ETIMEDOUT;
+- goto out;
++ return -ETIMEDOUT;
+ }
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot target initialised\n");
+-
+-out:
+- ath10k_pci_sleep(ar);
+- return ret;
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target initialised\n");
++ return 0;
+ }
+
+ static int ath10k_pci_cold_reset(struct ath10k *ar)
+ {
+- int i, ret;
++ int i;
+ u32 val;
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset\n");
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset\n");
+
+- ret = ath10k_do_pci_wake(ar);
+- if (ret) {
+- ath10k_err("failed to wake up target: %d\n",
+- ret);
+- return ret;
+- }
++ spin_lock_bh(&ar->data_lock);
++
++ ar->stats.fw_cold_reset_counter++;
++
++ spin_unlock_bh(&ar->data_lock);
+
+ /* Put Target, including PCIe, into RESET. */
+ val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS);
+@@ -2531,181 +2488,227 @@ static int ath10k_pci_cold_reset(struct
+ msleep(1);
+ }
+
+- ath10k_do_pci_sleep(ar);
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset complete\n");
++
++ return 0;
++}
++
++static int ath10k_pci_claim(struct ath10k *ar)
++{
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct pci_dev *pdev = ar_pci->pdev;
++ u32 lcr_val;
++ int ret;
++
++ pci_set_drvdata(pdev, ar);
++
++ ret = pci_enable_device(pdev);
++ if (ret) {
++ ath10k_err(ar, "failed to enable pci device: %d\n", ret);
++ return ret;
++ }
++
++ ret = pci_request_region(pdev, BAR_NUM, "ath");
++ if (ret) {
++ ath10k_err(ar, "failed to request region BAR%d: %d\n", BAR_NUM,
++ ret);
++ goto err_device;
++ }
++
++ /* Target expects 32 bit DMA. Enforce it. */
++ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
++ if (ret) {
++ ath10k_err(ar, "failed to set dma mask to 32-bit: %d\n", ret);
++ goto err_region;
++ }
++
++ ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
++ if (ret) {
++ ath10k_err(ar, "failed to set consistent dma mask to 32-bit: %d\n",
++ ret);
++ goto err_region;
++ }
++
++ pci_set_master(pdev);
++
++ /* Workaround: Disable ASPM */
++ pci_read_config_dword(pdev, 0x80, &lcr_val);
++ pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00));
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset complete\n");
++ /* Arrange for access to Target SoC registers. */
++ ar_pci->mem = pci_iomap(pdev, BAR_NUM, 0);
++ if (!ar_pci->mem) {
++ ath10k_err(ar, "failed to iomap BAR%d\n", BAR_NUM);
++ ret = -EIO;
++ goto err_master;
++ }
+
++ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
+ return 0;
++
++err_master:
++ pci_clear_master(pdev);
++
++err_region:
++ pci_release_region(pdev, BAR_NUM);
++
++err_device:
++ pci_disable_device(pdev);
++
++ return ret;
+ }
+
+-static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
++static void ath10k_pci_release(struct ath10k *ar)
+ {
++ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
++ struct pci_dev *pdev = ar_pci->pdev;
++
++ pci_iounmap(pdev, ar_pci->mem);
++ pci_release_region(pdev, BAR_NUM);
++ pci_clear_master(pdev);
++ pci_disable_device(pdev);
++}
++
++static bool ath10k_pci_chip_is_supported(u32 dev_id, u32 chip_id)
++{
++ const struct ath10k_pci_supp_chip *supp_chip;
+ int i;
++ u32 rev_id = MS(chip_id, SOC_CHIP_ID_REV);
+
+- for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) {
+- if (!test_bit(i, ar_pci->features))
+- continue;
++ for (i = 0; i < ARRAY_SIZE(ath10k_pci_supp_chips); i++) {
++ supp_chip = &ath10k_pci_supp_chips[i];
+
+- switch (i) {
+- case ATH10K_PCI_FEATURE_MSI_X:
+- ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n");
+- break;
+- case ATH10K_PCI_FEATURE_SOC_POWER_SAVE:
+- ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n");
+- break;
+- }
++ if (supp_chip->dev_id == dev_id &&
++ supp_chip->rev_id == rev_id)
++ return true;
+ }
++
++ return false;
+ }
+
+ static int ath10k_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_dev)
+ {
+- void __iomem *mem;
+ int ret = 0;
+ struct ath10k *ar;
+ struct ath10k_pci *ar_pci;
+- u32 lcr_val, chip_id;
+-
+- ath10k_dbg(ATH10K_DBG_PCI, "pci probe\n");
+-
+- ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
+- if (ar_pci == NULL)
+- return -ENOMEM;
+-
+- ar_pci->pdev = pdev;
+- ar_pci->dev = &pdev->dev;
++ enum ath10k_hw_rev hw_rev;
++ u32 chip_id;
+
+ switch (pci_dev->device) {
+ case QCA988X_2_0_DEVICE_ID:
+- set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
++ hw_rev = ATH10K_HW_QCA988X;
++ break;
++ case QCA6174_2_1_DEVICE_ID:
++ hw_rev = ATH10K_HW_QCA6174;
+ break;
+ default:
+- ret = -ENODEV;
+- ath10k_err("Unknown device ID: %d\n", pci_dev->device);
+- goto err_ar_pci;
++ WARN_ON(1);
++ return -ENOTSUPP;
+ }
+
+- if (ath10k_pci_target_ps)
+- set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features);
+-
+- ath10k_pci_dump_features(ar_pci);
+-
+- ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
++ ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, ATH10K_BUS_PCI,
++ hw_rev, &ath10k_pci_hif_ops);
+ if (!ar) {
+- ath10k_err("failed to create driver core\n");
+- ret = -EINVAL;
+- goto err_ar_pci;
++ dev_err(&pdev->dev, "failed to allocate core\n");
++ return -ENOMEM;
+ }
+
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci probe\n");
++
++ ar_pci = ath10k_pci_priv(ar);
++ ar_pci->pdev = pdev;
++ ar_pci->dev = &pdev->dev;
+ ar_pci->ar = ar;
+- atomic_set(&ar_pci->keep_awake_count, 0);
+
+- pci_set_drvdata(pdev, ar);
++ spin_lock_init(&ar_pci->ce_lock);
++ setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
++ (unsigned long)ar);
+
+- /*
+- * Without any knowledge of the Host, the Target may have been reset or
+- * power cycled and its Config Space may no longer reflect the PCI
+- * address space that was assigned earlier by the PCI infrastructure.
+- * Refresh it now.
+- */
+- ret = pci_assign_resource(pdev, BAR_NUM);
++ ret = ath10k_pci_claim(ar);
+ if (ret) {
+- ath10k_err("failed to assign PCI space: %d\n", ret);
+- goto err_ar;
++ ath10k_err(ar, "failed to claim device: %d\n", ret);
++ goto err_core_destroy;
+ }
+
+- ret = pci_enable_device(pdev);
++ ret = ath10k_pci_wake(ar);
+ if (ret) {
+- ath10k_err("failed to enable PCI device: %d\n", ret);
+- goto err_ar;
++ ath10k_err(ar, "failed to wake up: %d\n", ret);
++ goto err_release;
+ }
+
+- /* Request MMIO resources */
+- ret = pci_request_region(pdev, BAR_NUM, "ath");
++ ret = ath10k_pci_alloc_pipes(ar);
+ if (ret) {
+- ath10k_err("failed to request MMIO region: %d\n", ret);
+- goto err_device;
++ ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
++ ret);
++ goto err_sleep;
+ }
+
+- /*
+- * Target structures have a limit of 32 bit DMA pointers.
+- * DMA pointers can be wider than 32 bits by default on some systems.
+- */
+- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+- if (ret) {
+- ath10k_err("failed to set DMA mask to 32-bit: %d\n", ret);
+- goto err_region;
+- }
++ ath10k_pci_ce_deinit(ar);
++ ath10k_pci_irq_disable(ar);
+
+- ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
++ ret = ath10k_pci_init_irq(ar);
+ if (ret) {
+- ath10k_err("failed to set consistent DMA mask to 32-bit\n");
+- goto err_region;
++ ath10k_err(ar, "failed to init irqs: %d\n", ret);
++ goto err_free_pipes;
+ }
+
+- /* Set bus master bit in PCI_COMMAND to enable DMA */
+- pci_set_master(pdev);
+-
+- /*
+- * Temporary FIX: disable ASPM
+- * Will be removed after the OTP is programmed
+- */
+- pci_read_config_dword(pdev, 0x80, &lcr_val);
+- pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00));
++ ath10k_info(ar, "pci irq %s interrupts %d irq_mode %d reset_mode %d\n",
++ ath10k_pci_get_irq_method(ar), ar_pci->num_msi_intrs,
++ ath10k_pci_irq_mode, ath10k_pci_reset_mode);
+
+- /* Arrange for access to Target SoC registers. */
+- mem = pci_iomap(pdev, BAR_NUM, 0);
+- if (!mem) {
+- ath10k_err("failed to perform IOMAP for BAR%d\n", BAR_NUM);
+- ret = -EIO;
+- goto err_master;
++ ret = ath10k_pci_request_irq(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to request irqs: %d\n", ret);
++ goto err_deinit_irq;
+ }
+
+- ar_pci->mem = mem;
+-
+- spin_lock_init(&ar_pci->ce_lock);
+-
+- ret = ath10k_do_pci_wake(ar);
++ ret = ath10k_pci_chip_reset(ar);
+ if (ret) {
+- ath10k_err("Failed to get chip id: %d\n", ret);
+- goto err_iomap;
++ ath10k_err(ar, "failed to reset chip: %d\n", ret);
++ goto err_free_irq;
+ }
+
+ chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
++ if (chip_id == 0xffffffff) {
++ ath10k_err(ar, "failed to get chip id\n");
++ goto err_free_irq;
++ }
+
+- ath10k_do_pci_sleep(ar);
+-
+- ret = ath10k_pci_alloc_ce(ar);
+- if (ret) {
+- ath10k_err("failed to allocate copy engine pipes: %d\n", ret);
+- goto err_iomap;
++ if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) {
++ ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n",
++ pdev->device, chip_id);
++ goto err_sleep;
+ }
+
+- ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
++ ath10k_pci_sleep(ar);
+
+ ret = ath10k_core_register(ar, chip_id);
+ if (ret) {
+- ath10k_err("failed to register driver core: %d\n", ret);
+- goto err_free_ce;
++ ath10k_err(ar, "failed to register driver core: %d\n", ret);
++ goto err_free_irq;
+ }
+
+ return 0;
+
+-err_free_ce:
+- ath10k_pci_free_ce(ar);
+-err_iomap:
+- pci_iounmap(pdev, mem);
+-err_master:
+- pci_clear_master(pdev);
+-err_region:
+- pci_release_region(pdev, BAR_NUM);
+-err_device:
+- pci_disable_device(pdev);
+-err_ar:
++err_free_irq:
++ ath10k_pci_free_irq(ar);
++ ath10k_pci_kill_tasklet(ar);
++
++err_deinit_irq:
++ ath10k_pci_deinit_irq(ar);
++
++err_free_pipes:
++ ath10k_pci_free_pipes(ar);
++
++err_sleep:
++ ath10k_pci_sleep(ar);
++
++err_release:
++ ath10k_pci_release(ar);
++
++err_core_destroy:
+ ath10k_core_destroy(ar);
+-err_ar_pci:
+- /* call HIF PCI free here */
+- kfree(ar_pci);
+
+ return ret;
+ }
+@@ -2715,7 +2718,7 @@ static void ath10k_pci_remove(struct pci
+ struct ath10k *ar = pci_get_drvdata(pdev);
+ struct ath10k_pci *ar_pci;
+
+- ath10k_dbg(ATH10K_DBG_PCI, "pci remove\n");
++ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci remove\n");
+
+ if (!ar)
+ return;
+@@ -2725,18 +2728,14 @@ static void ath10k_pci_remove(struct pci
+ if (!ar_pci)
+ return;
+
+- tasklet_kill(&ar_pci->msi_fw_err);
+-
+ ath10k_core_unregister(ar);
+- ath10k_pci_free_ce(ar);
+-
+- pci_iounmap(pdev, ar_pci->mem);
+- pci_release_region(pdev, BAR_NUM);
+- pci_clear_master(pdev);
+- pci_disable_device(pdev);
+-
++ ath10k_pci_free_irq(ar);
++ ath10k_pci_kill_tasklet(ar);
++ ath10k_pci_deinit_irq(ar);
++ ath10k_pci_ce_deinit(ar);
++ ath10k_pci_free_pipes(ar);
++ ath10k_pci_release(ar);
+ ath10k_core_destroy(ar);
+- kfree(ar_pci);
+ }
+
+ MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
+@@ -2754,7 +2753,8 @@ static int __init ath10k_pci_init(void)
+
+ ret = pci_register_driver(&ath10k_pci_driver);
+ if (ret)
+- ath10k_err("failed to register PCI driver: %d\n", ret);
++ printk(KERN_ERR "failed to register ath10k pci driver: %d\n",
++ ret);
+
+ return ret;
+ }
+@@ -2770,5 +2770,7 @@ module_exit(ath10k_pci_exit);
+ MODULE_AUTHOR("Qualcomm Atheros");
+ MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
+ MODULE_LICENSE("Dual BSD/GPL");
+-MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_2_FILE);
++MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
++MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API2_FILE);
++MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API3_FILE);
+ MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
+--- a/drivers/net/wireless/ath/ath10k/pci.h
++++ b/drivers/net/wireless/ath/ath10k/pci.h
+@@ -23,9 +23,6 @@
+ #include "hw.h"
+ #include "ce.h"
+
+-/* FW dump area */
+-#define REG_DUMP_COUNT_QCA988X 60
+-
+ /*
+ * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite
+ */
+@@ -38,7 +35,8 @@
+ #define DIAG_TRANSFER_LIMIT 2048
+
+ struct bmi_xfer {
+- struct completion done;
++ bool tx_done;
++ bool rx_done;
+ bool wait_for_resp;
+ u32 resp_len;
+ };
+@@ -102,12 +100,12 @@ struct pcie_state {
+ * NOTE: Structure is shared between Host software and Target firmware!
+ */
+ struct ce_pipe_config {
+- u32 pipenum;
+- u32 pipedir;
+- u32 nentries;
+- u32 nbytes_max;
+- u32 flags;
+- u32 reserved;
++ __le32 pipenum;
++ __le32 pipedir;
++ __le32 nentries;
++ __le32 nbytes_max;
++ __le32 flags;
++ __le32 reserved;
+ };
+
+ /*
+@@ -129,17 +127,9 @@ struct ce_pipe_config {
+
+ /* Establish a mapping between a service/direction and a pipe. */
+ struct service_to_pipe {
+- u32 service_id;
+- u32 pipedir;
+- u32 pipenum;
+-};
+-
+-enum ath10k_pci_features {
+- ATH10K_PCI_FEATURE_MSI_X = 0,
+- ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 1,
+-
+- /* keep last */
+- ATH10K_PCI_FEATURE_COUNT
++ __le32 service_id;
++ __le32 pipedir;
++ __le32 pipenum;
+ };
+
+ /* Per-pipe state. */
+@@ -162,14 +152,17 @@ struct ath10k_pci_pipe {
+ struct tasklet_struct intr;
+ };
+
++struct ath10k_pci_supp_chip {
++ u32 dev_id;
++ u32 rev_id;
++};
++
+ struct ath10k_pci {
+ struct pci_dev *pdev;
+ struct device *dev;
+ struct ath10k *ar;
+ void __iomem *mem;
+
+- DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
+-
+ /*
+ * Number of MSI interrupts granted, 0 --> using legacy PCI line
+ * interrupts.
+@@ -178,12 +171,6 @@ struct ath10k_pci {
+
+ struct tasklet_struct intr_tq;
+ struct tasklet_struct msi_fw_err;
+- struct tasklet_struct early_irq_tasklet;
+-
+- int started;
+-
+- atomic_t keep_awake_count;
+- bool verified_awake;
+
+ struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
+
+@@ -197,29 +184,17 @@ struct ath10k_pci {
+
+ /* Map CE id to ce_state */
+ struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
++ struct timer_list rx_post_retry;
+ };
+
+ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
+ {
+- return ar->hif.priv;
+-}
+-
+-static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr)
+-{
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+-
+- return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+-}
+-
+-static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val)
+-{
+- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+-
+- iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
++ return (struct ath10k_pci *)ar->drv_priv;
+ }
+
++#define ATH10K_PCI_RX_POST_RETRY_MS 50
+ #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
+-#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */
++#define PCIE_WAKE_TIMEOUT 10000 /* 10ms */
+
+ #define BAR_NUM 0
+
+@@ -241,35 +216,17 @@ static inline void ath10k_pci_reg_write3
+ /* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
+ #define DIAG_ACCESS_CE_TIMEOUT_MS 10
+
+-/*
+- * This API allows the Host to access Target registers directly
+- * and relatively efficiently over PCIe.
+- * This allows the Host to avoid extra overhead associated with
+- * sending a message to firmware and waiting for a response message
+- * from firmware, as is done on other interconnects.
+- *
+- * Yet there is some complexity with direct accesses because the
+- * Target's power state is not known a priori. The Host must issue
+- * special PCIe reads/writes in order to explicitly wake the Target
+- * and to verify that it is awake and will remain awake.
+- *
+- * Usage:
++/* Target exposes its registers for direct access. However before host can
++ * access them it needs to make sure the target is awake (ath10k_pci_wake,
++ * ath10k_pci_wake_wait, ath10k_pci_is_awake). Once target is awake it won't go
++ * to sleep unless host tells it to (ath10k_pci_sleep).
+ *
+- * Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space.
+- * These calls must be bracketed by ath10k_pci_wake and
+- * ath10k_pci_sleep. A single BEGIN/END pair is adequate for
+- * multiple READ/WRITE operations.
++ * If host tries to access target registers without waking it up it can
++ * scribble over host memory.
+ *
+- * Use ath10k_pci_wake to put the Target in a state in
+- * which it is legal for the Host to directly access it. This
+- * may involve waking the Target from a low power state, which
+- * may take up to 2Ms!
+- *
+- * Use ath10k_pci_sleep to tell the Target that as far as
+- * this code path is concerned, it no longer needs to remain
+- * directly accessible. BEGIN/END is under a reference counter;
+- * multiple code paths may issue BEGIN/END on a single targid.
++ * If target is asleep waking it up may take up to even 2ms.
+ */
++
+ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
+ u32 value)
+ {
+@@ -295,25 +252,18 @@ static inline void ath10k_pci_soc_write3
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + addr, val);
+ }
+
+-int ath10k_do_pci_wake(struct ath10k *ar);
+-void ath10k_do_pci_sleep(struct ath10k *ar);
+-
+-static inline int ath10k_pci_wake(struct ath10k *ar)
++static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr)
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+- if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+- return ath10k_do_pci_wake(ar);
+-
+- return 0;
++ return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+ }
+
+-static inline void ath10k_pci_sleep(struct ath10k *ar)
++static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val)
+ {
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+- if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+- ath10k_do_pci_sleep(ar);
++ iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+ }
+
+ #endif /* _PCI_H_ */
+--- a/drivers/net/wireless/ath/ath10k/rx_desc.h
++++ b/drivers/net/wireless/ath/ath10k/rx_desc.h
+@@ -839,7 +839,6 @@ struct rx_ppdu_start {
+ * Reserved: HW should fill with 0, FW should ignore.
+ */
+
+-
+ #define RX_PPDU_END_FLAGS_PHY_ERR (1 << 0)
+ #define RX_PPDU_END_FLAGS_RX_LOCATION (1 << 1)
+ #define RX_PPDU_END_FLAGS_TXBF_H_INFO (1 << 2)
+@@ -851,7 +850,7 @@ struct rx_ppdu_start {
+
+ #define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
+
+-struct rx_ppdu_end {
++struct rx_ppdu_end_common {
+ __le32 evm_p0;
+ __le32 evm_p1;
+ __le32 evm_p2;
+@@ -874,10 +873,33 @@ struct rx_ppdu_end {
+ u8 phy_err_code;
+ __le16 flags; /* %RX_PPDU_END_FLAGS_ */
+ __le32 info0; /* %RX_PPDU_END_INFO0_ */
++} __packed;
++
++struct rx_ppdu_end_qca988x {
++ __le16 bb_length;
++ __le16 info1; /* %RX_PPDU_END_INFO1_ */
++} __packed;
++
++#define RX_PPDU_END_RTT_CORRELATION_VALUE_MASK 0x00ffffff
++#define RX_PPDU_END_RTT_CORRELATION_VALUE_LSB 0
++#define RX_PPDU_END_RTT_UNUSED_MASK 0x7f000000
++#define RX_PPDU_END_RTT_UNUSED_LSB 24
++#define RX_PPDU_END_RTT_NORMAL_MODE BIT(31)
++
++struct rx_ppdu_end_qca6174 {
++ __le32 rtt; /* %RX_PPDU_END_RTT_ */
+ __le16 bb_length;
+ __le16 info1; /* %RX_PPDU_END_INFO1_ */
+ } __packed;
+
++struct rx_ppdu_end {
++ struct rx_ppdu_end_common common;
++ union {
++ struct rx_ppdu_end_qca988x qca988x;
++ struct rx_ppdu_end_qca6174 qca6174;
++ } __packed;
++} __packed;
++
+ /*
+ * evm_p0
+ * EVM for pilot 0. Contain EVM for streams: 0, 1, 2 and 3.
+--- a/drivers/net/wireless/ath/ath10k/targaddrs.h
++++ b/drivers/net/wireless/ath/ath10k/targaddrs.h
+@@ -18,6 +18,8 @@
+ #ifndef __TARGADDRS_H__
+ #define __TARGADDRS_H__
+
++#include "hw.h"
++
+ /*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure. It must match the address of the _host_interest
+@@ -284,7 +286,6 @@ Fw Mode/SubMode Mask
+ #define HI_OPTION_ALL_FW_SUBMODE_MASK 0xFF00
+ #define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8
+
+-
+ /* hi_option_flag2 options */
+ #define HI_OPTION_OFFLOAD_AMSDU 0x01
+ #define HI_OPTION_DFS_SUPPORT 0x02 /* Enable DFS support */
+@@ -446,4 +447,7 @@ Fw Mode/SubMode Mask
+ #define QCA988X_BOARD_DATA_SZ 7168
+ #define QCA988X_BOARD_EXT_DATA_SZ 0
+
++#define QCA6174_BOARD_DATA_SZ 8192
++#define QCA6174_BOARD_EXT_DATA_SZ 0
++
+ #endif /* __TARGADDRS_H__ */
+--- a/drivers/net/wireless/ath/ath10k/trace.h
++++ b/drivers/net/wireless/ath/ath10k/trace.h
+@@ -18,6 +18,16 @@
+ #if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+
+ #include <linux/tracepoint.h>
++#include "core.h"
++
++#if !defined(_TRACE_H_)
++static inline u32 ath10k_frm_hdr_len(const void *buf)
++{
++ const struct ieee80211_hdr *hdr = buf;
++
++ return ieee80211_hdrlen(hdr->frame_control);
++}
++#endif
+
+ #define _TRACE_H_
+
+@@ -39,59 +49,79 @@ static inline void trace_ ## name(proto)
+ #define ATH10K_MSG_MAX 200
+
+ DECLARE_EVENT_CLASS(ath10k_log_event,
+- TP_PROTO(struct va_format *vaf),
+- TP_ARGS(vaf),
++ TP_PROTO(struct ath10k *ar, struct va_format *vaf),
++ TP_ARGS(ar, vaf),
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __dynamic_array(char, msg, ATH10K_MSG_MAX)
+ ),
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ ATH10K_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= ATH10K_MSG_MAX);
+ ),
+- TP_printk("%s", __get_str(msg))
++ TP_printk(
++ "%s %s %s",
++ __get_str(driver),
++ __get_str(device),
++ __get_str(msg)
++ )
+ );
+
+ DEFINE_EVENT(ath10k_log_event, ath10k_log_err,
+- TP_PROTO(struct va_format *vaf),
+- TP_ARGS(vaf)
++ TP_PROTO(struct ath10k *ar, struct va_format *vaf),
++ TP_ARGS(ar, vaf)
+ );
+
+ DEFINE_EVENT(ath10k_log_event, ath10k_log_warn,
+- TP_PROTO(struct va_format *vaf),
+- TP_ARGS(vaf)
++ TP_PROTO(struct ath10k *ar, struct va_format *vaf),
++ TP_ARGS(ar, vaf)
+ );
+
+ DEFINE_EVENT(ath10k_log_event, ath10k_log_info,
+- TP_PROTO(struct va_format *vaf),
+- TP_ARGS(vaf)
++ TP_PROTO(struct ath10k *ar, struct va_format *vaf),
++ TP_ARGS(ar, vaf)
+ );
+
+ TRACE_EVENT(ath10k_log_dbg,
+- TP_PROTO(unsigned int level, struct va_format *vaf),
+- TP_ARGS(level, vaf),
++ TP_PROTO(struct ath10k *ar, unsigned int level, struct va_format *vaf),
++ TP_ARGS(ar, level, vaf),
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __field(unsigned int, level)
+ __dynamic_array(char, msg, ATH10K_MSG_MAX)
+ ),
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->level = level;
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ ATH10K_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= ATH10K_MSG_MAX);
+ ),
+- TP_printk("%s", __get_str(msg))
++ TP_printk(
++ "%s %s %s",
++ __get_str(driver),
++ __get_str(device),
++ __get_str(msg)
++ )
+ );
+
+ TRACE_EVENT(ath10k_log_dbg_dump,
+- TP_PROTO(const char *msg, const char *prefix,
++ TP_PROTO(struct ath10k *ar, const char *msg, const char *prefix,
+ const void *buf, size_t buf_len),
+
+- TP_ARGS(msg, prefix, buf, buf_len),
++ TP_ARGS(ar, msg, prefix, buf, buf_len),
+
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __string(msg, msg)
+ __string(prefix, prefix)
+ __field(size_t, buf_len)
+@@ -99,6 +129,8 @@ TRACE_EVENT(ath10k_log_dbg_dump,
+ ),
+
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ __assign_str(msg, msg);
+ __assign_str(prefix, prefix);
+ __entry->buf_len = buf_len;
+@@ -106,16 +138,23 @@ TRACE_EVENT(ath10k_log_dbg_dump,
+ ),
+
+ TP_printk(
+- "%s/%s\n", __get_str(prefix), __get_str(msg)
++ "%s %s %s/%s\n",
++ __get_str(driver),
++ __get_str(device),
++ __get_str(prefix),
++ __get_str(msg)
+ )
+ );
+
+ TRACE_EVENT(ath10k_wmi_cmd,
+- TP_PROTO(int id, void *buf, size_t buf_len, int ret),
++ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len,
++ int ret),
+
+- TP_ARGS(id, buf, buf_len, ret),
++ TP_ARGS(ar, id, buf, buf_len, ret),
+
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __field(unsigned int, id)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+@@ -123,6 +162,8 @@ TRACE_EVENT(ath10k_wmi_cmd,
+ ),
+
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->id = id;
+ __entry->buf_len = buf_len;
+ __entry->ret = ret;
+@@ -130,7 +171,9 @@ TRACE_EVENT(ath10k_wmi_cmd,
+ ),
+
+ TP_printk(
+- "id %d len %zu ret %d",
++ "%s %s id %d len %zu ret %d",
++ __get_str(driver),
++ __get_str(device),
+ __entry->id,
+ __entry->buf_len,
+ __entry->ret
+@@ -138,71 +181,346 @@ TRACE_EVENT(ath10k_wmi_cmd,
+ );
+
+ TRACE_EVENT(ath10k_wmi_event,
+- TP_PROTO(int id, void *buf, size_t buf_len),
++ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
+
+- TP_ARGS(id, buf, buf_len),
++ TP_ARGS(ar, id, buf, buf_len),
+
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __field(unsigned int, id)
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->id = id;
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+- "id %d len %zu",
++ "%s %s id %d len %zu",
++ __get_str(driver),
++ __get_str(device),
+ __entry->id,
+ __entry->buf_len
+ )
+ );
+
+ TRACE_EVENT(ath10k_htt_stats,
+- TP_PROTO(void *buf, size_t buf_len),
++ TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
+
+- TP_ARGS(buf, buf_len),
++ TP_ARGS(ar, buf, buf_len),
+
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+- "len %zu",
++ "%s %s len %zu",
++ __get_str(driver),
++ __get_str(device),
+ __entry->buf_len
+ )
+ );
+
+ TRACE_EVENT(ath10k_wmi_dbglog,
+- TP_PROTO(void *buf, size_t buf_len),
++ TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
+
+- TP_ARGS(buf, buf_len),
++ TP_ARGS(ar, buf, buf_len),
+
+ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
+ __field(size_t, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+- "len %zu",
++ "%s %s len %zu",
++ __get_str(driver),
++ __get_str(device),
+ __entry->buf_len
+ )
+ );
+
++TRACE_EVENT(ath10k_htt_pktlog,
++ TP_PROTO(struct ath10k *ar, const void *buf, u16 buf_len),
++
++ TP_ARGS(ar, buf, buf_len),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(u16, buf_len)
++ __dynamic_array(u8, pktlog, buf_len)
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->buf_len = buf_len;
++ memcpy(__get_dynamic_array(pktlog), buf, buf_len);
++ ),
++
++ TP_printk(
++ "%s %s size %hu",
++ __get_str(driver),
++ __get_str(device),
++ __entry->buf_len
++ )
++);
++
++TRACE_EVENT(ath10k_htt_tx,
++ TP_PROTO(struct ath10k *ar, u16 msdu_id, u16 msdu_len,
++ u8 vdev_id, u8 tid),
++
++ TP_ARGS(ar, msdu_id, msdu_len, vdev_id, tid),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(u16, msdu_id)
++ __field(u16, msdu_len)
++ __field(u8, vdev_id)
++ __field(u8, tid)
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->msdu_id = msdu_id;
++ __entry->msdu_len = msdu_len;
++ __entry->vdev_id = vdev_id;
++ __entry->tid = tid;
++ ),
++
++ TP_printk(
++ "%s %s msdu_id %d msdu_len %d vdev_id %d tid %d",
++ __get_str(driver),
++ __get_str(device),
++ __entry->msdu_id,
++ __entry->msdu_len,
++ __entry->vdev_id,
++ __entry->tid
++ )
++);
++
++TRACE_EVENT(ath10k_txrx_tx_unref,
++ TP_PROTO(struct ath10k *ar, u16 msdu_id),
++
++ TP_ARGS(ar, msdu_id),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(u16, msdu_id)
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->msdu_id = msdu_id;
++ ),
++
++ TP_printk(
++ "%s %s msdu_id %d",
++ __get_str(driver),
++ __get_str(device),
++ __entry->msdu_id
++ )
++);
++
++DECLARE_EVENT_CLASS(ath10k_hdr_event,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++
++ TP_ARGS(ar, data, len),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(size_t, len)
++ __dynamic_array(u8, data, ath10k_frm_hdr_len(data))
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->len = ath10k_frm_hdr_len(data);
++ memcpy(__get_dynamic_array(data), data, __entry->len);
++ ),
++
++ TP_printk(
++ "%s %s len %zu\n",
++ __get_str(driver),
++ __get_str(device),
++ __entry->len
++ )
++);
++
++DECLARE_EVENT_CLASS(ath10k_payload_event,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++
++ TP_ARGS(ar, data, len),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(size_t, len)
++ __dynamic_array(u8, payload, (len - ath10k_frm_hdr_len(data)))
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->len = len - ath10k_frm_hdr_len(data);
++ memcpy(__get_dynamic_array(payload),
++ data + ath10k_frm_hdr_len(data), __entry->len);
++ ),
++
++ TP_printk(
++ "%s %s len %zu\n",
++ __get_str(driver),
++ __get_str(device),
++ __entry->len
++ )
++);
++
++DEFINE_EVENT(ath10k_hdr_event, ath10k_tx_hdr,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++ TP_ARGS(ar, data, len)
++);
++
++DEFINE_EVENT(ath10k_payload_event, ath10k_tx_payload,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++ TP_ARGS(ar, data, len)
++);
++
++DEFINE_EVENT(ath10k_hdr_event, ath10k_rx_hdr,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++ TP_ARGS(ar, data, len)
++);
++
++DEFINE_EVENT(ath10k_payload_event, ath10k_rx_payload,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++ TP_ARGS(ar, data, len)
++);
++
++TRACE_EVENT(ath10k_htt_rx_desc,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++
++ TP_ARGS(ar, data, len),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(u16, len)
++ __dynamic_array(u8, rxdesc, len)
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->len = len;
++ memcpy(__get_dynamic_array(rxdesc), data, len);
++ ),
++
++ TP_printk(
++ "%s %s rxdesc len %d",
++ __get_str(driver),
++ __get_str(device),
++ __entry->len
++ )
++);
++
++TRACE_EVENT(ath10k_wmi_diag_container,
++ TP_PROTO(struct ath10k *ar,
++ u8 type,
++ u32 timestamp,
++ u32 code,
++ u16 len,
++ const void *data),
++
++ TP_ARGS(ar, type, timestamp, code, len, data),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(u8, type)
++ __field(u32, timestamp)
++ __field(u32, code)
++ __field(u16, len)
++ __dynamic_array(u8, data, len)
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->type = type;
++ __entry->timestamp = timestamp;
++ __entry->code = code;
++ __entry->len = len;
++ memcpy(__get_dynamic_array(data), data, len);
++ ),
++
++ TP_printk(
++ "%s %s diag container type %hhu timestamp %u code %u len %d",
++ __get_str(driver),
++ __get_str(device),
++ __entry->type,
++ __entry->timestamp,
++ __entry->code,
++ __entry->len
++ )
++);
++
++TRACE_EVENT(ath10k_wmi_diag,
++ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
++
++ TP_ARGS(ar, data, len),
++
++ TP_STRUCT__entry(
++ __string(device, dev_name(ar->dev))
++ __string(driver, dev_driver_string(ar->dev))
++ __field(u16, len)
++ __dynamic_array(u8, data, len)
++ ),
++
++ TP_fast_assign(
++ __assign_str(device, dev_name(ar->dev));
++ __assign_str(driver, dev_driver_string(ar->dev));
++ __entry->len = len;
++ memcpy(__get_dynamic_array(data), data, len);
++ ),
++
++ TP_printk(
++ "%s %s tlv diag len %d",
++ __get_str(driver),
++ __get_str(device),
++ __entry->len
++ )
++);
++
+ #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
+
+ /* we don't want to use include/trace/events */
+--- a/drivers/net/wireless/ath/ath10k/txrx.c
++++ b/drivers/net/wireless/ath/ath10k/txrx.c
+@@ -32,14 +32,14 @@ static void ath10k_report_offchan_tx(str
+ * offchan_tx_skb. */
+ spin_lock_bh(&ar->data_lock);
+ if (ar->offchan_tx_skb != skb) {
+- ath10k_warn("completed old offchannel frame\n");
++ ath10k_warn(ar, "completed old offchannel frame\n");
+ goto out;
+ }
+
+ complete(&ar->offchan_tx_completed);
+ ar->offchan_tx_skb = NULL; /* just for sanity */
+
+- ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb);
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb);
+ out:
+ spin_unlock_bh(&ar->data_lock);
+ }
+@@ -47,23 +47,30 @@ out:
+ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
+ const struct htt_tx_done *tx_done)
+ {
+- struct device *dev = htt->ar->dev;
++ struct ath10k *ar = htt->ar;
++ struct device *dev = ar->dev;
+ struct ieee80211_tx_info *info;
+ struct ath10k_skb_cb *skb_cb;
+ struct sk_buff *msdu;
+
+ lockdep_assert_held(&htt->tx_lock);
+
+- ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
+ tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
+
+ if (tx_done->msdu_id >= htt->max_num_pending_tx) {
+- ath10k_warn("warning: msdu_id %d too big, ignoring\n",
++ ath10k_warn(ar, "warning: msdu_id %d too big, ignoring\n",
++ tx_done->msdu_id);
++ return;
++ }
++
++ msdu = idr_find(&htt->pending_tx, tx_done->msdu_id);
++ if (!msdu) {
++ ath10k_warn(ar, "received tx completion for invalid msdu_id: %d\n",
+ tx_done->msdu_id);
+ return;
+ }
+
+- msdu = htt->pending_tx[tx_done->msdu_id];
+ skb_cb = ATH10K_SKB_CB(msdu);
+
+ dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
+@@ -77,6 +84,7 @@ void ath10k_txrx_tx_unref(struct ath10k_
+
+ info = IEEE80211_SKB_CB(msdu);
+ memset(&info->status, 0, sizeof(info->status));
++ trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
+
+ if (tx_done->discard) {
+ ieee80211_free_txskb(htt->ar->hw, msdu);
+@@ -93,7 +101,6 @@ void ath10k_txrx_tx_unref(struct ath10k_
+ /* we do not own the msdu anymore */
+
+ exit:
+- htt->pending_tx[tx_done->msdu_id] = NULL;
+ ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
+ __ath10k_htt_tx_dec_pending(htt);
+ if (htt->num_pending_tx == 0)
+@@ -119,8 +126,7 @@ struct ath10k_peer *ath10k_peer_find(str
+ return NULL;
+ }
+
+-static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar,
+- int peer_id)
++struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id)
+ {
+ struct ath10k_peer *peer;
+
+@@ -145,7 +151,8 @@ static int ath10k_wait_for_peer_common(s
+ mapped = !!ath10k_peer_find(ar, vdev_id, addr);
+ spin_unlock_bh(&ar->data_lock);
+
+- mapped == expect_mapped;
++ (mapped == expect_mapped ||
++ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags));
+ }), 3*HZ);
+
+ if (ret <= 0)
+@@ -178,12 +185,12 @@ void ath10k_peer_map_event(struct ath10k
+ goto exit;
+
+ peer->vdev_id = ev->vdev_id;
+- memcpy(peer->addr, ev->addr, ETH_ALEN);
++ ether_addr_copy(peer->addr, ev->addr);
+ list_add(&peer->list, &ar->peers);
+ wake_up(&ar->peer_mapping_wq);
+ }
+
+- ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
+ ev->vdev_id, ev->addr, ev->peer_id);
+
+ set_bit(ev->peer_id, peer->peer_ids);
+@@ -200,12 +207,12 @@ void ath10k_peer_unmap_event(struct ath1
+ spin_lock_bh(&ar->data_lock);
+ peer = ath10k_peer_find_by_id(ar, ev->peer_id);
+ if (!peer) {
+- ath10k_warn("peer-unmap-event: unknown peer id %d\n",
++ ath10k_warn(ar, "peer-unmap-event: unknown peer id %d\n",
+ ev->peer_id);
+ goto exit;
+ }
+
+- ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
+ peer->vdev_id, peer->addr, ev->peer_id);
+
+ clear_bit(ev->peer_id, peer->peer_ids);
+--- a/drivers/net/wireless/ath/ath10k/txrx.h
++++ b/drivers/net/wireless/ath/ath10k/txrx.h
+@@ -24,6 +24,7 @@ void ath10k_txrx_tx_unref(struct ath10k_
+
+ struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+ const u8 *addr);
++struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id);
+ int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id,
+ const u8 *addr);
+ int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id,
+--- a/drivers/net/wireless/ath/ath10k/wmi.c
++++ b/drivers/net/wireless/ath/ath10k/wmi.c
+@@ -22,7 +22,10 @@
+ #include "htc.h"
+ #include "debug.h"
+ #include "wmi.h"
++#include "wmi-tlv.h"
+ #include "mac.h"
++#include "testmode.h"
++#include "wmi-ops.h"
+
+ /* MAIN WMI cmd track */
+ static struct wmi_cmd_map wmi_cmd_map = {
+@@ -142,6 +145,7 @@ static struct wmi_cmd_map wmi_cmd_map =
+ .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID,
+ .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
+ .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
++ .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
+ };
+
+ /* 10.X WMI cmd track */
+@@ -264,6 +268,129 @@ static struct wmi_cmd_map wmi_10x_cmd_ma
+ .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
+ .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
+ .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
++ .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
++};
++
++/* 10.2.4 WMI cmd track */
++static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
++ .init_cmdid = WMI_10_2_INIT_CMDID,
++ .start_scan_cmdid = WMI_10_2_START_SCAN_CMDID,
++ .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID,
++ .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID,
++ .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
++ .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
++ .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID,
++ .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID,
++ .pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID,
++ .pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID,
++ .pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID,
++ .pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID,
++ .pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID,
++ .pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID,
++ .pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID,
++ .pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID,
++ .pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID,
++ .vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID,
++ .vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID,
++ .vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID,
++ .vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID,
++ .vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID,
++ .vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID,
++ .vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID,
++ .vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID,
++ .vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID,
++ .peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID,
++ .peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID,
++ .peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID,
++ .peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID,
++ .peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID,
++ .peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID,
++ .peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID,
++ .peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID,
++ .bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID,
++ .pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID,
++ .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
++ .bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID,
++ .prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID,
++ .mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID,
++ .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
++ .addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID,
++ .addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID,
++ .addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID,
++ .delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID,
++ .addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID,
++ .send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID,
++ .sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID,
++ .sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID,
++ .sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID,
++ .pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID,
++ .pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID,
++ .roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE,
++ .roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD,
++ .roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD,
++ .roam_scan_rssi_change_threshold =
++ WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
++ .roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE,
++ .ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE,
++ .ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE,
++ .ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD,
++ .p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO,
++ .p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY,
++ .p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE,
++ .p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE,
++ .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
++ .ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID,
++ .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
++ .peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID,
++ .wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID,
++ .wlan_profile_set_hist_intvl_cmdid =
++ WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
++ .wlan_profile_get_profile_data_cmdid =
++ WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
++ .wlan_profile_enable_profile_id_cmdid =
++ WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
++ .wlan_profile_list_profile_id_cmdid =
++ WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
++ .pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID,
++ .pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID,
++ .add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID,
++ .rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID,
++ .wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID,
++ .wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID,
++ .wow_enable_disable_wake_event_cmdid =
++ WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
++ .wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID,
++ .wow_hostwakeup_from_sleep_cmdid =
++ WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
++ .rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID,
++ .rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID,
++ .vdev_spectral_scan_configure_cmdid =
++ WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
++ .vdev_spectral_scan_enable_cmdid =
++ WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
++ .request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID,
++ .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
++ .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
++ .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
++ .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
++ .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
++ .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
++ .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
++ .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
++ .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
++ .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
++ .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
++ .echo_cmdid = WMI_10_2_ECHO_CMDID,
++ .pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID,
++ .dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID,
++ .pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID,
++ .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
++ .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
++ .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
++ .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
++ .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
++ .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
++ .pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
+ };
+
+ /* MAIN WMI VDEV param map */
+@@ -384,6 +511,64 @@ static struct wmi_vdev_param_map wmi_10x
+ WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
+ };
+
++static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = {
++ .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD,
++ .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
++ .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
++ .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
++ .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE,
++ .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
++ .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME,
++ .preamble = WMI_10X_VDEV_PARAM_PREAMBLE,
++ .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME,
++ .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD,
++ .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
++ .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL,
++ .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD,
++ .wmi_vdev_oc_scheduler_air_time_limit =
++ WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
++ .wds = WMI_10X_VDEV_PARAM_WDS,
++ .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW,
++ .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
++ .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
++ .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
++ .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM,
++ .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH,
++ .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET,
++ .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
++ .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
++ .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE,
++ .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE,
++ .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE,
++ .sgi = WMI_10X_VDEV_PARAM_SGI,
++ .ldpc = WMI_10X_VDEV_PARAM_LDPC,
++ .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC,
++ .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC,
++ .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
++ .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID,
++ .nss = WMI_10X_VDEV_PARAM_NSS,
++ .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
++ .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
++ .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE,
++ .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE,
++ .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
++ .ap_keepalive_min_idle_inactive_time_secs =
++ WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
++ .ap_keepalive_max_idle_inactive_time_secs =
++ WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
++ .ap_keepalive_max_unresponsive_time_secs =
++ WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
++ .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
++ .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
++ .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
++ .txbf = WMI_VDEV_PARAM_UNSUPPORTED,
++ .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
++ .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
++ .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
++ .ap_detect_out_of_sync_sleeping_sta_time_secs =
++ WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
++};
++
+ static struct wmi_pdev_param_map wmi_pdev_param_map = {
+ .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK,
+ .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK,
+@@ -433,6 +618,7 @@ static struct wmi_pdev_param_map wmi_pde
+ .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED,
+ .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED,
+ .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED,
++ .cal_period = WMI_PDEV_PARAM_UNSUPPORTED,
+ };
+
+ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
+@@ -485,11 +671,221 @@ static struct wmi_pdev_param_map wmi_10x
+ .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
+ .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
+ .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
++ .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD,
++};
++
++static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
++ .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK,
++ .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
++ .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
++ .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
++ .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
++ .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
++ .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
++ .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
++ .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE,
++ .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW,
++ .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
++ .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
++ .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
++ .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
++ .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE,
++ .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
++ .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
++ .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
++ .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
++ .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
++ .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
++ .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
++ .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
++ .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE,
++ .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
++ .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED,
++ .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED,
++ .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED,
++ .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED,
++ .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
++ .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
++ .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
++ .bcnflt_stats_update_period =
++ WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
++ .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
++ .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
++ .dcs = WMI_10X_PDEV_PARAM_DCS,
++ .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
++ .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
++ .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
++ .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
++ .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
++ .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN,
++ .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED,
++ .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED,
++ .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED,
++ .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
++ .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
++ .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
++ .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD,
++};
++
++/* firmware 10.2 specific mappings */
++static struct wmi_cmd_map wmi_10_2_cmd_map = {
++ .init_cmdid = WMI_10_2_INIT_CMDID,
++ .start_scan_cmdid = WMI_10_2_START_SCAN_CMDID,
++ .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID,
++ .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID,
++ .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
++ .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
++ .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID,
++ .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID,
++ .pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID,
++ .pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID,
++ .pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID,
++ .pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID,
++ .pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID,
++ .pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID,
++ .pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID,
++ .pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID,
++ .pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID,
++ .vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID,
++ .vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID,
++ .vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID,
++ .vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID,
++ .vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID,
++ .vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID,
++ .vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID,
++ .vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID,
++ .vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID,
++ .peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID,
++ .peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID,
++ .peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID,
++ .peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID,
++ .peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID,
++ .peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID,
++ .peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID,
++ .peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID,
++ .bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID,
++ .pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID,
++ .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
++ .bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID,
++ .prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID,
++ .mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID,
++ .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
++ .addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID,
++ .addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID,
++ .addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID,
++ .delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID,
++ .addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID,
++ .send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID,
++ .sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID,
++ .sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID,
++ .sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID,
++ .pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID,
++ .pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID,
++ .roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE,
++ .roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD,
++ .roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD,
++ .roam_scan_rssi_change_threshold =
++ WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
++ .roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE,
++ .ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE,
++ .ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE,
++ .ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD,
++ .p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO,
++ .p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY,
++ .p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE,
++ .p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE,
++ .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
++ .ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID,
++ .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
++ .peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID,
++ .wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID,
++ .wlan_profile_set_hist_intvl_cmdid =
++ WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
++ .wlan_profile_get_profile_data_cmdid =
++ WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
++ .wlan_profile_enable_profile_id_cmdid =
++ WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
++ .wlan_profile_list_profile_id_cmdid =
++ WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
++ .pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID,
++ .pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID,
++ .add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID,
++ .rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID,
++ .wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID,
++ .wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID,
++ .wow_enable_disable_wake_event_cmdid =
++ WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
++ .wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID,
++ .wow_hostwakeup_from_sleep_cmdid =
++ WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
++ .rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID,
++ .rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID,
++ .vdev_spectral_scan_configure_cmdid =
++ WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
++ .vdev_spectral_scan_enable_cmdid =
++ WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
++ .request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID,
++ .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
++ .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
++ .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
++ .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
++ .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
++ .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
++ .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
++ .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
++ .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
++ .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
++ .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
++ .echo_cmdid = WMI_10_2_ECHO_CMDID,
++ .pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID,
++ .dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID,
++ .pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID,
++ .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
++ .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
++ .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
++ .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
++ .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
++ .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
++ .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
+ };
+
++void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
++ const struct wmi_channel_arg *arg)
++{
++ u32 flags = 0;
++
++ memset(ch, 0, sizeof(*ch));
++
++ if (arg->passive)
++ flags |= WMI_CHAN_FLAG_PASSIVE;
++ if (arg->allow_ibss)
++ flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
++ if (arg->allow_ht)
++ flags |= WMI_CHAN_FLAG_ALLOW_HT;
++ if (arg->allow_vht)
++ flags |= WMI_CHAN_FLAG_ALLOW_VHT;
++ if (arg->ht40plus)
++ flags |= WMI_CHAN_FLAG_HT40_PLUS;
++ if (arg->chan_radar)
++ flags |= WMI_CHAN_FLAG_DFS;
++
++ ch->mhz = __cpu_to_le32(arg->freq);
++ ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1);
++ ch->band_center_freq2 = 0;
++ ch->min_power = arg->min_power;
++ ch->max_power = arg->max_power;
++ ch->reg_power = arg->max_reg_power;
++ ch->antenna_max = arg->max_antenna_gain;
++
++ /* mode & flags share storage */
++ ch->mode = arg->mode;
++ ch->flags |= __cpu_to_le32(flags);
++}
++
+ int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
+ {
+ int ret;
++
+ ret = wait_for_completion_timeout(&ar->wmi.service_ready,
+ WMI_SERVICE_READY_TIMEOUT_HZ);
+ return ret;
+@@ -498,23 +894,24 @@ int ath10k_wmi_wait_for_service_ready(st
+ int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar)
+ {
+ int ret;
++
+ ret = wait_for_completion_timeout(&ar->wmi.unified_ready,
+ WMI_UNIFIED_READY_TIMEOUT_HZ);
+ return ret;
+ }
+
+-static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
++struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len)
+ {
+ struct sk_buff *skb;
+ u32 round_len = roundup(len, 4);
+
+- skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len);
++ skb = ath10k_htc_alloc_skb(ar, WMI_SKB_HEADROOM + round_len);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, WMI_SKB_HEADROOM);
+ if (!IS_ALIGNED((unsigned long)skb->data, 4))
+- ath10k_warn("Unaligned WMI skb\n");
++ ath10k_warn(ar, "Unaligned WMI skb\n");
+
+ skb_put(skb, round_len);
+ memset(skb->data, 0, round_len);
+@@ -527,8 +924,8 @@ static void ath10k_wmi_htc_tx_complete(s
+ dev_kfree_skb(skb);
+ }
+
+-static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
+- u32 cmd_id)
++int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
++ u32 cmd_id)
+ {
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+ struct wmi_cmd_hdr *cmd_hdr;
+@@ -545,7 +942,7 @@ static int ath10k_wmi_cmd_send_nowait(st
+
+ memset(skb_cb, 0, sizeof(*skb_cb));
+ ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
+- trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret);
++ trace_ath10k_wmi_cmd(ar, cmd_id, skb->data, skb->len, ret);
+
+ if (ret)
+ goto err_pull;
+@@ -559,23 +956,45 @@ err_pull:
+
+ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
+ {
++ struct ath10k *ar = arvif->ar;
++ struct ath10k_skb_cb *cb;
++ struct sk_buff *bcn;
+ int ret;
+
+- lockdep_assert_held(&arvif->ar->data_lock);
++ spin_lock_bh(&ar->data_lock);
+
+- if (arvif->beacon == NULL)
+- return;
++ bcn = arvif->beacon;
+
+- if (arvif->beacon_sent)
+- return;
++ if (!bcn)
++ goto unlock;
+
+- ret = ath10k_wmi_beacon_send_ref_nowait(arvif);
+- if (ret)
+- return;
++ cb = ATH10K_SKB_CB(bcn);
++
++ switch (arvif->beacon_state) {
++ case ATH10K_BEACON_SENDING:
++ case ATH10K_BEACON_SENT:
++ break;
++ case ATH10K_BEACON_SCHEDULED:
++ arvif->beacon_state = ATH10K_BEACON_SENDING;
++ spin_unlock_bh(&ar->data_lock);
++
++ ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar,
++ arvif->vdev_id,
++ bcn->data, bcn->len,
++ cb->paddr,
++ cb->bcn.dtim_zero,
++ cb->bcn.deliver_cab);
++
++ spin_lock_bh(&ar->data_lock);
+
+- /* We need to retain the arvif->beacon reference for DMA unmapping and
+- * freeing the skbuff later. */
+- arvif->beacon_sent = true;
++ if (ret == 0)
++ arvif->beacon_state = ATH10K_BEACON_SENT;
++ else
++ arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
++ }
++
++unlock:
++ spin_unlock_bh(&ar->data_lock);
+ }
+
+ static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
+@@ -588,12 +1007,10 @@ static void ath10k_wmi_tx_beacons_iter(v
+
+ static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar)
+ {
+- spin_lock_bh(&ar->data_lock);
+ ieee80211_iterate_active_interfaces_atomic(ar->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ ath10k_wmi_tx_beacons_iter,
+ NULL);
+- spin_unlock_bh(&ar->data_lock);
+ }
+
+ static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar)
+@@ -604,15 +1021,14 @@ static void ath10k_wmi_op_ep_tx_credits(
+ wake_up(&ar->wmi.tx_credits_wq);
+ }
+
+-static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
+- u32 cmd_id)
++int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
+ {
+ int ret = -EOPNOTSUPP;
+
+ might_sleep();
+
+ if (cmd_id == WMI_CMD_UNSUPPORTED) {
+- ath10k_warn("wmi command %d is not supported by firmware\n",
++ ath10k_warn(ar, "wmi command %d is not supported by firmware\n",
+ cmd_id);
+ return ret;
+ }
+@@ -622,6 +1038,10 @@ static int ath10k_wmi_cmd_send(struct at
+ ath10k_wmi_tx_beacons_nowait(ar);
+
+ ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
++
++ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
++ ret = -ESHUTDOWN;
++
+ (ret != -EAGAIN);
+ }), 3*HZ);
+
+@@ -631,147 +1051,270 @@ static int ath10k_wmi_cmd_send(struct at
+ return ret;
+ }
+
+-int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
++static struct sk_buff *
++ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
+ {
+- int ret = 0;
+ struct wmi_mgmt_tx_cmd *cmd;
+ struct ieee80211_hdr *hdr;
+- struct sk_buff *wmi_skb;
+- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ struct sk_buff *skb;
+ int len;
++ u32 buf_len = msdu->len;
+ u16 fc;
+
+- hdr = (struct ieee80211_hdr *)skb->data;
++ hdr = (struct ieee80211_hdr *)msdu->data;
+ fc = le16_to_cpu(hdr->frame_control);
+
+ if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
++
++ len = sizeof(cmd->hdr) + msdu->len;
++
++ if ((ieee80211_is_action(hdr->frame_control) ||
++ ieee80211_is_deauth(hdr->frame_control) ||
++ ieee80211_is_disassoc(hdr->frame_control)) &&
++ ieee80211_has_protected(hdr->frame_control)) {
++ len += IEEE80211_CCMP_MIC_LEN;
++ buf_len += IEEE80211_CCMP_MIC_LEN;
++ }
+
+- len = sizeof(cmd->hdr) + skb->len;
+ len = round_up(len, 4);
+
+- wmi_skb = ath10k_wmi_alloc_skb(len);
+- if (!wmi_skb)
+- return -ENOMEM;
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
+
+- cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
++ cmd = (struct wmi_mgmt_tx_cmd *)skb->data;
+
+- cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
++ cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id);
+ cmd->hdr.tx_rate = 0;
+ cmd->hdr.tx_power = 0;
+- cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len));
++ cmd->hdr.buf_len = __cpu_to_le32(buf_len);
+
+- memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
+- memcpy(cmd->buf, skb->data, skb->len);
++ ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr));
++ memcpy(cmd->buf, msdu->data, msdu->len);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
+- wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
++ msdu, skb->len, fc & IEEE80211_FCTL_FTYPE,
+ fc & IEEE80211_FCTL_STYPE);
++ trace_ath10k_tx_hdr(ar, skb->data, skb->len);
++ trace_ath10k_tx_payload(ar, skb->data, skb->len);
+
+- /* Send the management frame buffer to the target */
+- ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
+- if (ret)
+- return ret;
+-
+- /* TODO: report tx status to mac80211 - temporary just ACK */
+- info->flags |= IEEE80211_TX_STAT_ACK;
+- ieee80211_tx_status_irqsafe(ar->hw, skb);
+-
+- return ret;
++ return skb;
+ }
+
+-static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
++static void ath10k_wmi_event_scan_started(struct ath10k *ar)
+ {
+- struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
+- enum wmi_scan_event_type event_type;
+- enum wmi_scan_completion_reason reason;
+- u32 freq;
+- u32 req_id;
+- u32 scan_id;
+- u32 vdev_id;
+-
+- event_type = __le32_to_cpu(event->event_type);
+- reason = __le32_to_cpu(event->reason);
+- freq = __le32_to_cpu(event->channel_freq);
+- req_id = __le32_to_cpu(event->scan_req_id);
+- scan_id = __le32_to_cpu(event->scan_id);
+- vdev_id = __le32_to_cpu(event->vdev_id);
+-
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n");
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "scan event type %d reason %d freq %d req_id %d "
+- "scan_id %d vdev_id %d\n",
+- event_type, reason, freq, req_id, scan_id, vdev_id);
++ lockdep_assert_held(&ar->data_lock);
+
+- spin_lock_bh(&ar->data_lock);
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
++ ath10k_warn(ar, "received scan started event in an invalid scan state: %s (%d)\n",
++ ath10k_scan_state_str(ar->scan.state),
++ ar->scan.state);
++ break;
++ case ATH10K_SCAN_STARTING:
++ ar->scan.state = ATH10K_SCAN_RUNNING;
+
+- switch (event_type) {
+- case WMI_SCAN_EVENT_STARTED:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n");
+- if (ar->scan.in_progress && ar->scan.is_roc)
++ if (ar->scan.is_roc)
+ ieee80211_ready_on_channel(ar->hw);
+
+ complete(&ar->scan.started);
+ break;
+- case WMI_SCAN_EVENT_COMPLETED:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n");
+- switch (reason) {
+- case WMI_SCAN_REASON_COMPLETED:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n");
+- break;
+- case WMI_SCAN_REASON_CANCELLED:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n");
+- break;
+- case WMI_SCAN_REASON_PREEMPTED:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n");
+- break;
+- case WMI_SCAN_REASON_TIMEDOUT:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n");
+- break;
+- default:
+- break;
+- }
+-
+- ar->scan_channel = NULL;
+- if (!ar->scan.in_progress) {
+- ath10k_warn("no scan requested, ignoring\n");
+- break;
+- }
+-
+- if (ar->scan.is_roc) {
+- ath10k_offchan_tx_purge(ar);
++ }
++}
+
+- if (!ar->scan.aborting)
+- ieee80211_remain_on_channel_expired(ar->hw);
+- } else {
+- ieee80211_scan_completed(ar->hw, ar->scan.aborting);
+- }
++static void ath10k_wmi_event_scan_start_failed(struct ath10k *ar)
++{
++ lockdep_assert_held(&ar->data_lock);
+
+- del_timer(&ar->scan.timeout);
+- complete_all(&ar->scan.completed);
+- ar->scan.in_progress = false;
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
++ ath10k_warn(ar, "received scan start failed event in an invalid scan state: %s (%d)\n",
++ ath10k_scan_state_str(ar->scan.state),
++ ar->scan.state);
+ break;
+- case WMI_SCAN_EVENT_BSS_CHANNEL:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n");
+- ar->scan_channel = NULL;
++ case ATH10K_SCAN_STARTING:
++ complete(&ar->scan.started);
++ __ath10k_scan_finish(ar);
+ break;
+- case WMI_SCAN_EVENT_FOREIGN_CHANNEL:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n");
++ }
++}
++
++static void ath10k_wmi_event_scan_completed(struct ath10k *ar)
++{
++ lockdep_assert_held(&ar->data_lock);
++
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ case ATH10K_SCAN_STARTING:
++ /* One suspected reason scan can be completed while starting is
++ * if firmware fails to deliver all scan events to the host,
++ * e.g. when transport pipe is full. This has been observed
++ * with spectral scan phyerr events starving wmi transport
++ * pipe. In such case the "scan completed" event should be (and
++ * is) ignored by the host as it may be just firmware's scan
++ * state machine recovering.
++ */
++ ath10k_warn(ar, "received scan completed event in an invalid scan state: %s (%d)\n",
++ ath10k_scan_state_str(ar->scan.state),
++ ar->scan.state);
++ break;
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
++ __ath10k_scan_finish(ar);
++ break;
++ }
++}
++
++static void ath10k_wmi_event_scan_bss_chan(struct ath10k *ar)
++{
++ lockdep_assert_held(&ar->data_lock);
++
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ case ATH10K_SCAN_STARTING:
++ ath10k_warn(ar, "received scan bss chan event in an invalid scan state: %s (%d)\n",
++ ath10k_scan_state_str(ar->scan.state),
++ ar->scan.state);
++ break;
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
++ ar->scan_channel = NULL;
++ break;
++ }
++}
++
++static void ath10k_wmi_event_scan_foreign_chan(struct ath10k *ar, u32 freq)
++{
++ lockdep_assert_held(&ar->data_lock);
++
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ case ATH10K_SCAN_STARTING:
++ ath10k_warn(ar, "received scan foreign chan event in an invalid scan state: %s (%d)\n",
++ ath10k_scan_state_str(ar->scan.state),
++ ar->scan.state);
++ break;
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
+ ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq);
+- if (ar->scan.in_progress && ar->scan.is_roc &&
+- ar->scan.roc_freq == freq) {
++
++ if (ar->scan.is_roc && ar->scan.roc_freq == freq)
+ complete(&ar->scan.on_channel);
+- }
+ break;
++ }
++}
++
++static const char *
++ath10k_wmi_event_scan_type_str(enum wmi_scan_event_type type,
++ enum wmi_scan_completion_reason reason)
++{
++ switch (type) {
++ case WMI_SCAN_EVENT_STARTED:
++ return "started";
++ case WMI_SCAN_EVENT_COMPLETED:
++ switch (reason) {
++ case WMI_SCAN_REASON_COMPLETED:
++ return "completed";
++ case WMI_SCAN_REASON_CANCELLED:
++ return "completed [cancelled]";
++ case WMI_SCAN_REASON_PREEMPTED:
++ return "completed [preempted]";
++ case WMI_SCAN_REASON_TIMEDOUT:
++ return "completed [timedout]";
++ case WMI_SCAN_REASON_MAX:
++ break;
++ }
++ return "completed [unknown]";
++ case WMI_SCAN_EVENT_BSS_CHANNEL:
++ return "bss channel";
++ case WMI_SCAN_EVENT_FOREIGN_CHANNEL:
++ return "foreign channel";
+ case WMI_SCAN_EVENT_DEQUEUED:
+- ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n");
+- break;
++ return "dequeued";
+ case WMI_SCAN_EVENT_PREEMPTED:
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n");
++ return "preempted";
++ case WMI_SCAN_EVENT_START_FAILED:
++ return "start failed";
++ default:
++ return "unknown";
++ }
++}
++
++static int ath10k_wmi_op_pull_scan_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_scan_ev_arg *arg)
++{
++ struct wmi_scan_event *ev = (void *)skb->data;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ skb_pull(skb, sizeof(*ev));
++ arg->event_type = ev->event_type;
++ arg->reason = ev->reason;
++ arg->channel_freq = ev->channel_freq;
++ arg->scan_req_id = ev->scan_req_id;
++ arg->scan_id = ev->scan_id;
++ arg->vdev_id = ev->vdev_id;
++
++ return 0;
++}
++
++int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_scan_ev_arg arg = {};
++ enum wmi_scan_event_type event_type;
++ enum wmi_scan_completion_reason reason;
++ u32 freq;
++ u32 req_id;
++ u32 scan_id;
++ u32 vdev_id;
++ int ret;
++
++ ret = ath10k_wmi_pull_scan(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse scan event: %d\n", ret);
++ return ret;
++ }
++
++ event_type = __le32_to_cpu(arg.event_type);
++ reason = __le32_to_cpu(arg.reason);
++ freq = __le32_to_cpu(arg.channel_freq);
++ req_id = __le32_to_cpu(arg.scan_req_id);
++ scan_id = __le32_to_cpu(arg.scan_id);
++ vdev_id = __le32_to_cpu(arg.vdev_id);
++
++ spin_lock_bh(&ar->data_lock);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "scan event %s type %d reason %d freq %d req_id %d scan_id %d vdev_id %d state %s (%d)\n",
++ ath10k_wmi_event_scan_type_str(event_type, reason),
++ event_type, reason, freq, req_id, scan_id, vdev_id,
++ ath10k_scan_state_str(ar->scan.state), ar->scan.state);
++
++ switch (event_type) {
++ case WMI_SCAN_EVENT_STARTED:
++ ath10k_wmi_event_scan_started(ar);
++ break;
++ case WMI_SCAN_EVENT_COMPLETED:
++ ath10k_wmi_event_scan_completed(ar);
++ break;
++ case WMI_SCAN_EVENT_BSS_CHANNEL:
++ ath10k_wmi_event_scan_bss_chan(ar);
++ break;
++ case WMI_SCAN_EVENT_FOREIGN_CHANNEL:
++ ath10k_wmi_event_scan_foreign_chan(ar, freq);
+ break;
+ case WMI_SCAN_EVENT_START_FAILED:
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n");
++ ath10k_warn(ar, "received scan start failure event\n");
++ ath10k_wmi_event_scan_start_failed(ar);
+ break;
++ case WMI_SCAN_EVENT_DEQUEUED:
++ case WMI_SCAN_EVENT_PREEMPTED:
+ default:
+ break;
+ }
+@@ -865,13 +1408,86 @@ static inline u8 get_rate_idx(u32 rate,
+ return rate_idx;
+ }
+
+-static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
++/* If keys are configured, HW decrypts all frames
++ * with protected bit set. Mark such frames as decrypted.
++ */
++static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct ieee80211_rx_status *status)
++{
++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
++ unsigned int hdrlen;
++ bool peer_key;
++ u8 *addr, keyidx;
++
++ if (!ieee80211_is_auth(hdr->frame_control) ||
++ !ieee80211_has_protected(hdr->frame_control))
++ return;
++
++ hdrlen = ieee80211_hdrlen(hdr->frame_control);
++ if (skb->len < (hdrlen + IEEE80211_WEP_IV_LEN))
++ return;
++
++ keyidx = skb->data[hdrlen + (IEEE80211_WEP_IV_LEN - 1)] >> WEP_KEYID_SHIFT;
++ addr = ieee80211_get_SA(hdr);
++
++ spin_lock_bh(&ar->data_lock);
++ peer_key = ath10k_mac_is_peer_wep_key_set(ar, addr, keyidx);
++ spin_unlock_bh(&ar->data_lock);
++
++ if (peer_key) {
++ ath10k_dbg(ar, ATH10K_DBG_MAC,
++ "mac wep key present for peer %pM\n", addr);
++ status->flag |= RX_FLAG_DECRYPTED;
++ }
++}
++
++static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_mgmt_rx_ev_arg *arg)
+ {
+ struct wmi_mgmt_rx_event_v1 *ev_v1;
+ struct wmi_mgmt_rx_event_v2 *ev_v2;
+ struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
++ size_t pull_len;
++ u32 msdu_len;
++
++ if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
++ ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
++ ev_hdr = &ev_v2->hdr.v1;
++ pull_len = sizeof(*ev_v2);
++ } else {
++ ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data;
++ ev_hdr = &ev_v1->hdr;
++ pull_len = sizeof(*ev_v1);
++ }
++
++ if (skb->len < pull_len)
++ return -EPROTO;
++
++ skb_pull(skb, pull_len);
++ arg->channel = ev_hdr->channel;
++ arg->buf_len = ev_hdr->buf_len;
++ arg->status = ev_hdr->status;
++ arg->snr = ev_hdr->snr;
++ arg->phy_mode = ev_hdr->phy_mode;
++ arg->rate = ev_hdr->rate;
++
++ msdu_len = __le32_to_cpu(arg->buf_len);
++ if (skb->len < msdu_len)
++ return -EPROTO;
++
++ /* the WMI buffer might've ended up being padded to 4 bytes due to HTC
++ * trailer with credit update. Trim the excess garbage.
++ */
++ skb_trim(skb, msdu_len);
++
++ return 0;
++}
++
++int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_mgmt_rx_ev_arg arg = {};
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+- struct ieee80211_channel *ch;
+ struct ieee80211_hdr *hdr;
+ u32 rx_status;
+ u32 channel;
+@@ -880,28 +1496,24 @@ static int ath10k_wmi_event_mgmt_rx(stru
+ u32 rate;
+ u32 buf_len;
+ u16 fc;
+- int pull_len;
++ int ret;
+
+- if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
+- ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
+- ev_hdr = &ev_v2->hdr.v1;
+- pull_len = sizeof(*ev_v2);
+- } else {
+- ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data;
+- ev_hdr = &ev_v1->hdr;
+- pull_len = sizeof(*ev_v1);
++ ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse mgmt rx event: %d\n", ret);
++ return ret;
+ }
+
+- channel = __le32_to_cpu(ev_hdr->channel);
+- buf_len = __le32_to_cpu(ev_hdr->buf_len);
+- rx_status = __le32_to_cpu(ev_hdr->status);
+- snr = __le32_to_cpu(ev_hdr->snr);
+- phy_mode = __le32_to_cpu(ev_hdr->phy_mode);
+- rate = __le32_to_cpu(ev_hdr->rate);
++ channel = __le32_to_cpu(arg.channel);
++ buf_len = __le32_to_cpu(arg.buf_len);
++ rx_status = __le32_to_cpu(arg.status);
++ snr = __le32_to_cpu(arg.snr);
++ phy_mode = __le32_to_cpu(arg.phy_mode);
++ rate = __le32_to_cpu(arg.rate);
+
+ memset(status, 0, sizeof(*status));
+
+- ath10k_dbg(ATH10K_DBG_MGMT,
++ ath10k_dbg(ar, ATH10K_DBG_MGMT,
+ "event mgmt rx status %08x\n", rx_status);
+
+ if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+@@ -919,66 +1531,70 @@ static int ath10k_wmi_event_mgmt_rx(stru
+ return 0;
+ }
+
+- if (rx_status & WMI_RX_STATUS_ERR_CRC)
+- status->flag |= RX_FLAG_FAILED_FCS_CRC;
++ if (rx_status & WMI_RX_STATUS_ERR_CRC) {
++ dev_kfree_skb(skb);
++ return 0;
++ }
++
+ if (rx_status & WMI_RX_STATUS_ERR_MIC)
+ status->flag |= RX_FLAG_MMIC_ERROR;
+
+- /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to
++ /* Hardware can Rx CCK rates on 5GHz. In that case phy_mode is set to
+ * MODE_11B. This means phy_mode is not a reliable source for the band
+- * of mgmt rx. */
+-
+- ch = ar->scan_channel;
+- if (!ch)
+- ch = ar->rx_channel;
+-
+- if (ch) {
+- status->band = ch->band;
+-
+- if (phy_mode == MODE_11B &&
+- status->band == IEEE80211_BAND_5GHZ)
+- ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
++ * of mgmt rx.
++ */
++ if (channel >= 1 && channel <= 14) {
++ status->band = IEEE80211_BAND_2GHZ;
++ } else if (channel >= 36 && channel <= 165) {
++ status->band = IEEE80211_BAND_5GHZ;
+ } else {
+- ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n");
+- status->band = phy_mode_to_band(phy_mode);
++ /* Shouldn't happen unless list of advertised channels to
++ * mac80211 has been changed.
++ */
++ WARN_ON_ONCE(1);
++ dev_kfree_skb(skb);
++ return 0;
+ }
+
++ if (phy_mode == MODE_11B && status->band == IEEE80211_BAND_5GHZ)
++ ath10k_dbg(ar, ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
++
+ status->freq = ieee80211_channel_to_frequency(channel, status->band);
+ status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
+ status->rate_idx = get_rate_idx(rate, status->band);
+
+- skb_pull(skb, pull_len);
+-
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_control);
+
++ ath10k_wmi_handle_wep_reauth(ar, skb, status);
++
+ /* FW delivers WEP Shared Auth frame with Protected Bit set and
+ * encrypted payload. However in case of PMF it delivers decrypted
+ * frames with Protected Bit set. */
+ if (ieee80211_has_protected(hdr->frame_control) &&
+ !ieee80211_is_auth(hdr->frame_control)) {
+- status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+- RX_FLAG_MMIC_STRIPPED;
+- hdr->frame_control = __cpu_to_le16(fc &
++ status->flag |= RX_FLAG_DECRYPTED;
++
++ if (!ieee80211_is_action(hdr->frame_control) &&
++ !ieee80211_is_deauth(hdr->frame_control) &&
++ !ieee80211_is_disassoc(hdr->frame_control)) {
++ status->flag |= RX_FLAG_IV_STRIPPED |
++ RX_FLAG_MMIC_STRIPPED;
++ hdr->frame_control = __cpu_to_le16(fc &
+ ~IEEE80211_FCTL_PROTECTED);
++ }
+ }
+
+- ath10k_dbg(ATH10K_DBG_MGMT,
++ ath10k_dbg(ar, ATH10K_DBG_MGMT,
+ "event mgmt rx skb %p len %d ftype %02x stype %02x\n",
+ skb, skb->len,
+ fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE);
+
+- ath10k_dbg(ATH10K_DBG_MGMT,
++ ath10k_dbg(ar, ATH10K_DBG_MGMT,
+ "event mgmt rx freq %d band %d snr %d, rate_idx %d\n",
+ status->freq, status->band, status->signal,
+ status->rate_idx);
+
+- /*
+- * packets from HTC come aligned to 4byte boundaries
+- * because they can originally come in along with a trailer
+- */
+- skb_trim(skb, buf_len);
+-
+ ieee80211_rx(ar->hw, skb);
+ return 0;
+ }
+@@ -1002,37 +1618,65 @@ exit:
+ return idx;
+ }
+
+-static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
++static int ath10k_wmi_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_ch_info_ev_arg *arg)
++{
++ struct wmi_chan_info_event *ev = (void *)skb->data;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ skb_pull(skb, sizeof(*ev));
++ arg->err_code = ev->err_code;
++ arg->freq = ev->freq;
++ arg->cmd_flags = ev->cmd_flags;
++ arg->noise_floor = ev->noise_floor;
++ arg->rx_clear_count = ev->rx_clear_count;
++ arg->cycle_count = ev->cycle_count;
++
++ return 0;
++}
++
++void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+ {
+- struct wmi_chan_info_event *ev;
++ struct wmi_ch_info_ev_arg arg = {};
+ struct survey_info *survey;
+ u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
+- int idx;
++ int idx, ret;
+
+- ev = (struct wmi_chan_info_event *)skb->data;
++ ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
++ return;
++ }
+
+- err_code = __le32_to_cpu(ev->err_code);
+- freq = __le32_to_cpu(ev->freq);
+- cmd_flags = __le32_to_cpu(ev->cmd_flags);
+- noise_floor = __le32_to_cpu(ev->noise_floor);
+- rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
+- cycle_count = __le32_to_cpu(ev->cycle_count);
++ err_code = __le32_to_cpu(arg.err_code);
++ freq = __le32_to_cpu(arg.freq);
++ cmd_flags = __le32_to_cpu(arg.cmd_flags);
++ noise_floor = __le32_to_cpu(arg.noise_floor);
++ rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
++ cycle_count = __le32_to_cpu(arg.cycle_count);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+ err_code, freq, cmd_flags, noise_floor, rx_clear_count,
+ cycle_count);
+
+ spin_lock_bh(&ar->data_lock);
+
+- if (!ar->scan.in_progress) {
+- ath10k_warn("chan info event without a scan request?\n");
++ switch (ar->scan.state) {
++ case ATH10K_SCAN_IDLE:
++ case ATH10K_SCAN_STARTING:
++ ath10k_warn(ar, "received chan info event without a scan request, ignoring\n");
+ goto exit;
++ case ATH10K_SCAN_RUNNING:
++ case ATH10K_SCAN_ABORTING:
++ break;
+ }
+
+ idx = freq_to_idx(ar, freq);
+ if (idx >= ARRAY_SIZE(ar->survey)) {
+- ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n",
++ ath10k_warn(ar, "chan info: invalid frequency %d (idx %d out of bounds)\n",
+ freq, idx);
+ goto exit;
+ }
+@@ -1061,191 +1705,579 @@ exit:
+ spin_unlock_bh(&ar->data_lock);
+ }
+
+-static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
++void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
+ }
+
+-static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
++int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug mesg len %d\n",
+ skb->len);
+
+- trace_ath10k_wmi_dbglog(skb->data, skb->len);
++ trace_ath10k_wmi_dbglog(ar, skb->data, skb->len);
+
+ return 0;
+ }
+
+-static void ath10k_wmi_event_update_stats(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
++ struct ath10k_fw_stats_pdev *dst)
+ {
+- struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
+-
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
+-
+- ath10k_debug_read_target_stats(ar, ev);
+-}
+-
+-static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
+- struct sk_buff *skb)
++ dst->ch_noise_floor = __le32_to_cpu(src->chan_nf);
++ dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
++ dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
++ dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count);
++ dst->cycle_count = __le32_to_cpu(src->cycle_count);
++ dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
++ dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
++}
++
++void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
++ struct ath10k_fw_stats_pdev *dst)
++{
++ dst->comp_queued = __le32_to_cpu(src->comp_queued);
++ dst->comp_delivered = __le32_to_cpu(src->comp_delivered);
++ dst->msdu_enqued = __le32_to_cpu(src->msdu_enqued);
++ dst->mpdu_enqued = __le32_to_cpu(src->mpdu_enqued);
++ dst->wmm_drop = __le32_to_cpu(src->wmm_drop);
++ dst->local_enqued = __le32_to_cpu(src->local_enqued);
++ dst->local_freed = __le32_to_cpu(src->local_freed);
++ dst->hw_queued = __le32_to_cpu(src->hw_queued);
++ dst->hw_reaped = __le32_to_cpu(src->hw_reaped);
++ dst->underrun = __le32_to_cpu(src->underrun);
++ dst->tx_abort = __le32_to_cpu(src->tx_abort);
++ dst->mpdus_requed = __le32_to_cpu(src->mpdus_requed);
++ dst->tx_ko = __le32_to_cpu(src->tx_ko);
++ dst->data_rc = __le32_to_cpu(src->data_rc);
++ dst->self_triggers = __le32_to_cpu(src->self_triggers);
++ dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure);
++ dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err);
++ dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry);
++ dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout);
++ dst->pdev_resets = __le32_to_cpu(src->pdev_resets);
++ dst->phy_underrun = __le32_to_cpu(src->phy_underrun);
++ dst->txop_ovf = __le32_to_cpu(src->txop_ovf);
++}
++
++void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
++ struct ath10k_fw_stats_pdev *dst)
++{
++ dst->mid_ppdu_route_change = __le32_to_cpu(src->mid_ppdu_route_change);
++ dst->status_rcvd = __le32_to_cpu(src->status_rcvd);
++ dst->r0_frags = __le32_to_cpu(src->r0_frags);
++ dst->r1_frags = __le32_to_cpu(src->r1_frags);
++ dst->r2_frags = __le32_to_cpu(src->r2_frags);
++ dst->r3_frags = __le32_to_cpu(src->r3_frags);
++ dst->htt_msdus = __le32_to_cpu(src->htt_msdus);
++ dst->htt_mpdus = __le32_to_cpu(src->htt_mpdus);
++ dst->loc_msdus = __le32_to_cpu(src->loc_msdus);
++ dst->loc_mpdus = __le32_to_cpu(src->loc_mpdus);
++ dst->oversize_amsdu = __le32_to_cpu(src->oversize_amsdu);
++ dst->phy_errs = __le32_to_cpu(src->phy_errs);
++ dst->phy_err_drop = __le32_to_cpu(src->phy_err_drop);
++ dst->mpdu_errs = __le32_to_cpu(src->mpdu_errs);
++}
++
++void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src,
++ struct ath10k_fw_stats_pdev *dst)
++{
++ dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
++ dst->rts_bad = __le32_to_cpu(src->rts_bad);
++ dst->rts_good = __le32_to_cpu(src->rts_good);
++ dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
++ dst->no_beacons = __le32_to_cpu(src->no_beacons);
++ dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
++}
++
++void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
++ struct ath10k_fw_stats_peer *dst)
++{
++ ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
++ dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
++ dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
++}
++
++static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct ath10k_fw_stats *stats)
+ {
+- struct wmi_vdev_start_response_event *ev;
++ const struct wmi_stats_event *ev = (void *)skb->data;
++ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
++ int i;
+
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
++ if (!skb_pull(skb, sizeof(*ev)))
++ return -EPROTO;
+
+- ev = (struct wmi_vdev_start_response_event *)skb->data;
++ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
++ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
++ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
++
++ for (i = 0; i < num_pdev_stats; i++) {
++ const struct wmi_pdev_stats *src;
++ struct ath10k_fw_stats_pdev *dst;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
+
+- if (WARN_ON(__le32_to_cpu(ev->status)))
+- return;
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
+
+- complete(&ar->vdev_setup_done);
+-}
++ ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
++ ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
++ ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
+
+-static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
+- struct sk_buff *skb)
+-{
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
+- complete(&ar->vdev_setup_done);
+-}
++ list_add_tail(&dst->list, &stats->pdevs);
++ }
+
+-static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
+- struct sk_buff *skb)
+-{
+- struct wmi_peer_sta_kickout_event *ev;
+- struct ieee80211_sta *sta;
++ /* fw doesn't implement vdev stats */
+
+- ev = (struct wmi_peer_sta_kickout_event *)skb->data;
++ for (i = 0; i < num_peer_stats; i++) {
++ const struct wmi_peer_stats *src;
++ struct ath10k_fw_stats_peer *dst;
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
+- ev->peer_macaddr.addr);
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
+
+- rcu_read_lock();
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
+
+- sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL);
+- if (!sta) {
+- ath10k_warn("Spurious quick kickout for STA %pM\n",
+- ev->peer_macaddr.addr);
+- goto exit;
++ ath10k_wmi_pull_peer_stats(src, dst);
++ list_add_tail(&dst->list, &stats->peers);
+ }
+
+- ieee80211_report_low_ack(sta, 10);
+-
+-exit:
+- rcu_read_unlock();
++ return 0;
+ }
+
+-/*
+- * FIXME
+- *
+- * We don't report to mac80211 sleep state of connected
+- * stations. Due to this mac80211 can't fill in TIM IE
+- * correctly.
+- *
+- * I know of no way of getting nullfunc frames that contain
+- * sleep transition from connected stations - these do not
+- * seem to be sent from the target to the host. There also
+- * doesn't seem to be a dedicated event for that. So the
+- * only way left to do this would be to read tim_bitmap
+- * during SWBA.
+- *
+- * We could probably try using tim_bitmap from SWBA to tell
+- * mac80211 which stations are asleep and which are not. The
+- * problem here is calling mac80211 functions so many times
+- * could take too long and make us miss the time to submit
+- * the beacon to the target.
+- *
+- * So as a workaround we try to extend the TIM IE if there
+- * is unicast buffered for stations with aid > 7 and fill it
+- * in ourselves.
+- */
+-static void ath10k_wmi_update_tim(struct ath10k *ar,
+- struct ath10k_vif *arvif,
+- struct sk_buff *bcn,
+- struct wmi_bcn_info *bcn_info)
++static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct ath10k_fw_stats *stats)
+ {
+- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
+- struct ieee80211_tim_ie *tim;
+- u8 *ies, *ie;
+- u8 ie_len, pvm_len;
++ const struct wmi_stats_event *ev = (void *)skb->data;
++ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
++ int i;
+
+- /* if next SWBA has no tim_changed the tim_bitmap is garbage.
+- * we must copy the bitmap upon change and reuse it later */
+- if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) {
+- int i;
++ if (!skb_pull(skb, sizeof(*ev)))
++ return -EPROTO;
+
+- BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
+- sizeof(bcn_info->tim_info.tim_bitmap));
++ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
++ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
++ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
++
++ for (i = 0; i < num_pdev_stats; i++) {
++ const struct wmi_10x_pdev_stats *src;
++ struct ath10k_fw_stats_pdev *dst;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
+
+- for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
+- __le32 t = bcn_info->tim_info.tim_bitmap[i / 4];
+- u32 v = __le32_to_cpu(t);
+- arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
+- }
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
+
+- /* FW reports either length 0 or 16
+- * so we calculate this on our own */
+- arvif->u.ap.tim_len = 0;
+- for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++)
+- if (arvif->u.ap.tim_bitmap[i])
+- arvif->u.ap.tim_len = i;
++ ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
++ ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
++ ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
++ ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
+
+- arvif->u.ap.tim_len++;
++ list_add_tail(&dst->list, &stats->pdevs);
+ }
+
+- ies = bcn->data;
+- ies += ieee80211_hdrlen(hdr->frame_control);
+- ies += 12; /* fixed parameters */
++ /* fw doesn't implement vdev stats */
+
+- ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
+- (u8 *)skb_tail_pointer(bcn) - ies);
+- if (!ie) {
+- if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+- ath10k_warn("no tim ie found;\n");
+- return;
+- }
++ for (i = 0; i < num_peer_stats; i++) {
++ const struct wmi_10x_peer_stats *src;
++ struct ath10k_fw_stats_peer *dst;
+
+- tim = (void *)ie + 2;
+- ie_len = ie[1];
+- pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
+
+- if (pvm_len < arvif->u.ap.tim_len) {
+- int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len;
+- int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len);
+- void *next_ie = ie + 2 + ie_len;
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
+
+- if (skb_put(bcn, expand_size)) {
+- memmove(next_ie + expand_size, next_ie, move_size);
++ ath10k_wmi_pull_peer_stats(&src->old, dst);
+
+- ie[1] += expand_size;
+- ie_len += expand_size;
+- pvm_len += expand_size;
+- } else {
+- ath10k_warn("tim expansion failed\n");
+- }
+- }
++ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+
+- if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) {
+- ath10k_warn("tim pvm length is too great (%d)\n", pvm_len);
+- return;
++ list_add_tail(&dst->list, &stats->peers);
+ }
+
+- tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
+- memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
++ return 0;
++}
+
+- if (tim->dtim_count == 0) {
+- ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
++static int ath10k_wmi_10_2_op_pull_fw_stats(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct ath10k_fw_stats *stats)
++{
++ const struct wmi_10_2_stats_event *ev = (void *)skb->data;
++ u32 num_pdev_stats;
++ u32 num_pdev_ext_stats;
++ u32 num_vdev_stats;
++ u32 num_peer_stats;
++ int i;
+
+- if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1)
+- ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
+- }
++ if (!skb_pull(skb, sizeof(*ev)))
++ return -EPROTO;
+
+- ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
+- tim->dtim_count, tim->dtim_period,
+- tim->bitmap_ctrl, pvm_len);
++ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
++ num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
++ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
++ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
++
++ for (i = 0; i < num_pdev_stats; i++) {
++ const struct wmi_10_2_pdev_stats *src;
++ struct ath10k_fw_stats_pdev *dst;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
++ ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
++ ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
++ ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
++ /* FIXME: expose 10.2 specific values */
++
++ list_add_tail(&dst->list, &stats->pdevs);
++ }
++
++ for (i = 0; i < num_pdev_ext_stats; i++) {
++ const struct wmi_10_2_pdev_ext_stats *src;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
++
++ /* FIXME: expose values to userspace
++ *
++ * Note: Even though this loop seems to do nothing it is
++ * required to parse following sub-structures properly.
++ */
++ }
++
++ /* fw doesn't implement vdev stats */
++
++ for (i = 0; i < num_peer_stats; i++) {
++ const struct wmi_10_2_peer_stats *src;
++ struct ath10k_fw_stats_peer *dst;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_pull_peer_stats(&src->old, dst);
++
++ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
++ /* FIXME: expose 10.2 specific values */
++
++ list_add_tail(&dst->list, &stats->peers);
++ }
++
++ return 0;
++}
++
++static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct ath10k_fw_stats *stats)
++{
++ const struct wmi_10_2_stats_event *ev = (void *)skb->data;
++ u32 num_pdev_stats;
++ u32 num_pdev_ext_stats;
++ u32 num_vdev_stats;
++ u32 num_peer_stats;
++ int i;
++
++ if (!skb_pull(skb, sizeof(*ev)))
++ return -EPROTO;
++
++ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
++ num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
++ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
++ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
++
++ for (i = 0; i < num_pdev_stats; i++) {
++ const struct wmi_10_2_pdev_stats *src;
++ struct ath10k_fw_stats_pdev *dst;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
++ ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
++ ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
++ ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst);
++ /* FIXME: expose 10.2 specific values */
++
++ list_add_tail(&dst->list, &stats->pdevs);
++ }
++
++ for (i = 0; i < num_pdev_ext_stats; i++) {
++ const struct wmi_10_2_pdev_ext_stats *src;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
++
++ /* FIXME: expose values to userspace
++ *
++ * Note: Even though this loop seems to do nothing it is
++ * required to parse following sub-structures properly.
++ */
++ }
++
++ /* fw doesn't implement vdev stats */
++
++ for (i = 0; i < num_peer_stats; i++) {
++ const struct wmi_10_2_4_peer_stats *src;
++ struct ath10k_fw_stats_peer *dst;
++
++ src = (void *)skb->data;
++ if (!skb_pull(skb, sizeof(*src)))
++ return -EPROTO;
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_pull_peer_stats(&src->common.old, dst);
++
++ dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate);
++ /* FIXME: expose 10.2 specific values */
++
++ list_add_tail(&dst->list, &stats->peers);
++ }
++
++ return 0;
++}
++
++void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb)
++{
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
++ ath10k_debug_fw_stats_process(ar, skb);
++}
++
++static int
++ath10k_wmi_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_vdev_start_ev_arg *arg)
++{
++ struct wmi_vdev_start_response_event *ev = (void *)skb->data;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ skb_pull(skb, sizeof(*ev));
++ arg->vdev_id = ev->vdev_id;
++ arg->req_id = ev->req_id;
++ arg->resp_type = ev->resp_type;
++ arg->status = ev->status;
++
++ return 0;
++}
++
++void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_vdev_start_ev_arg arg = {};
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
++
++ ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret);
++ return;
++ }
++
++ if (WARN_ON(__le32_to_cpu(arg.status)))
++ return;
++
++ complete(&ar->vdev_setup_done);
++}
++
++void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb)
++{
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
++ complete(&ar->vdev_setup_done);
++}
++
++static int
++ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_peer_kick_ev_arg *arg)
++{
++ struct wmi_peer_sta_kickout_event *ev = (void *)skb->data;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ skb_pull(skb, sizeof(*ev));
++ arg->mac_addr = ev->peer_macaddr.addr;
++
++ return 0;
++}
++
++void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_peer_kick_ev_arg arg = {};
++ struct ieee80211_sta *sta;
++ int ret;
++
++ ret = ath10k_wmi_pull_peer_kick(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse peer kickout event: %d\n",
++ ret);
++ return;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
++ arg.mac_addr);
++
++ rcu_read_lock();
++
++ sta = ieee80211_find_sta_by_ifaddr(ar->hw, arg.mac_addr, NULL);
++ if (!sta) {
++ ath10k_warn(ar, "Spurious quick kickout for STA %pM\n",
++ arg.mac_addr);
++ goto exit;
++ }
++
++ ieee80211_report_low_ack(sta, 10);
++
++exit:
++ rcu_read_unlock();
++}
++
++/*
++ * FIXME
++ *
++ * We don't report to mac80211 sleep state of connected
++ * stations. Due to this mac80211 can't fill in TIM IE
++ * correctly.
++ *
++ * I know of no way of getting nullfunc frames that contain
++ * sleep transition from connected stations - these do not
++ * seem to be sent from the target to the host. There also
++ * doesn't seem to be a dedicated event for that. So the
++ * only way left to do this would be to read tim_bitmap
++ * during SWBA.
++ *
++ * We could probably try using tim_bitmap from SWBA to tell
++ * mac80211 which stations are asleep and which are not. The
++ * problem here is calling mac80211 functions so many times
++ * could take too long and make us miss the time to submit
++ * the beacon to the target.
++ *
++ * So as a workaround we try to extend the TIM IE if there
++ * is unicast buffered for stations with aid > 7 and fill it
++ * in ourselves.
++ */
++static void ath10k_wmi_update_tim(struct ath10k *ar,
++ struct ath10k_vif *arvif,
++ struct sk_buff *bcn,
++ const struct wmi_tim_info *tim_info)
++{
++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
++ struct ieee80211_tim_ie *tim;
++ u8 *ies, *ie;
++ u8 ie_len, pvm_len;
++ __le32 t;
++ u32 v;
++
++ /* if next SWBA has no tim_changed the tim_bitmap is garbage.
++ * we must copy the bitmap upon change and reuse it later */
++ if (__le32_to_cpu(tim_info->tim_changed)) {
++ int i;
++
++ BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
++ sizeof(tim_info->tim_bitmap));
++
++ for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
++ t = tim_info->tim_bitmap[i / 4];
++ v = __le32_to_cpu(t);
++ arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
++ }
++
++ /* FW reports either length 0 or 16
++ * so we calculate this on our own */
++ arvif->u.ap.tim_len = 0;
++ for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++)
++ if (arvif->u.ap.tim_bitmap[i])
++ arvif->u.ap.tim_len = i;
++
++ arvif->u.ap.tim_len++;
++ }
++
++ ies = bcn->data;
++ ies += ieee80211_hdrlen(hdr->frame_control);
++ ies += 12; /* fixed parameters */
++
++ ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
++ (u8 *)skb_tail_pointer(bcn) - ies);
++ if (!ie) {
++ if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
++ ath10k_warn(ar, "no tim ie found;\n");
++ return;
++ }
++
++ tim = (void *)ie + 2;
++ ie_len = ie[1];
++ pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */
++
++ if (pvm_len < arvif->u.ap.tim_len) {
++ int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len;
++ int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len);
++ void *next_ie = ie + 2 + ie_len;
++
++ if (skb_put(bcn, expand_size)) {
++ memmove(next_ie + expand_size, next_ie, move_size);
++
++ ie[1] += expand_size;
++ ie_len += expand_size;
++ pvm_len += expand_size;
++ } else {
++ ath10k_warn(ar, "tim expansion failed\n");
++ }
++ }
++
++ if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) {
++ ath10k_warn(ar, "tim pvm length is too great (%d)\n", pvm_len);
++ return;
++ }
++
++ tim->bitmap_ctrl = !!__le32_to_cpu(tim_info->tim_mcast);
++ memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
++
++ if (tim->dtim_count == 0) {
++ ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
++
++ if (__le32_to_cpu(tim_info->tim_mcast) == 1)
++ ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
++ }
++
++ ath10k_dbg(ar, ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
++ tim->dtim_count, tim->dtim_period,
++ tim->bitmap_ctrl, pvm_len);
+ }
+
+ static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len,
+- struct wmi_p2p_noa_info *noa)
++ const struct wmi_p2p_noa_info *noa)
+ {
+ struct ieee80211_p2p_noa_attr *noa_attr;
+ u8 ctwindow_oppps = noa->ctwindow_oppps;
+@@ -1287,14 +2319,13 @@ static void ath10k_p2p_fill_noa_ie(u8 *d
+ *noa_attr_len = __cpu_to_le16(attr_len);
+ }
+
+-static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa)
++static u32 ath10k_p2p_calc_noa_ie_len(const struct wmi_p2p_noa_info *noa)
+ {
+ u32 len = 0;
+ u8 noa_descriptors = noa->num_descriptors;
+ u8 opp_ps_info = noa->ctwindow_oppps;
+ bool opps_enabled = !!(opp_ps_info & WMI_P2P_OPPPS_ENABLE_BIT);
+
+-
+ if (!noa_descriptors && !opps_enabled)
+ return len;
+
+@@ -1308,16 +2339,15 @@ static u32 ath10k_p2p_calc_noa_ie_len(st
+
+ static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
+ struct sk_buff *bcn,
+- struct wmi_bcn_info *bcn_info)
++ const struct wmi_p2p_noa_info *noa)
+ {
+- struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info;
+ u8 *new_data, *old_data = arvif->u.ap.noa_data;
+ u32 new_len;
+
+ if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+ return;
+
+- ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed);
++ ath10k_dbg(ar, ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed);
+ if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) {
+ new_len = ath10k_p2p_calc_noa_ie_len(noa);
+ if (!new_len)
+@@ -1351,22 +2381,59 @@ cleanup:
+ kfree(old_data);
+ }
+
++static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_swba_ev_arg *arg)
++{
++ struct wmi_host_swba_event *ev = (void *)skb->data;
++ u32 map;
++ size_t i;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ skb_pull(skb, sizeof(*ev));
++ arg->vdev_map = ev->vdev_map;
++
++ for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) {
++ if (!(map & BIT(0)))
++ continue;
++
++ /* If this happens there were some changes in firmware and
++ * ath10k should update the max size of tim_info array.
++ */
++ if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info)))
++ break;
++
++ arg->tim_info[i] = &ev->bcn_info[i].tim_info;
++ arg->noa_info[i] = &ev->bcn_info[i].p2p_noa_info;
++ i++;
++ }
++
++ return 0;
++}
+
+-static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
++void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
+ {
+- struct wmi_host_swba_event *ev;
++ struct wmi_swba_ev_arg arg = {};
+ u32 map;
+ int i = -1;
+- struct wmi_bcn_info *bcn_info;
++ const struct wmi_tim_info *tim_info;
++ const struct wmi_p2p_noa_info *noa_info;
+ struct ath10k_vif *arvif;
+ struct sk_buff *bcn;
++ dma_addr_t paddr;
+ int ret, vdev_id = 0;
+
+- ev = (struct wmi_host_swba_event *)skb->data;
+- map = __le32_to_cpu(ev->vdev_map);
++ ret = ath10k_wmi_pull_swba(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse swba event: %d\n", ret);
++ return;
++ }
++
++ map = __le32_to_cpu(arg.vdev_map);
+
+- ath10k_dbg(ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n",
+- ev->vdev_map);
++ ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n",
++ map);
+
+ for (; map; map >>= 1, vdev_id++) {
+ if (!(map & 0x1))
+@@ -1375,27 +2442,29 @@ static void ath10k_wmi_event_host_swba(s
+ i++;
+
+ if (i >= WMI_MAX_AP_VDEV) {
+- ath10k_warn("swba has corrupted vdev map\n");
++ ath10k_warn(ar, "swba has corrupted vdev map\n");
+ break;
+ }
+
+- bcn_info = &ev->bcn_info[i];
++ tim_info = arg.tim_info[i];
++ noa_info = arg.noa_info[i];
+
+- ath10k_dbg(ATH10K_DBG_MGMT,
++ ath10k_dbg(ar, ATH10K_DBG_MGMT,
+ "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n",
+ i,
+- __le32_to_cpu(bcn_info->tim_info.tim_len),
+- __le32_to_cpu(bcn_info->tim_info.tim_mcast),
+- __le32_to_cpu(bcn_info->tim_info.tim_changed),
+- __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending),
+- __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]),
+- __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]),
+- __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]),
+- __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0]));
++ __le32_to_cpu(tim_info->tim_len),
++ __le32_to_cpu(tim_info->tim_mcast),
++ __le32_to_cpu(tim_info->tim_changed),
++ __le32_to_cpu(tim_info->tim_num_ps_pending),
++ __le32_to_cpu(tim_info->tim_bitmap[3]),
++ __le32_to_cpu(tim_info->tim_bitmap[2]),
++ __le32_to_cpu(tim_info->tim_bitmap[1]),
++ __le32_to_cpu(tim_info->tim_bitmap[0]));
+
+ arvif = ath10k_get_arvif(ar, vdev_id);
+ if (arvif == NULL) {
+- ath10k_warn("no vif for vdev_id %d found\n", vdev_id);
++ ath10k_warn(ar, "no vif for vdev_id %d found\n",
++ vdev_id);
+ continue;
+ }
+
+@@ -1412,57 +2481,77 @@ static void ath10k_wmi_event_host_swba(s
+
+ bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
+ if (!bcn) {
+- ath10k_warn("could not get mac80211 beacon\n");
++ ath10k_warn(ar, "could not get mac80211 beacon\n");
+ continue;
+ }
+
+- ath10k_tx_h_seq_no(bcn);
+- ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
+- ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
++ ath10k_tx_h_seq_no(arvif->vif, bcn);
++ ath10k_wmi_update_tim(ar, arvif, bcn, tim_info);
++ ath10k_wmi_update_noa(ar, arvif, bcn, noa_info);
+
+ spin_lock_bh(&ar->data_lock);
+
+ if (arvif->beacon) {
+- if (!arvif->beacon_sent)
+- ath10k_warn("SWBA overrun on vdev %d\n",
++ switch (arvif->beacon_state) {
++ case ATH10K_BEACON_SENT:
++ break;
++ case ATH10K_BEACON_SCHEDULED:
++ ath10k_warn(ar, "SWBA overrun on vdev %d, skipped old beacon\n",
+ arvif->vdev_id);
++ break;
++ case ATH10K_BEACON_SENDING:
++ ath10k_warn(ar, "SWBA overrun on vdev %d, skipped new beacon\n",
++ arvif->vdev_id);
++ dev_kfree_skb(bcn);
++ goto skip;
++ }
+
+- dma_unmap_single(arvif->ar->dev,
+- ATH10K_SKB_CB(arvif->beacon)->paddr,
+- arvif->beacon->len, DMA_TO_DEVICE);
+- dev_kfree_skb_any(arvif->beacon);
+- arvif->beacon = NULL;
++ ath10k_mac_vif_beacon_free(arvif);
+ }
+
+- ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
+- bcn->data, bcn->len,
+- DMA_TO_DEVICE);
+- ret = dma_mapping_error(arvif->ar->dev,
+- ATH10K_SKB_CB(bcn)->paddr);
+- if (ret) {
+- ath10k_warn("failed to map beacon: %d\n", ret);
+- dev_kfree_skb_any(bcn);
+- goto skip;
++ if (!arvif->beacon_buf) {
++ paddr = dma_map_single(arvif->ar->dev, bcn->data,
++ bcn->len, DMA_TO_DEVICE);
++ ret = dma_mapping_error(arvif->ar->dev, paddr);
++ if (ret) {
++ ath10k_warn(ar, "failed to map beacon: %d\n",
++ ret);
++ dev_kfree_skb_any(bcn);
++ goto skip;
++ }
++
++ ATH10K_SKB_CB(bcn)->paddr = paddr;
++ } else {
++ if (bcn->len > IEEE80211_MAX_FRAME_LEN) {
++ ath10k_warn(ar, "trimming beacon %d -> %d bytes!\n",
++ bcn->len, IEEE80211_MAX_FRAME_LEN);
++ skb_trim(bcn, IEEE80211_MAX_FRAME_LEN);
++ }
++ memcpy(arvif->beacon_buf, bcn->data, bcn->len);
++ ATH10K_SKB_CB(bcn)->paddr = arvif->beacon_paddr;
+ }
+
+ arvif->beacon = bcn;
+- arvif->beacon_sent = false;
++ arvif->beacon_state = ATH10K_BEACON_SCHEDULED;
++
++ trace_ath10k_tx_hdr(ar, bcn->data, bcn->len);
++ trace_ath10k_tx_payload(ar, bcn->data, bcn->len);
+
+- ath10k_wmi_tx_beacon_nowait(arvif);
+ skip:
+ spin_unlock_bh(&ar->data_lock);
+ }
++
++ ath10k_wmi_tx_beacons_nowait(ar);
+ }
+
+-static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
+ }
+
+ static void ath10k_dfs_radar_report(struct ath10k *ar,
+- struct wmi_single_phyerr_rx_event *event,
+- struct phyerr_radar_report *rr,
++ const struct wmi_phyerr *phyerr,
++ const struct phyerr_radar_report *rr,
+ u64 tsf)
+ {
+ u32 reg0, reg1, tsf32l;
+@@ -1473,20 +2562,20 @@ static void ath10k_dfs_radar_report(stru
+ reg0 = __le32_to_cpu(rr->reg0);
+ reg1 = __le32_to_cpu(rr->reg1);
+
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi phyerr radar report chirp %d max_width %d agc_total_gain %d pulse_delta_diff %d\n",
+ MS(reg0, RADAR_REPORT_REG0_PULSE_IS_CHIRP),
+ MS(reg0, RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH),
+ MS(reg0, RADAR_REPORT_REG0_AGC_TOTAL_GAIN),
+ MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_DIFF));
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi phyerr radar report pulse_delta_pean %d pulse_sidx %d fft_valid %d agc_mb_gain %d subchan_mask %d\n",
+ MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_PEAK),
+ MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK));
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi phyerr radar report pulse_tsf_offset 0x%X pulse_dur: %d\n",
+ MS(reg1, RADAR_REPORT_REG1_PULSE_TSF_OFFSET),
+ MS(reg1, RADAR_REPORT_REG1_PULSE_DUR));
+@@ -1495,12 +2584,12 @@ static void ath10k_dfs_radar_report(stru
+ return;
+
+ /* report event to DFS pattern detector */
+- tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp);
++ tsf32l = __le32_to_cpu(phyerr->tsf_timestamp);
+ tsf64 = tsf & (~0xFFFFFFFFULL);
+ tsf64 |= tsf32l;
+
+ width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR);
+- rssi = event->hdr.rssi_combined;
++ rssi = phyerr->rssi_combined;
+
+ /* hardware store this as 8 bit signed value,
+ * set to zero if negative number
+@@ -1513,25 +2602,25 @@ static void ath10k_dfs_radar_report(stru
+ pe.width = width;
+ pe.rssi = rssi;
+
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "dfs add pulse freq: %d, width: %d, rssi %d, tsf: %llX\n",
+ pe.freq, pe.width, pe.rssi, pe.ts);
+
+ ATH10K_DFS_STAT_INC(ar, pulses_detected);
+
+ if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) {
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "dfs no pulse pattern detected, yet\n");
+ return;
+ }
+
+- ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n");
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs radar detected\n");
+ ATH10K_DFS_STAT_INC(ar, radar_detected);
+
+ /* Control radar events reporting in debugfs file
+ dfs_block_radar_events */
+ if (ar->dfs_block_radar_events) {
+- ath10k_info("DFS Radar detected, but ignored as requested\n");
++ ath10k_info(ar, "DFS Radar detected, but ignored as requested\n");
+ return;
+ }
+
+@@ -1539,8 +2628,8 @@ static void ath10k_dfs_radar_report(stru
+ }
+
+ static int ath10k_dfs_fft_report(struct ath10k *ar,
+- struct wmi_single_phyerr_rx_event *event,
+- struct phyerr_fft_report *fftr,
++ const struct wmi_phyerr *phyerr,
++ const struct phyerr_fft_report *fftr,
+ u64 tsf)
+ {
+ u32 reg0, reg1;
+@@ -1548,15 +2637,15 @@ static int ath10k_dfs_fft_report(struct
+
+ reg0 = __le32_to_cpu(fftr->reg0);
+ reg1 = __le32_to_cpu(fftr->reg1);
+- rssi = event->hdr.rssi_combined;
++ rssi = phyerr->rssi_combined;
+
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n",
+ MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB),
+ MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB),
+ MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX),
+ MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX));
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi phyerr fft report rel_pwr_db %d avgpwr_db %d peak_mag %d num_store_bin %d\n",
+ MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB),
+ MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB),
+@@ -1568,7 +2657,7 @@ static int ath10k_dfs_fft_report(struct
+ /* false event detection */
+ if (rssi == DFS_RSSI_POSSIBLY_FALSE &&
+ peak_mag < 2 * DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE) {
+- ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs false pulse detected\n");
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs false pulse detected\n");
+ ATH10K_DFS_STAT_INC(ar, pulses_discarded);
+ return -EINVAL;
+ }
+@@ -1576,21 +2665,21 @@ static int ath10k_dfs_fft_report(struct
+ return 0;
+ }
+
+-static void ath10k_wmi_event_dfs(struct ath10k *ar,
+- struct wmi_single_phyerr_rx_event *event,
+- u64 tsf)
++void ath10k_wmi_event_dfs(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr,
++ u64 tsf)
+ {
+ int buf_len, tlv_len, res, i = 0;
+- struct phyerr_tlv *tlv;
+- struct phyerr_radar_report *rr;
+- struct phyerr_fft_report *fftr;
+- u8 *tlv_buf;
++ const struct phyerr_tlv *tlv;
++ const struct phyerr_radar_report *rr;
++ const struct phyerr_fft_report *fftr;
++ const u8 *tlv_buf;
+
+- buf_len = __le32_to_cpu(event->hdr.buf_len);
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ buf_len = __le32_to_cpu(phyerr->buf_len);
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n",
+- event->hdr.phy_err_code, event->hdr.rssi_combined,
+- __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len);
++ phyerr->phy_err_code, phyerr->rssi_combined,
++ __le32_to_cpu(phyerr->tsf_timestamp), tsf, buf_len);
+
+ /* Skip event if DFS disabled */
+ if (!config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED))
+@@ -1600,36 +2689,38 @@ static void ath10k_wmi_event_dfs(struct
+
+ while (i < buf_len) {
+ if (i + sizeof(*tlv) > buf_len) {
+- ath10k_warn("too short buf for tlv header (%d)\n", i);
++ ath10k_warn(ar, "too short buf for tlv header (%d)\n",
++ i);
+ return;
+ }
+
+- tlv = (struct phyerr_tlv *)&event->bufp[i];
++ tlv = (struct phyerr_tlv *)&phyerr->buf[i];
+ tlv_len = __le16_to_cpu(tlv->len);
+- tlv_buf = &event->bufp[i + sizeof(*tlv)];
+- ath10k_dbg(ATH10K_DBG_REGULATORY,
++ tlv_buf = &phyerr->buf[i + sizeof(*tlv)];
++ ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
+ "wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n",
+ tlv_len, tlv->tag, tlv->sig);
+
+ switch (tlv->tag) {
+ case PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY:
+ if (i + sizeof(*tlv) + sizeof(*rr) > buf_len) {
+- ath10k_warn("too short radar pulse summary (%d)\n",
++ ath10k_warn(ar, "too short radar pulse summary (%d)\n",
+ i);
+ return;
+ }
+
+ rr = (struct phyerr_radar_report *)tlv_buf;
+- ath10k_dfs_radar_report(ar, event, rr, tsf);
++ ath10k_dfs_radar_report(ar, phyerr, rr, tsf);
+ break;
+ case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
+ if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) {
+- ath10k_warn("too short fft report (%d)\n", i);
++ ath10k_warn(ar, "too short fft report (%d)\n",
++ i);
+ return;
+ }
+
+ fftr = (struct phyerr_fft_report *)tlv_buf;
+- res = ath10k_dfs_fft_report(ar, event, fftr, tsf);
++ res = ath10k_dfs_fft_report(ar, phyerr, fftr, tsf);
+ if (res)
+ return;
+ break;
+@@ -1639,58 +2730,122 @@ static void ath10k_wmi_event_dfs(struct
+ }
+ }
+
+-static void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
+- struct wmi_single_phyerr_rx_event *event,
+- u64 tsf)
+-{
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi event spectral scan\n");
+-}
+-
+-static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
++void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr,
++ u64 tsf)
+ {
+- struct wmi_comb_phyerr_rx_event *comb_event;
+- struct wmi_single_phyerr_rx_event *event;
+- u32 count, i, buf_len, phy_err_code;
+- u64 tsf;
+- int left_len = skb->len;
+-
+- ATH10K_DFS_STAT_INC(ar, phy_errors);
+-
+- /* Check if combined event available */
+- if (left_len < sizeof(*comb_event)) {
+- ath10k_warn("wmi phyerr combined event wrong len\n");
+- return;
+- }
+-
+- left_len -= sizeof(*comb_event);
++ int buf_len, tlv_len, res, i = 0;
++ struct phyerr_tlv *tlv;
++ const void *tlv_buf;
++ const struct phyerr_fft_report *fftr;
++ size_t fftr_len;
+
+- /* Check number of included events */
+- comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data;
+- count = __le32_to_cpu(comb_event->hdr.num_phyerr_events);
++ buf_len = __le32_to_cpu(phyerr->buf_len);
+
+- tsf = __le32_to_cpu(comb_event->hdr.tsf_u32);
+- tsf <<= 32;
+- tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32);
++ while (i < buf_len) {
++ if (i + sizeof(*tlv) > buf_len) {
++ ath10k_warn(ar, "failed to parse phyerr tlv header at byte %d\n",
++ i);
++ return;
++ }
+
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi event phyerr count %d tsf64 0x%llX\n",
+- count, tsf);
++ tlv = (struct phyerr_tlv *)&phyerr->buf[i];
++ tlv_len = __le16_to_cpu(tlv->len);
++ tlv_buf = &phyerr->buf[i + sizeof(*tlv)];
+
+- event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp;
+- for (i = 0; i < count; i++) {
+- /* Check if we can read event header */
+- if (left_len < sizeof(*event)) {
+- ath10k_warn("single event (%d) wrong head len\n", i);
++ if (i + sizeof(*tlv) + tlv_len > buf_len) {
++ ath10k_warn(ar, "failed to parse phyerr tlv payload at byte %d\n",
++ i);
+ return;
+ }
+
+- left_len -= sizeof(*event);
+-
+- buf_len = __le32_to_cpu(event->hdr.buf_len);
+- phy_err_code = event->hdr.phy_err_code;
++ switch (tlv->tag) {
++ case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
++ if (sizeof(*fftr) > tlv_len) {
++ ath10k_warn(ar, "failed to parse fft report at byte %d\n",
++ i);
++ return;
++ }
++
++ fftr_len = tlv_len - sizeof(*fftr);
++ fftr = tlv_buf;
++ res = ath10k_spectral_process_fft(ar, phyerr,
++ fftr, fftr_len,
++ tsf);
++ if (res < 0) {
++ ath10k_warn(ar, "failed to process fft report: %d\n",
++ res);
++ return;
++ }
++ break;
++ }
++
++ i += sizeof(*tlv) + tlv_len;
++ }
++}
++
++static int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_phyerr_ev_arg *arg)
++{
++ struct wmi_phyerr_event *ev = (void *)skb->data;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ arg->num_phyerrs = ev->num_phyerrs;
++ arg->tsf_l32 = ev->tsf_l32;
++ arg->tsf_u32 = ev->tsf_u32;
++ arg->buf_len = __cpu_to_le32(skb->len - sizeof(*ev));
++ arg->phyerrs = ev->phyerrs;
++
++ return 0;
++}
++
++void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_phyerr_ev_arg arg = {};
++ const struct wmi_phyerr *phyerr;
++ u32 count, i, buf_len, phy_err_code;
++ u64 tsf;
++ int left_len, ret;
++
++ ATH10K_DFS_STAT_INC(ar, phy_errors);
++
++ ret = ath10k_wmi_pull_phyerr(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse phyerr event: %d\n", ret);
++ return;
++ }
++
++ left_len = __le32_to_cpu(arg.buf_len);
++
++ /* Check number of included events */
++ count = __le32_to_cpu(arg.num_phyerrs);
++
++ tsf = __le32_to_cpu(arg.tsf_u32);
++ tsf <<= 32;
++ tsf |= __le32_to_cpu(arg.tsf_l32);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi event phyerr count %d tsf64 0x%llX\n",
++ count, tsf);
++
++ phyerr = arg.phyerrs;
++ for (i = 0; i < count; i++) {
++ /* Check if we can read event header */
++ if (left_len < sizeof(*phyerr)) {
++ ath10k_warn(ar, "single event (%d) wrong head len\n",
++ i);
++ return;
++ }
++
++ left_len -= sizeof(*phyerr);
++
++ buf_len = __le32_to_cpu(phyerr->buf_len);
++ phy_err_code = phyerr->phy_err_code;
+
+ if (left_len < buf_len) {
+- ath10k_warn("single event (%d) wrong buf len\n", i);
++ ath10k_warn(ar, "single event (%d) wrong buf len\n", i);
+ return;
+ }
+
+@@ -1698,36 +2853,34 @@ static void ath10k_wmi_event_phyerr(stru
+
+ switch (phy_err_code) {
+ case PHY_ERROR_RADAR:
+- ath10k_wmi_event_dfs(ar, event, tsf);
++ ath10k_wmi_event_dfs(ar, phyerr, tsf);
+ break;
+ case PHY_ERROR_SPECTRAL_SCAN:
+- ath10k_wmi_event_spectral_scan(ar, event, tsf);
++ ath10k_wmi_event_spectral_scan(ar, phyerr, tsf);
+ break;
+ case PHY_ERROR_FALSE_RADAR_EXT:
+- ath10k_wmi_event_dfs(ar, event, tsf);
+- ath10k_wmi_event_spectral_scan(ar, event, tsf);
++ ath10k_wmi_event_dfs(ar, phyerr, tsf);
++ ath10k_wmi_event_spectral_scan(ar, phyerr, tsf);
+ break;
+ default:
+ break;
+ }
+
+- event += sizeof(*event) + buf_len;
++ phyerr = (void *)phyerr + sizeof(*phyerr) + buf_len;
+ }
+ }
+
+-static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
++void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_profile_match(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
+ }
+
+-static void ath10k_wmi_event_debug_print(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb)
+ {
+ char buf[101], c;
+ int i;
+@@ -1748,7 +2901,7 @@ static void ath10k_wmi_event_debug_print
+ }
+
+ if (i == sizeof(buf) - 1)
+- ath10k_warn("wmi debug print truncated: %d\n", skb->len);
++ ath10k_warn(ar, "wmi debug print truncated: %d\n", skb->len);
+
+ /* for some reason the debug prints end with \n, remove that */
+ if (skb->data[i - 1] == '\n')
+@@ -1757,112 +2910,99 @@ static void ath10k_wmi_event_debug_print
+ /* the last byte is always reserved for the null character */
+ buf[i] = '\0';
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
++ ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
+ }
+
+-static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
++void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
++void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
+ struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
++void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
+ struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_dcs_interference(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_delba_complete(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_addba_complete(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
++void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
+ struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
+ }
+
+-static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar,
+- struct sk_buff *skb)
++void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb)
+ {
+- ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
+ }
+
+ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+- u32 num_units, u32 unit_len)
++ u32 num_units, u32 unit_len)
+ {
+ dma_addr_t paddr;
+ u32 pool_size;
+@@ -1878,7 +3018,7 @@ static int ath10k_wmi_alloc_host_mem(str
+ &paddr,
+ GFP_ATOMIC);
+ if (!ar->wmi.mem_chunks[idx].vaddr) {
+- ath10k_warn("failed to allocate memory chunk\n");
++ ath10k_warn(ar, "failed to allocate memory chunk\n");
+ return -ENOMEM;
+ }
+
+@@ -1892,45 +3032,124 @@ static int ath10k_wmi_alloc_host_mem(str
+ return 0;
+ }
+
+-static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
+- struct sk_buff *skb)
++static int
++ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_svc_rdy_ev_arg *arg)
++{
++ struct wmi_service_ready_event *ev;
++ size_t i, n;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ ev = (void *)skb->data;
++ skb_pull(skb, sizeof(*ev));
++ arg->min_tx_power = ev->hw_min_tx_power;
++ arg->max_tx_power = ev->hw_max_tx_power;
++ arg->ht_cap = ev->ht_cap_info;
++ arg->vht_cap = ev->vht_cap_info;
++ arg->sw_ver0 = ev->sw_version;
++ arg->sw_ver1 = ev->sw_version_1;
++ arg->phy_capab = ev->phy_capability;
++ arg->num_rf_chains = ev->num_rf_chains;
++ arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
++ arg->num_mem_reqs = ev->num_mem_reqs;
++ arg->service_map = ev->wmi_service_bitmap;
++ arg->service_map_len = sizeof(ev->wmi_service_bitmap);
++
++ n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
++ ARRAY_SIZE(arg->mem_reqs));
++ for (i = 0; i < n; i++)
++ arg->mem_reqs[i] = &ev->mem_reqs[i];
++
++ if (skb->len <
++ __le32_to_cpu(arg->num_mem_reqs) * sizeof(arg->mem_reqs[0]))
++ return -EPROTO;
++
++ return 0;
++}
++
++static int
++ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_svc_rdy_ev_arg *arg)
++{
++ struct wmi_10x_service_ready_event *ev;
++ int i, n;
++
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ ev = (void *)skb->data;
++ skb_pull(skb, sizeof(*ev));
++ arg->min_tx_power = ev->hw_min_tx_power;
++ arg->max_tx_power = ev->hw_max_tx_power;
++ arg->ht_cap = ev->ht_cap_info;
++ arg->vht_cap = ev->vht_cap_info;
++ arg->sw_ver0 = ev->sw_version;
++ arg->phy_capab = ev->phy_capability;
++ arg->num_rf_chains = ev->num_rf_chains;
++ arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
++ arg->num_mem_reqs = ev->num_mem_reqs;
++ arg->service_map = ev->wmi_service_bitmap;
++ arg->service_map_len = sizeof(ev->wmi_service_bitmap);
++
++ n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
++ ARRAY_SIZE(arg->mem_reqs));
++ for (i = 0; i < n; i++)
++ arg->mem_reqs[i] = &ev->mem_reqs[i];
++
++ if (skb->len <
++ __le32_to_cpu(arg->num_mem_reqs) * sizeof(arg->mem_reqs[0]))
++ return -EPROTO;
++
++ return 0;
++}
++
++void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb)
+ {
+- struct wmi_service_ready_event *ev = (void *)skb->data;
++ struct wmi_svc_rdy_ev_arg arg = {};
++ u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
++ int ret;
+
+- if (skb->len < sizeof(*ev)) {
+- ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+- skb->len, sizeof(*ev));
++ ret = ath10k_wmi_pull_svc_rdy(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse service ready: %d\n", ret);
+ return;
+ }
+
+- ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+- ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+- ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+- ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
++ memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
++ ath10k_wmi_map_svc(ar, arg.service_map, ar->wmi.svc_map,
++ arg.service_map_len);
++
++ ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power);
++ ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power);
++ ar->ht_cap_info = __le32_to_cpu(arg.ht_cap);
++ ar->vht_cap_info = __le32_to_cpu(arg.vht_cap);
+ ar->fw_version_major =
+- (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+- ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
++ (__le32_to_cpu(arg.sw_ver0) & 0xff000000) >> 24;
++ ar->fw_version_minor = (__le32_to_cpu(arg.sw_ver0) & 0x00ffffff);
+ ar->fw_version_release =
+- (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
+- ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
+- ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+- ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
++ (__le32_to_cpu(arg.sw_ver1) & 0xffff0000) >> 16;
++ ar->fw_version_build = (__le32_to_cpu(arg.sw_ver1) & 0x0000ffff);
++ ar->phy_capability = __le32_to_cpu(arg.phy_capab);
++ ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains);
++ ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd);
++
++ ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
++ arg.service_map, arg.service_map_len);
+
+ /* only manually set fw features when not using FW IE format */
+ if (ar->fw_api == 1 && ar->fw_version_build > 636)
+ set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features);
+
+ if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+- ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
++ ath10k_warn(ar, "hardware advertises support for more spatial streams than it should (%d > %d)\n",
+ ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+ ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+ }
+
+- ar->ath_common.regulatory.current_rd =
+- __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+-
+- ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+- sizeof(ev->wmi_service_bitmap));
++ ar->supp_tx_chainmask = (1 << ar->num_rf_chains) - 1;
++ ar->supp_rx_chainmask = (1 << ar->num_rf_chains) - 1;
+
+ if (strlen(ar->hw->wiphy->fw_version) == 0) {
+ snprintf(ar->hw->wiphy->fw_version,
+@@ -1942,90 +3161,18 @@ static void ath10k_wmi_service_ready_eve
+ ar->fw_version_build);
+ }
+
+- /* FIXME: it probably should be better to support this */
+- if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
+- ath10k_warn("target requested %d memory chunks; ignoring\n",
+- __le32_to_cpu(ev->num_mem_reqs));
+- }
+-
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
+- __le32_to_cpu(ev->sw_version),
+- __le32_to_cpu(ev->sw_version_1),
+- __le32_to_cpu(ev->abi_version),
+- __le32_to_cpu(ev->phy_capability),
+- __le32_to_cpu(ev->ht_cap_info),
+- __le32_to_cpu(ev->vht_cap_info),
+- __le32_to_cpu(ev->vht_supp_mcs),
+- __le32_to_cpu(ev->sys_cap_info),
+- __le32_to_cpu(ev->num_mem_reqs),
+- __le32_to_cpu(ev->num_rf_chains));
+-
+- complete(&ar->wmi.service_ready);
+-}
+-
+-static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
+- struct sk_buff *skb)
+-{
+- u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
+- int ret;
+- struct wmi_service_ready_event_10x *ev = (void *)skb->data;
+-
+- if (skb->len < sizeof(*ev)) {
+- ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+- skb->len, sizeof(*ev));
+- return;
+- }
+-
+- ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+- ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+- ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+- ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+- ar->fw_version_major =
+- (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+- ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+- ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+- ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+-
+- if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
+- ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n",
+- ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
+- ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
+- }
+-
+- ar->ath_common.regulatory.current_rd =
+- __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+-
+- ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+- sizeof(ev->wmi_service_bitmap));
+-
+- if (strlen(ar->hw->wiphy->fw_version) == 0) {
+- snprintf(ar->hw->wiphy->fw_version,
+- sizeof(ar->hw->wiphy->fw_version),
+- "%u.%u",
+- ar->fw_version_major,
+- ar->fw_version_minor);
+- }
+-
+- num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs);
+-
+- if (num_mem_reqs > ATH10K_MAX_MEM_REQS) {
+- ath10k_warn("requested memory chunks number (%d) exceeds the limit\n",
++ num_mem_reqs = __le32_to_cpu(arg.num_mem_reqs);
++ if (num_mem_reqs > WMI_MAX_MEM_REQS) {
++ ath10k_warn(ar, "requested memory chunks number (%d) exceeds the limit\n",
+ num_mem_reqs);
+ return;
+ }
+
+- if (!num_mem_reqs)
+- goto exit;
+-
+- ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n",
+- num_mem_reqs);
+-
+ for (i = 0; i < num_mem_reqs; ++i) {
+- req_id = __le32_to_cpu(ev->mem_reqs[i].req_id);
+- num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
+- unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size);
+- num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info);
++ req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id);
++ num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units);
++ unit_size = __le32_to_cpu(arg.mem_reqs[i]->unit_size);
++ num_unit_info = __le32_to_cpu(arg.mem_reqs[i]->num_unit_info);
+
+ if (num_unit_info & NUM_UNITS_IS_NUM_PEERS)
+ /* number of units to allocate is number of
+@@ -2036,10 +3183,10 @@ static void ath10k_wmi_10x_service_ready
+ else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS)
+ num_units = TARGET_10X_NUM_VDEVS + 1;
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n",
+ req_id,
+- __le32_to_cpu(ev->mem_reqs[i].num_units),
++ __le32_to_cpu(arg.mem_reqs[i]->num_units),
+ num_unit_info,
+ unit_size,
+ num_units);
+@@ -2050,47 +3197,79 @@ static void ath10k_wmi_10x_service_ready
+ return;
+ }
+
+-exit:
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
+- __le32_to_cpu(ev->sw_version),
+- __le32_to_cpu(ev->abi_version),
+- __le32_to_cpu(ev->phy_capability),
+- __le32_to_cpu(ev->ht_cap_info),
+- __le32_to_cpu(ev->vht_cap_info),
+- __le32_to_cpu(ev->vht_supp_mcs),
+- __le32_to_cpu(ev->sys_cap_info),
+- __le32_to_cpu(ev->num_mem_reqs),
+- __le32_to_cpu(ev->num_rf_chains));
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
++ __le32_to_cpu(arg.min_tx_power),
++ __le32_to_cpu(arg.max_tx_power),
++ __le32_to_cpu(arg.ht_cap),
++ __le32_to_cpu(arg.vht_cap),
++ __le32_to_cpu(arg.sw_ver0),
++ __le32_to_cpu(arg.sw_ver1),
++ __le32_to_cpu(arg.fw_build),
++ __le32_to_cpu(arg.phy_capab),
++ __le32_to_cpu(arg.num_rf_chains),
++ __le32_to_cpu(arg.eeprom_rd),
++ __le32_to_cpu(arg.num_mem_reqs));
+
+ complete(&ar->wmi.service_ready);
+ }
+
+-static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
++static int ath10k_wmi_op_pull_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_rdy_ev_arg *arg)
+ {
+- struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
++ struct wmi_ready_event *ev = (void *)skb->data;
+
+- if (WARN_ON(skb->len < sizeof(*ev)))
+- return -EINVAL;
++ if (skb->len < sizeof(*ev))
++ return -EPROTO;
++
++ skb_pull(skb, sizeof(*ev));
++ arg->sw_version = ev->sw_version;
++ arg->abi_version = ev->abi_version;
++ arg->status = ev->status;
++ arg->mac_addr = ev->mac_addr.addr;
++
++ return 0;
++}
++
++int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_rdy_ev_arg arg = {};
++ int ret;
+
+- memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
++ ret = ath10k_wmi_pull_rdy(ar, skb, &arg);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse ready event: %d\n", ret);
++ return ret;
++ }
+
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n",
+- __le32_to_cpu(ev->sw_version),
+- __le32_to_cpu(ev->abi_version),
+- ev->mac_addr.addr,
+- __le32_to_cpu(ev->status), skb->len, sizeof(*ev));
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
++ __le32_to_cpu(arg.sw_version),
++ __le32_to_cpu(arg.abi_version),
++ arg.mac_addr,
++ __le32_to_cpu(arg.status));
+
++ ether_addr_copy(ar->mac_addr, arg.mac_addr);
+ complete(&ar->wmi.unified_ready);
+ return 0;
+ }
+
+-static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
++static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb)
++{
++ const struct wmi_pdev_temperature_event *ev;
++
++ ev = (struct wmi_pdev_temperature_event *)skb->data;
++ if (WARN_ON(skb->len < sizeof(*ev)))
++ return -EPROTO;
++
++ ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature));
++ return 0;
++}
++
++static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb)
+ {
+ struct wmi_cmd_hdr *cmd_hdr;
+ enum wmi_event_id id;
+- u16 len;
+
+ cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+ id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+@@ -2098,9 +3277,7 @@ static void ath10k_wmi_main_process_rx(s
+ if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+ return;
+
+- len = skb->len;
+-
+- trace_ath10k_wmi_event(id, skb->data, skb->len);
++ trace_ath10k_wmi_event(ar, id, skb->data, skb->len);
+
+ switch (id) {
+ case WMI_MGMT_RX_EVENTID:
+@@ -2192,24 +3369,24 @@ static void ath10k_wmi_main_process_rx(s
+ ath10k_wmi_event_vdev_install_key_complete(ar, skb);
+ break;
+ case WMI_SERVICE_READY_EVENTID:
+- ath10k_wmi_service_ready_event_rx(ar, skb);
++ ath10k_wmi_event_service_ready(ar, skb);
+ break;
+ case WMI_READY_EVENTID:
+- ath10k_wmi_ready_event_rx(ar, skb);
++ ath10k_wmi_event_ready(ar, skb);
+ break;
+ default:
+- ath10k_warn("Unknown eventid: %d\n", id);
++ ath10k_warn(ar, "Unknown eventid: %d\n", id);
+ break;
+ }
+
+ dev_kfree_skb(skb);
+ }
+
+-static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
++static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb)
+ {
+ struct wmi_cmd_hdr *cmd_hdr;
+ enum wmi_10x_event_id id;
+- u16 len;
++ bool consumed;
+
+ cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+ id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+@@ -2217,9 +3394,19 @@ static void ath10k_wmi_10x_process_rx(st
+ if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+ return;
+
+- len = skb->len;
++ trace_ath10k_wmi_event(ar, id, skb->data, skb->len);
++
++ consumed = ath10k_tm_event_wmi(ar, id, skb);
+
+- trace_ath10k_wmi_event(id, skb->data, skb->len);
++ /* Ready event must be handled normally also in UTF mode so that we
++ * know the UTF firmware has booted, others we are just bypass WMI
++ * events to testmode.
++ */
++ if (consumed && id != WMI_10X_READY_EVENTID) {
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi testmode consumed 0x%x\n", id);
++ goto out;
++ }
+
+ switch (id) {
+ case WMI_10X_MGMT_RX_EVENTID:
+@@ -2302,64 +3489,153 @@ static void ath10k_wmi_10x_process_rx(st
+ ath10k_wmi_event_vdev_resume_req(ar, skb);
+ break;
+ case WMI_10X_SERVICE_READY_EVENTID:
+- ath10k_wmi_10x_service_ready_event_rx(ar, skb);
++ ath10k_wmi_event_service_ready(ar, skb);
+ break;
+ case WMI_10X_READY_EVENTID:
+- ath10k_wmi_ready_event_rx(ar, skb);
++ ath10k_wmi_event_ready(ar, skb);
++ break;
++ case WMI_10X_PDEV_UTF_EVENTID:
++ /* ignore utf events */
+ break;
+ default:
+- ath10k_warn("Unknown eventid: %d\n", id);
++ ath10k_warn(ar, "Unknown eventid: %d\n", id);
+ break;
+ }
+
++out:
+ dev_kfree_skb(skb);
+ }
+
+-
+-static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
++static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb)
+ {
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+- ath10k_wmi_10x_process_rx(ar, skb);
+- else
+- ath10k_wmi_main_process_rx(ar, skb);
+-}
++ struct wmi_cmd_hdr *cmd_hdr;
++ enum wmi_10_2_event_id id;
+
+-/* WMI Initialization functions */
+-int ath10k_wmi_attach(struct ath10k *ar)
+-{
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+- ar->wmi.cmd = &wmi_10x_cmd_map;
+- ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+- ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+- } else {
+- ar->wmi.cmd = &wmi_cmd_map;
+- ar->wmi.vdev_param = &wmi_vdev_param_map;
+- ar->wmi.pdev_param = &wmi_pdev_param_map;
+- }
++ cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
++ id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+- init_completion(&ar->wmi.service_ready);
+- init_completion(&ar->wmi.unified_ready);
+- init_waitqueue_head(&ar->wmi.tx_credits_wq);
++ if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
++ return;
+
+- return 0;
++ trace_ath10k_wmi_event(ar, id, skb->data, skb->len);
++
++ switch (id) {
++ case WMI_10_2_MGMT_RX_EVENTID:
++ ath10k_wmi_event_mgmt_rx(ar, skb);
++ /* mgmt_rx() owns the skb now! */
++ return;
++ case WMI_10_2_SCAN_EVENTID:
++ ath10k_wmi_event_scan(ar, skb);
++ break;
++ case WMI_10_2_CHAN_INFO_EVENTID:
++ ath10k_wmi_event_chan_info(ar, skb);
++ break;
++ case WMI_10_2_ECHO_EVENTID:
++ ath10k_wmi_event_echo(ar, skb);
++ break;
++ case WMI_10_2_DEBUG_MESG_EVENTID:
++ ath10k_wmi_event_debug_mesg(ar, skb);
++ break;
++ case WMI_10_2_UPDATE_STATS_EVENTID:
++ ath10k_wmi_event_update_stats(ar, skb);
++ break;
++ case WMI_10_2_VDEV_START_RESP_EVENTID:
++ ath10k_wmi_event_vdev_start_resp(ar, skb);
++ break;
++ case WMI_10_2_VDEV_STOPPED_EVENTID:
++ ath10k_wmi_event_vdev_stopped(ar, skb);
++ break;
++ case WMI_10_2_PEER_STA_KICKOUT_EVENTID:
++ ath10k_wmi_event_peer_sta_kickout(ar, skb);
++ break;
++ case WMI_10_2_HOST_SWBA_EVENTID:
++ ath10k_wmi_event_host_swba(ar, skb);
++ break;
++ case WMI_10_2_TBTTOFFSET_UPDATE_EVENTID:
++ ath10k_wmi_event_tbttoffset_update(ar, skb);
++ break;
++ case WMI_10_2_PHYERR_EVENTID:
++ ath10k_wmi_event_phyerr(ar, skb);
++ break;
++ case WMI_10_2_ROAM_EVENTID:
++ ath10k_wmi_event_roam(ar, skb);
++ break;
++ case WMI_10_2_PROFILE_MATCH:
++ ath10k_wmi_event_profile_match(ar, skb);
++ break;
++ case WMI_10_2_DEBUG_PRINT_EVENTID:
++ ath10k_wmi_event_debug_print(ar, skb);
++ break;
++ case WMI_10_2_PDEV_QVIT_EVENTID:
++ ath10k_wmi_event_pdev_qvit(ar, skb);
++ break;
++ case WMI_10_2_WLAN_PROFILE_DATA_EVENTID:
++ ath10k_wmi_event_wlan_profile_data(ar, skb);
++ break;
++ case WMI_10_2_RTT_MEASUREMENT_REPORT_EVENTID:
++ ath10k_wmi_event_rtt_measurement_report(ar, skb);
++ break;
++ case WMI_10_2_TSF_MEASUREMENT_REPORT_EVENTID:
++ ath10k_wmi_event_tsf_measurement_report(ar, skb);
++ break;
++ case WMI_10_2_RTT_ERROR_REPORT_EVENTID:
++ ath10k_wmi_event_rtt_error_report(ar, skb);
++ break;
++ case WMI_10_2_WOW_WAKEUP_HOST_EVENTID:
++ ath10k_wmi_event_wow_wakeup_host(ar, skb);
++ break;
++ case WMI_10_2_DCS_INTERFERENCE_EVENTID:
++ ath10k_wmi_event_dcs_interference(ar, skb);
++ break;
++ case WMI_10_2_PDEV_TPC_CONFIG_EVENTID:
++ ath10k_wmi_event_pdev_tpc_config(ar, skb);
++ break;
++ case WMI_10_2_INST_RSSI_STATS_EVENTID:
++ ath10k_wmi_event_inst_rssi_stats(ar, skb);
++ break;
++ case WMI_10_2_VDEV_STANDBY_REQ_EVENTID:
++ ath10k_wmi_event_vdev_standby_req(ar, skb);
++ break;
++ case WMI_10_2_VDEV_RESUME_REQ_EVENTID:
++ ath10k_wmi_event_vdev_resume_req(ar, skb);
++ break;
++ case WMI_10_2_SERVICE_READY_EVENTID:
++ ath10k_wmi_event_service_ready(ar, skb);
++ break;
++ case WMI_10_2_READY_EVENTID:
++ ath10k_wmi_event_ready(ar, skb);
++ break;
++ case WMI_10_2_PDEV_TEMPERATURE_EVENTID:
++ ath10k_wmi_event_temperature(ar, skb);
++ break;
++ case WMI_10_2_RTT_KEEPALIVE_EVENTID:
++ case WMI_10_2_GPIO_INPUT_EVENTID:
++ case WMI_10_2_PEER_RATECODE_LIST_EVENTID:
++ case WMI_10_2_GENERIC_BUFFER_EVENTID:
++ case WMI_10_2_MCAST_BUF_RELEASE_EVENTID:
++ case WMI_10_2_MCAST_LIST_AGEOUT_EVENTID:
++ case WMI_10_2_WDS_PEER_EVENTID:
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "received event id %d not implemented\n", id);
++ break;
++ default:
++ ath10k_warn(ar, "Unknown eventid: %d\n", id);
++ break;
++ }
++
++ dev_kfree_skb(skb);
+ }
+
+-void ath10k_wmi_detach(struct ath10k *ar)
++static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+ {
+- int i;
+-
+- /* free the host memory chunks requested by firmware */
+- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+- dma_free_coherent(ar->dev,
+- ar->wmi.mem_chunks[i].len,
+- ar->wmi.mem_chunks[i].vaddr,
+- ar->wmi.mem_chunks[i].paddr);
+- }
++ int ret;
+
+- ar->wmi.num_mem_chunks = 0;
++ ret = ath10k_wmi_rx(ar, skb);
++ if (ret)
++ ath10k_warn(ar, "failed to process wmi rx: %d\n", ret);
+ }
+
+-int ath10k_wmi_connect_htc_service(struct ath10k *ar)
++int ath10k_wmi_connect(struct ath10k *ar)
+ {
+ int status;
+ struct ath10k_htc_svc_conn_req conn_req;
+@@ -2378,7 +3654,7 @@ int ath10k_wmi_connect_htc_service(struc
+
+ status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
+ if (status) {
+- ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
++ ath10k_warn(ar, "failed to connect to WMI CONTROL service status: %d\n",
+ status);
+ return status;
+ }
+@@ -2387,16 +3663,17 @@ int ath10k_wmi_connect_htc_service(struc
+ return 0;
+ }
+
+-static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd,
+- u16 rd2g, u16 rd5g, u16 ctl2g,
+- u16 ctl5g)
++static struct sk_buff *
++ath10k_wmi_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g,
++ u16 ctl2g, u16 ctl5g,
++ enum wmi_dfs_region dfs_reg)
+ {
+ struct wmi_pdev_set_regdomain_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data;
+ cmd->reg_domain = __cpu_to_le32(rd);
+@@ -2405,25 +3682,23 @@ static int ath10k_wmi_main_pdev_set_regd
+ cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g);
+ cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
+ rd, rd2g, rd5g, ctl2g, ctl5g);
+-
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->pdev_set_regdomain_cmdid);
++ return skb;
+ }
+
+-static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd,
+- u16 rd2g, u16 rd5g,
+- u16 ctl2g, u16 ctl5g,
+- enum wmi_dfs_region dfs_reg)
++static struct sk_buff *
++ath10k_wmi_10x_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16
++ rd5g, u16 ctl2g, u16 ctl5g,
++ enum wmi_dfs_region dfs_reg)
+ {
+ struct wmi_pdev_set_regdomain_cmd_10x *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data;
+ cmd->reg_domain = __cpu_to_le32(rd);
+@@ -2433,121 +3708,96 @@ static int ath10k_wmi_10x_pdev_set_regdo
+ cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g);
+ cmd->dfs_domain = __cpu_to_le32(dfs_reg);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n",
+ rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg);
+-
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->pdev_set_regdomain_cmdid);
+-}
+-
+-int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+- u16 rd5g, u16 ctl2g, u16 ctl5g,
+- enum wmi_dfs_region dfs_reg)
+-{
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+- return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g,
+- ctl2g, ctl5g, dfs_reg);
+- else
+- return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g,
+- ctl2g, ctl5g);
+-}
+-
+-int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+- const struct wmi_channel_arg *arg)
+-{
+- struct wmi_set_channel_cmd *cmd;
+- struct sk_buff *skb;
+- u32 ch_flags = 0;
+-
+- if (arg->passive)
+- return -EINVAL;
+-
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+- if (!skb)
+- return -ENOMEM;
+-
+- if (arg->chan_radar)
+- ch_flags |= WMI_CHAN_FLAG_DFS;
+-
+- cmd = (struct wmi_set_channel_cmd *)skb->data;
+- cmd->chan.mhz = __cpu_to_le32(arg->freq);
+- cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
+- cmd->chan.mode = arg->mode;
+- cmd->chan.flags |= __cpu_to_le32(ch_flags);
+- cmd->chan.min_power = arg->min_power;
+- cmd->chan.max_power = arg->max_power;
+- cmd->chan.reg_power = arg->max_reg_power;
+- cmd->chan.reg_classid = arg->reg_class_id;
+- cmd->chan.antenna_max = arg->max_antenna_gain;
+-
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi set channel mode %d freq %d\n",
+- arg->mode, arg->freq);
+-
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->pdev_set_channel_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
++static struct sk_buff *
++ath10k_wmi_op_gen_pdev_suspend(struct ath10k *ar, u32 suspend_opt)
+ {
+ struct wmi_pdev_suspend_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
+ cmd->suspend_opt = __cpu_to_le32(suspend_opt);
+
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
++static struct sk_buff *
++ath10k_wmi_op_gen_pdev_resume(struct ath10k *ar)
+ {
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(0);
+- if (skb == NULL)
+- return -ENOMEM;
++ skb = ath10k_wmi_alloc_skb(ar, 0);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
+
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
++static struct sk_buff *
++ath10k_wmi_op_gen_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
+ {
+ struct wmi_pdev_set_param_cmd *cmd;
+ struct sk_buff *skb;
+
+ if (id == WMI_PDEV_PARAM_UNSUPPORTED) {
+- ath10k_warn("pdev param %d not supported by firmware\n", id);
+- return -EOPNOTSUPP;
++ ath10k_warn(ar, "pdev param %d not supported by firmware\n",
++ id);
++ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_pdev_set_param_cmd *)skb->data;
+ cmd->param_id = __cpu_to_le32(id);
+ cmd->param_value = __cpu_to_le32(value);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
+ id, value);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
++ return skb;
+ }
+
+-static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
++void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
++ struct wmi_host_mem_chunks *chunks)
+ {
+- struct wmi_init_cmd *cmd;
+- struct sk_buff *buf;
+- struct wmi_resource_config config = {};
+- u32 len, val;
++ struct host_memory_chunk *chunk;
+ int i;
+
+- config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
+- config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
++ chunks->count = __cpu_to_le32(ar->wmi.num_mem_chunks);
++
++ for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
++ chunk = &chunks->items[i];
++ chunk->ptr = __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
++ chunk->size = __cpu_to_le32(ar->wmi.mem_chunks[i].len);
++ chunk->req_id = __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi chunk %d len %d requested, addr 0x%llx\n",
++ i,
++ ar->wmi.mem_chunks[i].len,
++ (unsigned long long)ar->wmi.mem_chunks[i].paddr);
++ }
++}
++
++static struct sk_buff *ath10k_wmi_op_gen_init(struct ath10k *ar)
++{
++ struct wmi_init_cmd *cmd;
++ struct sk_buff *buf;
++ struct wmi_resource_config config = {};
++ u32 len, val;
++
++ config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
++ config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS);
+ config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
+
+ config.num_offload_reorder_bufs =
+@@ -2600,50 +3850,25 @@ static int ath10k_wmi_main_cmd_init(stru
+ len = sizeof(*cmd) +
+ (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+- buf = ath10k_wmi_alloc_skb(len);
++ buf = ath10k_wmi_alloc_skb(ar, len);
+ if (!buf)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_init_cmd *)buf->data;
+
+- if (ar->wmi.num_mem_chunks == 0) {
+- cmd->num_host_mem_chunks = 0;
+- goto out;
+- }
+-
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+- ar->wmi.num_mem_chunks);
+-
+- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
+-
+- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+- cmd->host_mem_chunks[i].ptr =
+- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+- cmd->host_mem_chunks[i].size =
+- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+- cmd->host_mem_chunks[i].req_id =
+- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+-
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi chunk %d len %d requested, addr 0x%llx\n",
+- i,
+- ar->wmi.mem_chunks[i].len,
+- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
+- }
+-out:
+ memcpy(&cmd->resource_config, &config, sizeof(config));
++ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
+- return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n");
++ return buf;
+ }
+
+-static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
++static struct sk_buff *ath10k_wmi_10_1_op_gen_init(struct ath10k *ar)
+ {
+ struct wmi_init_cmd_10x *cmd;
+ struct sk_buff *buf;
+ struct wmi_resource_config_10x config = {};
+ u32 len, val;
+- int i;
+
+ config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
+ config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
+@@ -2691,101 +3916,132 @@ static int ath10k_wmi_10x_cmd_init(struc
+ len = sizeof(*cmd) +
+ (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
+
+- buf = ath10k_wmi_alloc_skb(len);
++ buf = ath10k_wmi_alloc_skb(ar, len);
+ if (!buf)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_init_cmd_10x *)buf->data;
+
+- if (ar->wmi.num_mem_chunks == 0) {
+- cmd->num_host_mem_chunks = 0;
+- goto out;
+- }
++ memcpy(&cmd->resource_config, &config, sizeof(config));
++ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
+- ar->wmi.num_mem_chunks);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n");
++ return buf;
++}
+
+- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
++static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
++{
++ struct wmi_init_cmd_10_2 *cmd;
++ struct sk_buff *buf;
++ struct wmi_resource_config_10x config = {};
++ u32 len, val, features;
+
+- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+- cmd->host_mem_chunks[i].ptr =
+- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+- cmd->host_mem_chunks[i].size =
+- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+- cmd->host_mem_chunks[i].req_id =
+- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
++ config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
++ config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
++ config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS);
++ config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS);
++ config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT);
++ config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK);
++ config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK);
++ config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
++ config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
++ config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
++ config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI);
++ config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi chunk %d len %d requested, addr 0x%llx\n",
+- i,
+- ar->wmi.mem_chunks[i].len,
+- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
+- }
+-out:
+- memcpy(&cmd->resource_config, &config, sizeof(config));
++ config.scan_max_pending_reqs =
++ __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS);
++
++ config.bmiss_offload_max_vdev =
++ __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV);
++
++ config.roam_offload_max_vdev =
++ __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV);
++
++ config.roam_offload_max_ap_profiles =
++ __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES);
++
++ config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS);
++ config.num_mcast_table_elems =
++ __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS);
++
++ config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE);
++ config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE);
++ config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES);
++ config.dma_burst_size = __cpu_to_le32(TARGET_10_2_DMA_BURST_SIZE);
++ config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM);
++
++ val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
++ config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
++
++ config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG);
++
++ config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC);
++ config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n");
+- return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
++ len = sizeof(*cmd) +
++ (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks);
++
++ buf = ath10k_wmi_alloc_skb(ar, len);
++ if (!buf)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_init_cmd_10_2 *)buf->data;
++
++ features = WMI_10_2_RX_BATCH_MODE;
++ cmd->resource_config.feature_mask = __cpu_to_le32(features);
++
++ memcpy(&cmd->resource_config.common, &config, sizeof(config));
++ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n");
++ return buf;
+ }
+
+-int ath10k_wmi_cmd_init(struct ath10k *ar)
++int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
+ {
+- int ret;
++ if (arg->ie_len && !arg->ie)
++ return -EINVAL;
++ if (arg->n_channels && !arg->channels)
++ return -EINVAL;
++ if (arg->n_ssids && !arg->ssids)
++ return -EINVAL;
++ if (arg->n_bssids && !arg->bssids)
++ return -EINVAL;
+
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+- ret = ath10k_wmi_10x_cmd_init(ar);
+- else
+- ret = ath10k_wmi_main_cmd_init(ar);
++ if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
++ return -EINVAL;
++ if (arg->n_channels > ARRAY_SIZE(arg->channels))
++ return -EINVAL;
++ if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
++ return -EINVAL;
++ if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
++ return -EINVAL;
+
+- return ret;
++ return 0;
+ }
+
+-static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
+- const struct wmi_start_scan_arg *arg)
++static size_t
++ath10k_wmi_start_scan_tlvs_len(const struct wmi_start_scan_arg *arg)
+ {
+- int len;
+-
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+- len = sizeof(struct wmi_start_scan_cmd_10x);
+- else
+- len = sizeof(struct wmi_start_scan_cmd);
++ int len = 0;
+
+ if (arg->ie_len) {
+- if (!arg->ie)
+- return -EINVAL;
+- if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
+- return -EINVAL;
+-
+ len += sizeof(struct wmi_ie_data);
+ len += roundup(arg->ie_len, 4);
+ }
+
+ if (arg->n_channels) {
+- if (!arg->channels)
+- return -EINVAL;
+- if (arg->n_channels > ARRAY_SIZE(arg->channels))
+- return -EINVAL;
+-
+ len += sizeof(struct wmi_chan_list);
+ len += sizeof(__le32) * arg->n_channels;
+ }
+
+ if (arg->n_ssids) {
+- if (!arg->ssids)
+- return -EINVAL;
+- if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
+- return -EINVAL;
+-
+ len += sizeof(struct wmi_ssid_list);
+ len += sizeof(struct wmi_ssid) * arg->n_ssids;
+ }
+
+ if (arg->n_bssids) {
+- if (!arg->bssids)
+- return -EINVAL;
+- if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
+- return -EINVAL;
+-
+ len += sizeof(struct wmi_bssid_list);
+ len += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+ }
+@@ -2793,28 +4049,11 @@ static int ath10k_wmi_start_scan_calc_le
+ return len;
+ }
+
+-int ath10k_wmi_start_scan(struct ath10k *ar,
+- const struct wmi_start_scan_arg *arg)
++void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
++ const struct wmi_start_scan_arg *arg)
+ {
+- struct wmi_start_scan_cmd *cmd;
+- struct sk_buff *skb;
+- struct wmi_ie_data *ie;
+- struct wmi_chan_list *channels;
+- struct wmi_ssid_list *ssids;
+- struct wmi_bssid_list *bssids;
+ u32 scan_id;
+ u32 scan_req_id;
+- int off;
+- int len = 0;
+- int i;
+-
+- len = ath10k_wmi_start_scan_calc_len(ar, arg);
+- if (len < 0)
+- return len; /* len contains error code here */
+-
+- skb = ath10k_wmi_alloc_skb(len);
+- if (!skb)
+- return -ENOMEM;
+
+ scan_id = WMI_HOST_SCAN_REQ_ID_PREFIX;
+ scan_id |= arg->scan_id;
+@@ -2822,48 +4061,49 @@ int ath10k_wmi_start_scan(struct ath10k
+ scan_req_id = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+ scan_req_id |= arg->scan_req_id;
+
+- cmd = (struct wmi_start_scan_cmd *)skb->data;
+- cmd->scan_id = __cpu_to_le32(scan_id);
+- cmd->scan_req_id = __cpu_to_le32(scan_req_id);
+- cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+- cmd->scan_priority = __cpu_to_le32(arg->scan_priority);
+- cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
+- cmd->dwell_time_active = __cpu_to_le32(arg->dwell_time_active);
+- cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
+- cmd->min_rest_time = __cpu_to_le32(arg->min_rest_time);
+- cmd->max_rest_time = __cpu_to_le32(arg->max_rest_time);
+- cmd->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time);
+- cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
+- cmd->idle_time = __cpu_to_le32(arg->idle_time);
+- cmd->max_scan_time = __cpu_to_le32(arg->max_scan_time);
+- cmd->probe_delay = __cpu_to_le32(arg->probe_delay);
+- cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags);
+-
+- /* TLV list starts after fields included in the struct */
+- /* There's just one filed that differes the two start_scan
+- * structures - burst_duration, which we are not using btw,
+- no point to make the split here, just shift the buffer to fit with
+- given FW */
+- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+- off = sizeof(struct wmi_start_scan_cmd_10x);
+- else
+- off = sizeof(struct wmi_start_scan_cmd);
++ cmn->scan_id = __cpu_to_le32(scan_id);
++ cmn->scan_req_id = __cpu_to_le32(scan_req_id);
++ cmn->vdev_id = __cpu_to_le32(arg->vdev_id);
++ cmn->scan_priority = __cpu_to_le32(arg->scan_priority);
++ cmn->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
++ cmn->dwell_time_active = __cpu_to_le32(arg->dwell_time_active);
++ cmn->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
++ cmn->min_rest_time = __cpu_to_le32(arg->min_rest_time);
++ cmn->max_rest_time = __cpu_to_le32(arg->max_rest_time);
++ cmn->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time);
++ cmn->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
++ cmn->idle_time = __cpu_to_le32(arg->idle_time);
++ cmn->max_scan_time = __cpu_to_le32(arg->max_scan_time);
++ cmn->probe_delay = __cpu_to_le32(arg->probe_delay);
++ cmn->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags);
++}
++
++static void
++ath10k_wmi_put_start_scan_tlvs(struct wmi_start_scan_tlvs *tlvs,
++ const struct wmi_start_scan_arg *arg)
++{
++ struct wmi_ie_data *ie;
++ struct wmi_chan_list *channels;
++ struct wmi_ssid_list *ssids;
++ struct wmi_bssid_list *bssids;
++ void *ptr = tlvs->tlvs;
++ int i;
+
+ if (arg->n_channels) {
+- channels = (void *)skb->data + off;
++ channels = ptr;
+ channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG);
+ channels->num_chan = __cpu_to_le32(arg->n_channels);
+
+ for (i = 0; i < arg->n_channels; i++)
+- channels->channel_list[i] =
+- __cpu_to_le32(arg->channels[i]);
++ channels->channel_list[i].freq =
++ __cpu_to_le16(arg->channels[i]);
+
+- off += sizeof(*channels);
+- off += sizeof(__le32) * arg->n_channels;
++ ptr += sizeof(*channels);
++ ptr += sizeof(__le32) * arg->n_channels;
+ }
+
+ if (arg->n_ssids) {
+- ssids = (void *)skb->data + off;
++ ssids = ptr;
+ ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG);
+ ssids->num_ssids = __cpu_to_le32(arg->n_ssids);
+
+@@ -2875,12 +4115,12 @@ int ath10k_wmi_start_scan(struct ath10k
+ arg->ssids[i].len);
+ }
+
+- off += sizeof(*ssids);
+- off += sizeof(struct wmi_ssid) * arg->n_ssids;
++ ptr += sizeof(*ssids);
++ ptr += sizeof(struct wmi_ssid) * arg->n_ssids;
+ }
+
+ if (arg->n_bssids) {
+- bssids = (void *)skb->data + off;
++ bssids = ptr;
+ bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG);
+ bssids->num_bssid = __cpu_to_le32(arg->n_bssids);
+
+@@ -2889,27 +4129,75 @@ int ath10k_wmi_start_scan(struct ath10k
+ arg->bssids[i].bssid,
+ ETH_ALEN);
+
+- off += sizeof(*bssids);
+- off += sizeof(struct wmi_mac_addr) * arg->n_bssids;
++ ptr += sizeof(*bssids);
++ ptr += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+ }
+
+ if (arg->ie_len) {
+- ie = (void *)skb->data + off;
++ ie = ptr;
+ ie->tag = __cpu_to_le32(WMI_IE_TAG);
+ ie->ie_len = __cpu_to_le32(arg->ie_len);
+ memcpy(ie->ie_data, arg->ie, arg->ie_len);
+
+- off += sizeof(*ie);
+- off += roundup(arg->ie_len, 4);
++ ptr += sizeof(*ie);
++ ptr += roundup(arg->ie_len, 4);
+ }
++}
+
+- if (off != skb->len) {
+- dev_kfree_skb(skb);
+- return -EINVAL;
+- }
++static struct sk_buff *
++ath10k_wmi_op_gen_start_scan(struct ath10k *ar,
++ const struct wmi_start_scan_arg *arg)
++{
++ struct wmi_start_scan_cmd *cmd;
++ struct sk_buff *skb;
++ size_t len;
++ int ret;
++
++ ret = ath10k_wmi_start_scan_verify(arg);
++ if (ret)
++ return ERR_PTR(ret);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
++ len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_start_scan_cmd *)skb->data;
++
++ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
++ ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
++
++ cmd->burst_duration_ms = __cpu_to_le32(0);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_10x_op_gen_start_scan(struct ath10k *ar,
++ const struct wmi_start_scan_arg *arg)
++{
++ struct wmi_10x_start_scan_cmd *cmd;
++ struct sk_buff *skb;
++ size_t len;
++ int ret;
++
++ ret = ath10k_wmi_start_scan_verify(arg);
++ if (ret)
++ return ERR_PTR(ret);
++
++ len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
++
++ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
++ ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi 10x start scan\n");
++ return skb;
+ }
+
+ void ath10k_wmi_start_scan_init(struct ath10k *ar,
+@@ -2938,7 +4226,9 @@ void ath10k_wmi_start_scan_init(struct a
+ arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF";
+ }
+
+-int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
++static struct sk_buff *
++ath10k_wmi_op_gen_stop_scan(struct ath10k *ar,
++ const struct wmi_stop_scan_arg *arg)
+ {
+ struct wmi_stop_scan_cmd *cmd;
+ struct sk_buff *skb;
+@@ -2946,13 +4236,13 @@ int ath10k_wmi_stop_scan(struct ath10k *
+ u32 req_id;
+
+ if (arg->req_id > 0xFFF)
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+ if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ scan_id = arg->u.scan_id;
+ scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
+@@ -2966,92 +4256,85 @@ int ath10k_wmi_stop_scan(struct ath10k *
+ cmd->scan_id = __cpu_to_le32(scan_id);
+ cmd->scan_req_id = __cpu_to_le32(req_id);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
+ arg->req_id, arg->req_type, arg->u.scan_id);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+- enum wmi_vdev_type type,
+- enum wmi_vdev_subtype subtype,
+- const u8 macaddr[ETH_ALEN])
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_create(struct ath10k *ar, u32 vdev_id,
++ enum wmi_vdev_type type,
++ enum wmi_vdev_subtype subtype,
++ const u8 macaddr[ETH_ALEN])
+ {
+ struct wmi_vdev_create_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_create_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->vdev_type = __cpu_to_le32(type);
+ cmd->vdev_subtype = __cpu_to_le32(subtype);
+- memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN);
++ ether_addr_copy(cmd->vdev_macaddr.addr, macaddr);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
+ vdev_id, type, subtype, macaddr);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id)
+ {
+ struct wmi_vdev_delete_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_delete_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "WMI vdev delete id %d\n", vdev_id);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
++ return skb;
+ }
+
+-static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
+- const struct wmi_vdev_start_request_arg *arg,
+- u32 cmd_id)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_start(struct ath10k *ar,
++ const struct wmi_vdev_start_request_arg *arg,
++ bool restart)
+ {
+ struct wmi_vdev_start_request_cmd *cmd;
+ struct sk_buff *skb;
+ const char *cmdname;
+ u32 flags = 0;
+- u32 ch_flags = 0;
+
+- if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
+- cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
+- return -EINVAL;
+ if (WARN_ON(arg->ssid && arg->ssid_len == 0))
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+ if (WARN_ON(arg->hidden_ssid && !arg->ssid))
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+ if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+
+- if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid)
+- cmdname = "start";
+- else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid)
++ if (restart)
+ cmdname = "restart";
+ else
+- return -EINVAL; /* should not happen, we already check cmd_id */
++ cmdname = "start";
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ if (arg->hidden_ssid)
+ flags |= WMI_VDEV_START_HIDDEN_SSID;
+ if (arg->pmf_enabled)
+ flags |= WMI_VDEV_START_PMF_ENABLED;
+- if (arg->channel.chan_radar)
+- ch_flags |= WMI_CHAN_FLAG_DFS;
+
+ cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+@@ -3067,143 +4350,118 @@ static int ath10k_wmi_vdev_start_restart
+ memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
+ }
+
+- cmd->chan.mhz = __cpu_to_le32(arg->channel.freq);
++ ath10k_wmi_put_wmi_channel(&cmd->chan, &arg->channel);
+
+- cmd->chan.band_center_freq1 =
+- __cpu_to_le32(arg->channel.band_center_freq1);
+-
+- cmd->chan.mode = arg->channel.mode;
+- cmd->chan.flags |= __cpu_to_le32(ch_flags);
+- cmd->chan.min_power = arg->channel.min_power;
+- cmd->chan.max_power = arg->channel.max_power;
+- cmd->chan.reg_power = arg->channel.max_reg_power;
+- cmd->chan.reg_classid = arg->channel.reg_class_id;
+- cmd->chan.antenna_max = arg->channel.max_antenna_gain;
+-
+- ath10k_dbg(ATH10K_DBG_WMI,
+- "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, "
+- "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, ch_flags: 0x%0X, max_power: %d\n",
++ cmdname, arg->vdev_id,
+ flags, arg->channel.freq, arg->channel.mode,
+ cmd->chan.flags, arg->channel.max_power);
+
+- return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+-}
+-
+-int ath10k_wmi_vdev_start(struct ath10k *ar,
+- const struct wmi_vdev_start_request_arg *arg)
+-{
+- u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid;
+-
+- return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
+-}
+-
+-int ath10k_wmi_vdev_restart(struct ath10k *ar,
+- const struct wmi_vdev_start_request_arg *arg)
+-{
+- u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid;
+-
+- return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id)
+ {
+ struct wmi_vdev_stop_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_stop_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
++ const u8 *bssid)
+ {
+ struct wmi_vdev_up_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_up_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->vdev_assoc_id = __cpu_to_le32(aid);
+- memcpy(&cmd->vdev_bssid.addr, bssid, ETH_ALEN);
++ ether_addr_copy(cmd->vdev_bssid.addr, bssid);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
+ vdev_id, aid, bssid);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id)
+ {
+ struct wmi_vdev_down_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_down_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi mgmt vdev down id 0x%x\n", vdev_id);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+- u32 param_id, u32 param_value)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id,
++ u32 param_id, u32 param_value)
+ {
+ struct wmi_vdev_set_param_cmd *cmd;
+ struct sk_buff *skb;
+
+ if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) {
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "vdev param %d not supported by firmware\n",
+ param_id);
+- return -EOPNOTSUPP;
++ return ERR_PTR(-EOPNOTSUPP);
+ }
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_set_param_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->param_id = __cpu_to_le32(param_id);
+ cmd->param_value = __cpu_to_le32(param_value);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi vdev id 0x%x set param %d value %d\n",
+ vdev_id, param_id, param_value);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+- const struct wmi_vdev_install_key_arg *arg)
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_install_key(struct ath10k *ar,
++ const struct wmi_vdev_install_key_arg *arg)
+ {
+ struct wmi_vdev_install_key_cmd *cmd;
+ struct sk_buff *skb;
+
+ if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+ if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len);
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + arg->key_len);
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+@@ -3215,176 +4473,232 @@ int ath10k_wmi_vdev_install_key(struct a
+ cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len);
+
+ if (arg->macaddr)
+- memcpy(cmd->peer_macaddr.addr, arg->macaddr, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr);
+ if (arg->key_data)
+ memcpy(cmd->key_data, arg->key_data, arg->key_len);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi vdev install key idx %d cipher %d len %d\n",
+ arg->key_idx, arg->key_cipher, arg->key_len);
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->vdev_install_key_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+- const u8 peer_addr[ETH_ALEN])
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_spectral_conf(struct ath10k *ar,
++ const struct wmi_vdev_spectral_conf_arg *arg)
++{
++ struct wmi_vdev_spectral_conf_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_vdev_spectral_conf_cmd *)skb->data;
++ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
++ cmd->scan_count = __cpu_to_le32(arg->scan_count);
++ cmd->scan_period = __cpu_to_le32(arg->scan_period);
++ cmd->scan_priority = __cpu_to_le32(arg->scan_priority);
++ cmd->scan_fft_size = __cpu_to_le32(arg->scan_fft_size);
++ cmd->scan_gc_ena = __cpu_to_le32(arg->scan_gc_ena);
++ cmd->scan_restart_ena = __cpu_to_le32(arg->scan_restart_ena);
++ cmd->scan_noise_floor_ref = __cpu_to_le32(arg->scan_noise_floor_ref);
++ cmd->scan_init_delay = __cpu_to_le32(arg->scan_init_delay);
++ cmd->scan_nb_tone_thr = __cpu_to_le32(arg->scan_nb_tone_thr);
++ cmd->scan_str_bin_thr = __cpu_to_le32(arg->scan_str_bin_thr);
++ cmd->scan_wb_rpt_mode = __cpu_to_le32(arg->scan_wb_rpt_mode);
++ cmd->scan_rssi_rpt_mode = __cpu_to_le32(arg->scan_rssi_rpt_mode);
++ cmd->scan_rssi_thr = __cpu_to_le32(arg->scan_rssi_thr);
++ cmd->scan_pwr_format = __cpu_to_le32(arg->scan_pwr_format);
++ cmd->scan_rpt_mode = __cpu_to_le32(arg->scan_rpt_mode);
++ cmd->scan_bin_scale = __cpu_to_le32(arg->scan_bin_scale);
++ cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj);
++ cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask);
++
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id,
++ u32 trigger, u32 enable)
++{
++ struct wmi_vdev_spectral_enable_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_vdev_spectral_enable_cmd *)skb->data;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->trigger_cmd = __cpu_to_le32(trigger);
++ cmd->enable_cmd = __cpu_to_le32(enable);
++
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_peer_create(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN])
+ {
+ struct wmi_peer_create_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_peer_create_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+- memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi peer create vdev_id %d peer_addr %pM\n",
+ vdev_id, peer_addr);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+- const u8 peer_addr[ETH_ALEN])
++static struct sk_buff *
++ath10k_wmi_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN])
+ {
+ struct wmi_peer_delete_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_peer_delete_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+- memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi peer delete vdev_id %d peer_addr %pM\n",
+ vdev_id, peer_addr);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+- const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
++static struct sk_buff *
++ath10k_wmi_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+ {
+ struct wmi_peer_flush_tids_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_peer_flush_tids_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap);
+- memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
+ vdev_id, peer_addr, tid_bitmap);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+- const u8 *peer_addr, enum wmi_peer_param param_id,
+- u32 param_value)
++static struct sk_buff *
++ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
++ const u8 *peer_addr,
++ enum wmi_peer_param param_id,
++ u32 param_value)
+ {
+ struct wmi_peer_set_param_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_peer_set_param_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->param_id = __cpu_to_le32(param_id);
+ cmd->param_value = __cpu_to_le32(param_value);
+- memcpy(&cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi vdev %d peer 0x%pM set param %d value %d\n",
+ vdev_id, peer_addr, param_id, param_value);
+-
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+- enum wmi_sta_ps_mode psmode)
++static struct sk_buff *
++ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_ps_mode psmode)
+ {
+ struct wmi_sta_powersave_mode_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->sta_ps_mode = __cpu_to_le32(psmode);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi set powersave id 0x%x mode %d\n",
+ vdev_id, psmode);
+-
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->sta_powersave_mode_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+- enum wmi_sta_powersave_param param_id,
+- u32 value)
++static struct sk_buff *
++ath10k_wmi_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_powersave_param param_id,
++ u32 value)
+ {
+ struct wmi_sta_powersave_param_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_sta_powersave_param_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->param_id = __cpu_to_le32(param_id);
+ cmd->param_value = __cpu_to_le32(value);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi sta ps param vdev_id 0x%x param %d value %d\n",
+ vdev_id, param_id, value);
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->sta_powersave_param_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+- enum wmi_ap_ps_peer_param param_id, u32 value)
++static struct sk_buff *
++ath10k_wmi_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ enum wmi_ap_ps_peer_param param_id, u32 value)
+ {
+ struct wmi_ap_ps_peer_cmd *cmd;
+ struct sk_buff *skb;
+
+ if (!mac)
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_ap_ps_peer_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(vdev_id);
+ cmd->param_id = __cpu_to_le32(param_id);
+ cmd->param_value = __cpu_to_le32(value);
+- memcpy(&cmd->peer_macaddr, mac, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, mac);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
+ vdev_id, param_id, value, mac);
+-
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->ap_ps_peer_param_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+- const struct wmi_scan_chan_list_arg *arg)
++static struct sk_buff *
++ath10k_wmi_op_gen_scan_chan_list(struct ath10k *ar,
++ const struct wmi_scan_chan_list_arg *arg)
+ {
+ struct wmi_scan_chan_list_cmd *cmd;
+ struct sk_buff *skb;
+@@ -3395,66 +4709,29 @@ int ath10k_wmi_scan_chan_list(struct ath
+
+ len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel);
+
+- skb = ath10k_wmi_alloc_skb(len);
++ skb = ath10k_wmi_alloc_skb(ar, len);
+ if (!skb)
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+
+ cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
+ cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
+
+ for (i = 0; i < arg->n_channels; i++) {
+- u32 flags = 0;
+-
+ ch = &arg->channels[i];
+ ci = &cmd->chan_info[i];
+
+- if (ch->passive)
+- flags |= WMI_CHAN_FLAG_PASSIVE;
+- if (ch->allow_ibss)
+- flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
+- if (ch->allow_ht)
+- flags |= WMI_CHAN_FLAG_ALLOW_HT;
+- if (ch->allow_vht)
+- flags |= WMI_CHAN_FLAG_ALLOW_VHT;
+- if (ch->ht40plus)
+- flags |= WMI_CHAN_FLAG_HT40_PLUS;
+- if (ch->chan_radar)
+- flags |= WMI_CHAN_FLAG_DFS;
+-
+- ci->mhz = __cpu_to_le32(ch->freq);
+- ci->band_center_freq1 = __cpu_to_le32(ch->freq);
+- ci->band_center_freq2 = 0;
+- ci->min_power = ch->min_power;
+- ci->max_power = ch->max_power;
+- ci->reg_power = ch->max_reg_power;
+- ci->antenna_max = ch->max_antenna_gain;
+-
+- /* mode & flags share storage */
+- ci->mode = ch->mode;
+- ci->flags |= __cpu_to_le32(flags);
++ ath10k_wmi_put_wmi_channel(ci, ch);
+ }
+
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_peer_assoc(struct ath10k *ar,
+- const struct wmi_peer_assoc_complete_arg *arg)
++static void
++ath10k_wmi_peer_assoc_fill(struct ath10k *ar, void *buf,
++ const struct wmi_peer_assoc_complete_arg *arg)
+ {
+- struct wmi_peer_assoc_complete_cmd *cmd;
+- struct sk_buff *skb;
++ struct wmi_common_peer_assoc_complete_cmd *cmd = buf;
+
+- if (arg->peer_mpdu_density > 16)
+- return -EINVAL;
+- if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
+- return -EINVAL;
+- if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
+- return -EINVAL;
+-
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+- if (!skb)
+- return -ENOMEM;
+-
+- cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data;
+ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
+ cmd->peer_new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1);
+ cmd->peer_associd = __cpu_to_le32(arg->peer_aid);
+@@ -3469,7 +4746,7 @@ int ath10k_wmi_peer_assoc(struct ath10k
+ cmd->peer_vht_caps = __cpu_to_le32(arg->peer_vht_caps);
+ cmd->peer_phymode = __cpu_to_le32(arg->peer_phymode);
+
+- memcpy(cmd->peer_macaddr.addr, arg->addr, ETH_ALEN);
++ ether_addr_copy(cmd->peer_macaddr.addr, arg->addr);
+
+ cmd->peer_legacy_rates.num_rates =
+ __cpu_to_le32(arg->peer_legacy_rates.num_rates);
+@@ -3489,57 +4766,183 @@ int ath10k_wmi_peer_assoc(struct ath10k
+ __cpu_to_le32(arg->peer_vht_rates.tx_max_rate);
+ cmd->peer_vht_rates.tx_mcs_set =
+ __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
++}
++
++static void
++ath10k_wmi_peer_assoc_fill_main(struct ath10k *ar, void *buf,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ struct wmi_main_peer_assoc_complete_cmd *cmd = buf;
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_wmi_peer_assoc_fill(ar, buf, arg);
++ memset(cmd->peer_ht_info, 0, sizeof(cmd->peer_ht_info));
++}
++
++static void
++ath10k_wmi_peer_assoc_fill_10_1(struct ath10k *ar, void *buf,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ ath10k_wmi_peer_assoc_fill(ar, buf, arg);
++}
++
++static void
++ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ struct wmi_10_2_peer_assoc_complete_cmd *cmd = buf;
++ int max_mcs, max_nss;
++ u32 info0;
++
++ /* TODO: Is using max values okay with firmware? */
++ max_mcs = 0xf;
++ max_nss = 0xf;
++
++ info0 = SM(max_mcs, WMI_PEER_ASSOC_INFO0_MAX_MCS_IDX) |
++ SM(max_nss, WMI_PEER_ASSOC_INFO0_MAX_NSS);
++
++ ath10k_wmi_peer_assoc_fill(ar, buf, arg);
++ cmd->info0 = __cpu_to_le32(info0);
++}
++
++static int
++ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg)
++{
++ if (arg->peer_mpdu_density > 16)
++ return -EINVAL;
++ if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
++ return -EINVAL;
++ if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
++ return -EINVAL;
++
++ return 0;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_peer_assoc(struct ath10k *ar,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ size_t len = sizeof(struct wmi_main_peer_assoc_complete_cmd);
++ struct sk_buff *skb;
++ int ret;
++
++ ret = ath10k_wmi_peer_assoc_check_arg(arg);
++ if (ret)
++ return ERR_PTR(ret);
++
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi peer assoc vdev %d addr %pM (%s)\n",
++ arg->vdev_id, arg->addr,
++ arg->peer_reassoc ? "reassociate" : "new");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_10_1_op_gen_peer_assoc(struct ath10k *ar,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ size_t len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd);
++ struct sk_buff *skb;
++ int ret;
++
++ ret = ath10k_wmi_peer_assoc_check_arg(arg);
++ if (ret)
++ return ERR_PTR(ret);
++
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi peer assoc vdev %d addr %pM (%s)\n",
++ arg->vdev_id, arg->addr,
++ arg->peer_reassoc ? "reassociate" : "new");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ size_t len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd);
++ struct sk_buff *skb;
++ int ret;
++
++ ret = ath10k_wmi_peer_assoc_check_arg(arg);
++ if (ret)
++ return ERR_PTR(ret);
++
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi peer assoc vdev %d addr %pM (%s)\n",
+ arg->vdev_id, arg->addr,
+ arg->peer_reassoc ? "reassociate" : "new");
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar)
++{
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, 0);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev get temperature\n");
++ return skb;
+ }
+
+ /* This function assumes the beacon is already DMA mapped */
+-int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
++static struct sk_buff *
++ath10k_wmi_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id, const void *bcn,
++ size_t bcn_len, u32 bcn_paddr, bool dtim_zero,
++ bool deliver_cab)
+ {
+ struct wmi_bcn_tx_ref_cmd *cmd;
+ struct sk_buff *skb;
+- struct sk_buff *beacon = arvif->beacon;
+- struct ath10k *ar = arvif->ar;
+ struct ieee80211_hdr *hdr;
+- int ret;
+ u16 fc;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+- hdr = (struct ieee80211_hdr *)beacon->data;
++ hdr = (struct ieee80211_hdr *)bcn;
+ fc = le16_to_cpu(hdr->frame_control);
+
+ cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data;
+- cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
+- cmd->data_len = __cpu_to_le32(beacon->len);
+- cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->data_len = __cpu_to_le32(bcn_len);
++ cmd->data_ptr = __cpu_to_le32(bcn_paddr);
+ cmd->msdu_id = 0;
+ cmd->frame_control = __cpu_to_le32(fc);
+ cmd->flags = 0;
++ cmd->antenna_mask = __cpu_to_le32(WMI_BCN_TX_REF_DEF_ANTENNA);
+
+- if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
++ if (dtim_zero)
+ cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
+
+- if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
++ if (deliver_cab)
+ cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
+
+- ret = ath10k_wmi_cmd_send_nowait(ar, skb,
+- ar->wmi.cmd->pdev_send_bcn_cmdid);
+-
+- if (ret)
+- dev_kfree_skb(skb);
+-
+- return ret;
++ return skb;
+ }
+
+-static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
+- const struct wmi_wmm_params_arg *arg)
++void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params,
++ const struct wmi_wmm_params_arg *arg)
+ {
+ params->cwmin = __cpu_to_le32(arg->cwmin);
+ params->cwmax = __cpu_to_le32(arg->cwmax);
+@@ -3549,76 +4952,81 @@ static void ath10k_wmi_pdev_set_wmm_para
+ params->no_ack = __cpu_to_le32(arg->no_ack);
+ }
+
+-int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+- const struct wmi_pdev_set_wmm_params_arg *arg)
++static struct sk_buff *
++ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar,
++ const struct wmi_wmm_params_all_arg *arg)
+ {
+ struct wmi_pdev_set_wmm_params *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
+- ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
+- ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
+- ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
+- ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
++ ath10k_wmi_set_wmm_param(&cmd->ac_be, &arg->ac_be);
++ ath10k_wmi_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
++ ath10k_wmi_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
++ ath10k_wmi_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
+- return ath10k_wmi_cmd_send(ar, skb,
+- ar->wmi.cmd->pdev_set_wmm_params_cmdid);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
++ return skb;
+ }
+
+-int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
++static struct sk_buff *
++ath10k_wmi_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
+ {
+ struct wmi_request_stats_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_request_stats_cmd *)skb->data;
+- cmd->stats_id = __cpu_to_le32(stats_id);
++ cmd->stats_id = __cpu_to_le32(stats_mask);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats 0x%08x\n",
++ stats_mask);
++ return skb;
+ }
+
+-int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+- enum wmi_force_fw_hang_type type, u32 delay_ms)
++static struct sk_buff *
++ath10k_wmi_op_gen_force_fw_hang(struct ath10k *ar,
++ enum wmi_force_fw_hang_type type, u32 delay_ms)
+ {
+ struct wmi_force_fw_hang_cmd *cmd;
+ struct sk_buff *skb;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+ cmd->type = __cpu_to_le32(type);
+ cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+- ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+ type, delay_ms);
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
++ return skb;
+ }
+
+-int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
++static struct sk_buff *
++ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable,
++ u32 log_level)
+ {
+ struct wmi_dbglog_cfg_cmd *cmd;
+ struct sk_buff *skb;
+ u32 cfg;
+
+- skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+
+ cmd = (struct wmi_dbglog_cfg_cmd *)skb->data;
+
+ if (module_enable) {
+- cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE,
++ cfg = SM(log_level,
+ ATH10K_DBGLOG_CFG_LOG_LVL);
+ } else {
+ /* set back defaults, all modules with WARN level */
+@@ -3632,12 +5040,474 @@ int ath10k_wmi_dbglog_cfg(struct ath10k
+ cmd->config_enable = __cpu_to_le32(cfg);
+ cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK);
+
+- ath10k_dbg(ATH10K_DBG_WMI,
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi dbglog cfg modules %08x %08x config %08x %08x\n",
+ __le32_to_cpu(cmd->module_enable),
+ __le32_to_cpu(cmd->module_valid),
+ __le32_to_cpu(cmd->config_enable),
+ __le32_to_cpu(cmd->config_valid));
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
++{
++ struct wmi_pdev_pktlog_enable_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ev_bitmap &= ATH10K_PKTLOG_ANY;
++
++ cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data;
++ cmd->ev_bitmap = __cpu_to_le32(ev_bitmap);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi enable pktlog filter 0x%08x\n",
++ ev_bitmap);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_pktlog_disable(struct ath10k *ar)
++{
++ struct sk_buff *skb;
+
+- return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
++ skb = ath10k_wmi_alloc_skb(ar, 0);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_pdev_set_quiet_mode(struct ath10k *ar, u32 period,
++ u32 duration, u32 next_offset,
++ u32 enabled)
++{
++ struct wmi_pdev_set_quiet_cmd *cmd;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_pdev_set_quiet_cmd *)skb->data;
++ cmd->period = __cpu_to_le32(period);
++ cmd->duration = __cpu_to_le32(duration);
++ cmd->next_start = __cpu_to_le32(next_offset);
++ cmd->enabled = __cpu_to_le32(enabled);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi quiet param: period %u duration %u enabled %d\n",
++ period, duration, enabled);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_addba_clear_resp(struct ath10k *ar, u32 vdev_id,
++ const u8 *mac)
++{
++ struct wmi_addba_clear_resp_cmd *cmd;
++ struct sk_buff *skb;
++
++ if (!mac)
++ return ERR_PTR(-EINVAL);
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_addba_clear_resp_cmd *)skb->data;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ ether_addr_copy(cmd->peer_macaddr.addr, mac);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi addba clear resp vdev_id 0x%X mac_addr %pM\n",
++ vdev_id, mac);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ u32 tid, u32 buf_size)
++{
++ struct wmi_addba_send_cmd *cmd;
++ struct sk_buff *skb;
++
++ if (!mac)
++ return ERR_PTR(-EINVAL);
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_addba_send_cmd *)skb->data;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ ether_addr_copy(cmd->peer_macaddr.addr, mac);
++ cmd->tid = __cpu_to_le32(tid);
++ cmd->buffersize = __cpu_to_le32(buf_size);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi addba send vdev_id 0x%X mac_addr %pM tid %u bufsize %u\n",
++ vdev_id, mac, tid, buf_size);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ u32 tid, u32 status)
++{
++ struct wmi_addba_setresponse_cmd *cmd;
++ struct sk_buff *skb;
++
++ if (!mac)
++ return ERR_PTR(-EINVAL);
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_addba_setresponse_cmd *)skb->data;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ ether_addr_copy(cmd->peer_macaddr.addr, mac);
++ cmd->tid = __cpu_to_le32(tid);
++ cmd->statuscode = __cpu_to_le32(status);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi addba set resp vdev_id 0x%X mac_addr %pM tid %u status %u\n",
++ vdev_id, mac, tid, status);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_op_gen_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ u32 tid, u32 initiator, u32 reason)
++{
++ struct wmi_delba_send_cmd *cmd;
++ struct sk_buff *skb;
++
++ if (!mac)
++ return ERR_PTR(-EINVAL);
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ cmd = (struct wmi_delba_send_cmd *)skb->data;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ ether_addr_copy(cmd->peer_macaddr.addr, mac);
++ cmd->tid = __cpu_to_le32(tid);
++ cmd->initiator = __cpu_to_le32(initiator);
++ cmd->reasoncode = __cpu_to_le32(reason);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi delba send vdev_id 0x%X mac_addr %pM tid %u initiator %u reason %u\n",
++ vdev_id, mac, tid, initiator, reason);
++ return skb;
++}
++
++static const struct wmi_ops wmi_ops = {
++ .rx = ath10k_wmi_op_rx,
++ .map_svc = wmi_main_svc_map,
++
++ .pull_scan = ath10k_wmi_op_pull_scan_ev,
++ .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
++ .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
++ .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
++ .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
++ .pull_swba = ath10k_wmi_op_pull_swba_ev,
++ .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
++ .pull_svc_rdy = ath10k_wmi_main_op_pull_svc_rdy_ev,
++ .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
++ .pull_fw_stats = ath10k_wmi_main_op_pull_fw_stats,
++
++ .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
++ .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
++ .gen_pdev_set_rd = ath10k_wmi_op_gen_pdev_set_rd,
++ .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
++ .gen_init = ath10k_wmi_op_gen_init,
++ .gen_start_scan = ath10k_wmi_op_gen_start_scan,
++ .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
++ .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
++ .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
++ .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
++ .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
++ .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
++ .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
++ .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
++ .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
++ .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
++ .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
++ /* .gen_vdev_wmm_conf not implemented */
++ .gen_peer_create = ath10k_wmi_op_gen_peer_create,
++ .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
++ .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
++ .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
++ .gen_peer_assoc = ath10k_wmi_op_gen_peer_assoc,
++ .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
++ .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
++ .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
++ .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
++ .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
++ .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
++ .gen_request_stats = ath10k_wmi_op_gen_request_stats,
++ .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
++ .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
++ .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
++ .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
++ .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
++ .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
++ /* .gen_pdev_get_temperature not implemented */
++ .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
++ .gen_addba_send = ath10k_wmi_op_gen_addba_send,
++ .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
++ .gen_delba_send = ath10k_wmi_op_gen_delba_send,
++ /* .gen_bcn_tmpl not implemented */
++ /* .gen_prb_tmpl not implemented */
++ /* .gen_p2p_go_bcn_ie not implemented */
++};
++
++static const struct wmi_ops wmi_10_1_ops = {
++ .rx = ath10k_wmi_10_1_op_rx,
++ .map_svc = wmi_10x_svc_map,
++ .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
++ .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
++ .gen_init = ath10k_wmi_10_1_op_gen_init,
++ .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
++ .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
++ .gen_peer_assoc = ath10k_wmi_10_1_op_gen_peer_assoc,
++ /* .gen_pdev_get_temperature not implemented */
++
++ /* shared with main branch */
++ .pull_scan = ath10k_wmi_op_pull_scan_ev,
++ .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
++ .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
++ .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
++ .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
++ .pull_swba = ath10k_wmi_op_pull_swba_ev,
++ .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
++ .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
++
++ .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
++ .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
++ .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
++ .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
++ .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
++ .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
++ .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
++ .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
++ .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
++ .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
++ .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
++ .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
++ .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
++ .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
++ /* .gen_vdev_wmm_conf not implemented */
++ .gen_peer_create = ath10k_wmi_op_gen_peer_create,
++ .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
++ .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
++ .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
++ .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
++ .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
++ .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
++ .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
++ .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
++ .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
++ .gen_request_stats = ath10k_wmi_op_gen_request_stats,
++ .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
++ .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
++ .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
++ .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
++ .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
++ .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
++ .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
++ .gen_addba_send = ath10k_wmi_op_gen_addba_send,
++ .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
++ .gen_delba_send = ath10k_wmi_op_gen_delba_send,
++ /* .gen_bcn_tmpl not implemented */
++ /* .gen_prb_tmpl not implemented */
++ /* .gen_p2p_go_bcn_ie not implemented */
++};
++
++static const struct wmi_ops wmi_10_2_ops = {
++ .rx = ath10k_wmi_10_2_op_rx,
++ .pull_fw_stats = ath10k_wmi_10_2_op_pull_fw_stats,
++ .gen_init = ath10k_wmi_10_2_op_gen_init,
++ .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
++ /* .gen_pdev_get_temperature not implemented */
++
++ /* shared with 10.1 */
++ .map_svc = wmi_10x_svc_map,
++ .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
++ .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
++ .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
++
++ .pull_scan = ath10k_wmi_op_pull_scan_ev,
++ .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
++ .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
++ .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
++ .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
++ .pull_swba = ath10k_wmi_op_pull_swba_ev,
++ .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
++ .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
++
++ .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
++ .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
++ .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
++ .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
++ .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
++ .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
++ .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
++ .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
++ .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
++ .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
++ .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
++ .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
++ .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
++ .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
++ /* .gen_vdev_wmm_conf not implemented */
++ .gen_peer_create = ath10k_wmi_op_gen_peer_create,
++ .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
++ .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
++ .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
++ .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
++ .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
++ .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
++ .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
++ .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
++ .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
++ .gen_request_stats = ath10k_wmi_op_gen_request_stats,
++ .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
++ .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
++ .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
++ .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
++ .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
++ .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
++ .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
++ .gen_addba_send = ath10k_wmi_op_gen_addba_send,
++ .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
++ .gen_delba_send = ath10k_wmi_op_gen_delba_send,
++};
++
++static const struct wmi_ops wmi_10_2_4_ops = {
++ .rx = ath10k_wmi_10_2_op_rx,
++ .pull_fw_stats = ath10k_wmi_10_2_4_op_pull_fw_stats,
++ .gen_init = ath10k_wmi_10_2_op_gen_init,
++ .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
++ .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
++
++ /* shared with 10.1 */
++ .map_svc = wmi_10x_svc_map,
++ .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
++ .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
++ .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
++
++ .pull_scan = ath10k_wmi_op_pull_scan_ev,
++ .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
++ .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
++ .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
++ .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
++ .pull_swba = ath10k_wmi_op_pull_swba_ev,
++ .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
++ .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
++
++ .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
++ .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
++ .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
++ .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
++ .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
++ .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
++ .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
++ .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
++ .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
++ .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
++ .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
++ .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
++ .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
++ .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
++ .gen_peer_create = ath10k_wmi_op_gen_peer_create,
++ .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
++ .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
++ .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
++ .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
++ .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
++ .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
++ .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
++ .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
++ .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
++ .gen_request_stats = ath10k_wmi_op_gen_request_stats,
++ .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
++ .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
++ .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
++ .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
++ .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
++ .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
++ .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp,
++ .gen_addba_send = ath10k_wmi_op_gen_addba_send,
++ .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
++ .gen_delba_send = ath10k_wmi_op_gen_delba_send,
++ /* .gen_bcn_tmpl not implemented */
++ /* .gen_prb_tmpl not implemented */
++ /* .gen_p2p_go_bcn_ie not implemented */
++};
++
++int ath10k_wmi_attach(struct ath10k *ar)
++{
++ switch (ar->wmi.op_version) {
++ case ATH10K_FW_WMI_OP_VERSION_10_2_4:
++ ar->wmi.cmd = &wmi_10_2_4_cmd_map;
++ ar->wmi.ops = &wmi_10_2_4_ops;
++ ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map;
++ ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_10_2:
++ ar->wmi.cmd = &wmi_10_2_cmd_map;
++ ar->wmi.ops = &wmi_10_2_ops;
++ ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
++ ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_10_1:
++ ar->wmi.cmd = &wmi_10x_cmd_map;
++ ar->wmi.ops = &wmi_10_1_ops;
++ ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
++ ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_MAIN:
++ ar->wmi.cmd = &wmi_cmd_map;
++ ar->wmi.ops = &wmi_ops;
++ ar->wmi.vdev_param = &wmi_vdev_param_map;
++ ar->wmi.pdev_param = &wmi_pdev_param_map;
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_TLV:
++ ath10k_wmi_tlv_attach(ar);
++ break;
++ case ATH10K_FW_WMI_OP_VERSION_UNSET:
++ case ATH10K_FW_WMI_OP_VERSION_MAX:
++ ath10k_err(ar, "unsupported WMI op version: %d\n",
++ ar->wmi.op_version);
++ return -EINVAL;
++ }
++
++ init_completion(&ar->wmi.service_ready);
++ init_completion(&ar->wmi.unified_ready);
++
++ return 0;
++}
++
++void ath10k_wmi_detach(struct ath10k *ar)
++{
++ int i;
++
++ /* free the host memory chunks requested by firmware */
++ for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
++ dma_free_coherent(ar->dev,
++ ar->wmi.mem_chunks[i].len,
++ ar->wmi.mem_chunks[i].vaddr,
++ ar->wmi.mem_chunks[i].paddr);
++ }
++
++ ar->wmi.num_mem_chunks = 0;
+ }
+--- a/drivers/net/wireless/ath/ath10k/wmi.h
++++ b/drivers/net/wireless/ath/ath10k/wmi.h
+@@ -73,119 +73,361 @@ struct wmi_cmd_hdr {
+ #define HTC_PROTOCOL_VERSION 0x0002
+ #define WMI_PROTOCOL_VERSION 0x0002
+
+-enum wmi_service_id {
+- WMI_SERVICE_BEACON_OFFLOAD = 0, /* beacon offload */
+- WMI_SERVICE_SCAN_OFFLOAD, /* scan offload */
+- WMI_SERVICE_ROAM_OFFLOAD, /* roam offload */
+- WMI_SERVICE_BCN_MISS_OFFLOAD, /* beacon miss offload */
+- WMI_SERVICE_STA_PWRSAVE, /* fake sleep + basic power save */
+- WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */
+- WMI_SERVICE_AP_UAPSD, /* uapsd on AP */
+- WMI_SERVICE_AP_DFS, /* DFS on AP */
+- WMI_SERVICE_11AC, /* supports 11ac */
+- WMI_SERVICE_BLOCKACK, /* Supports triggering ADDBA/DELBA from host*/
+- WMI_SERVICE_PHYERR, /* PHY error */
+- WMI_SERVICE_BCN_FILTER, /* Beacon filter support */
+- WMI_SERVICE_RTT, /* RTT (round trip time) support */
+- WMI_SERVICE_RATECTRL, /* Rate-control */
+- WMI_SERVICE_WOW, /* WOW Support */
+- WMI_SERVICE_RATECTRL_CACHE, /* Rate-control caching */
+- WMI_SERVICE_IRAM_TIDS, /* TIDs in IRAM */
+- WMI_SERVICE_ARPNS_OFFLOAD, /* ARP NS Offload support */
+- WMI_SERVICE_NLO, /* Network list offload service */
+- WMI_SERVICE_GTK_OFFLOAD, /* GTK offload */
+- WMI_SERVICE_SCAN_SCH, /* Scan Scheduler Service */
+- WMI_SERVICE_CSA_OFFLOAD, /* CSA offload service */
+- WMI_SERVICE_CHATTER, /* Chatter service */
+- WMI_SERVICE_COEX_FREQAVOID, /* FW report freq range to avoid */
+- WMI_SERVICE_PACKET_POWER_SAVE, /* packet power save service */
+- WMI_SERVICE_FORCE_FW_HANG, /* To test fw recovery mechanism */
+- WMI_SERVICE_GPIO, /* GPIO service */
+- WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */
+- WMI_STA_UAPSD_BASIC_AUTO_TRIG, /* UAPSD AC Trigger Generation */
+- WMI_STA_UAPSD_VAR_AUTO_TRIG, /* -do- */
+- WMI_SERVICE_STA_KEEP_ALIVE, /* STA keep alive mechanism support */
+- WMI_SERVICE_TX_ENCAP, /* Packet type for TX encapsulation */
+-
+- WMI_SERVICE_LAST,
+- WMI_MAX_SERVICE = 64 /* max service */
++enum wmi_service {
++ WMI_SERVICE_BEACON_OFFLOAD = 0,
++ WMI_SERVICE_SCAN_OFFLOAD,
++ WMI_SERVICE_ROAM_OFFLOAD,
++ WMI_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_SERVICE_STA_PWRSAVE,
++ WMI_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_SERVICE_AP_UAPSD,
++ WMI_SERVICE_AP_DFS,
++ WMI_SERVICE_11AC,
++ WMI_SERVICE_BLOCKACK,
++ WMI_SERVICE_PHYERR,
++ WMI_SERVICE_BCN_FILTER,
++ WMI_SERVICE_RTT,
++ WMI_SERVICE_RATECTRL,
++ WMI_SERVICE_WOW,
++ WMI_SERVICE_RATECTRL_CACHE,
++ WMI_SERVICE_IRAM_TIDS,
++ WMI_SERVICE_ARPNS_OFFLOAD,
++ WMI_SERVICE_NLO,
++ WMI_SERVICE_GTK_OFFLOAD,
++ WMI_SERVICE_SCAN_SCH,
++ WMI_SERVICE_CSA_OFFLOAD,
++ WMI_SERVICE_CHATTER,
++ WMI_SERVICE_COEX_FREQAVOID,
++ WMI_SERVICE_PACKET_POWER_SAVE,
++ WMI_SERVICE_FORCE_FW_HANG,
++ WMI_SERVICE_GPIO,
++ WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
++ WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
++ WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
++ WMI_SERVICE_STA_KEEP_ALIVE,
++ WMI_SERVICE_TX_ENCAP,
++ WMI_SERVICE_BURST,
++ WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT,
++ WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT,
++ WMI_SERVICE_ROAM_SCAN_OFFLOAD,
++ WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC,
++ WMI_SERVICE_EARLY_RX,
++ WMI_SERVICE_STA_SMPS,
++ WMI_SERVICE_FWTEST,
++ WMI_SERVICE_STA_WMMAC,
++ WMI_SERVICE_TDLS,
++ WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE,
++ WMI_SERVICE_ADAPTIVE_OCS,
++ WMI_SERVICE_BA_SSN_SUPPORT,
++ WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
++ WMI_SERVICE_WLAN_HB,
++ WMI_SERVICE_LTE_ANT_SHARE_SUPPORT,
++ WMI_SERVICE_BATCH_SCAN,
++ WMI_SERVICE_QPOWER,
++ WMI_SERVICE_PLMREQ,
++ WMI_SERVICE_THERMAL_MGMT,
++ WMI_SERVICE_RMC,
++ WMI_SERVICE_MHF_OFFLOAD,
++ WMI_SERVICE_COEX_SAR,
++ WMI_SERVICE_BCN_TXRATE_OVERRIDE,
++ WMI_SERVICE_NAN,
++ WMI_SERVICE_L1SS_STAT,
++ WMI_SERVICE_ESTIMATE_LINKSPEED,
++ WMI_SERVICE_OBSS_SCAN,
++ WMI_SERVICE_TDLS_OFFCHAN,
++ WMI_SERVICE_TDLS_UAPSD_BUFFER_STA,
++ WMI_SERVICE_TDLS_UAPSD_SLEEP_STA,
++ WMI_SERVICE_IBSS_PWRSAVE,
++ WMI_SERVICE_LPASS,
++ WMI_SERVICE_EXTSCAN,
++ WMI_SERVICE_D0WOW,
++ WMI_SERVICE_HSOFFLOAD,
++ WMI_SERVICE_ROAM_HO_OFFLOAD,
++ WMI_SERVICE_RX_FULL_REORDER,
++ WMI_SERVICE_DHCP_OFFLOAD,
++ WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
++ WMI_SERVICE_MDNS_OFFLOAD,
++ WMI_SERVICE_SAP_AUTH_OFFLOAD,
++
++ /* keep last */
++ WMI_SERVICE_MAX,
++};
++
++enum wmi_10x_service {
++ WMI_10X_SERVICE_BEACON_OFFLOAD = 0,
++ WMI_10X_SERVICE_SCAN_OFFLOAD,
++ WMI_10X_SERVICE_ROAM_OFFLOAD,
++ WMI_10X_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_10X_SERVICE_STA_PWRSAVE,
++ WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_10X_SERVICE_AP_UAPSD,
++ WMI_10X_SERVICE_AP_DFS,
++ WMI_10X_SERVICE_11AC,
++ WMI_10X_SERVICE_BLOCKACK,
++ WMI_10X_SERVICE_PHYERR,
++ WMI_10X_SERVICE_BCN_FILTER,
++ WMI_10X_SERVICE_RTT,
++ WMI_10X_SERVICE_RATECTRL,
++ WMI_10X_SERVICE_WOW,
++ WMI_10X_SERVICE_RATECTRL_CACHE,
++ WMI_10X_SERVICE_IRAM_TIDS,
++ WMI_10X_SERVICE_BURST,
++
++ /* introduced in 10.2 */
++ WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT,
++ WMI_10X_SERVICE_FORCE_FW_HANG,
++ WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
++};
++
++enum wmi_main_service {
++ WMI_MAIN_SERVICE_BEACON_OFFLOAD = 0,
++ WMI_MAIN_SERVICE_SCAN_OFFLOAD,
++ WMI_MAIN_SERVICE_ROAM_OFFLOAD,
++ WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_MAIN_SERVICE_STA_PWRSAVE,
++ WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_MAIN_SERVICE_AP_UAPSD,
++ WMI_MAIN_SERVICE_AP_DFS,
++ WMI_MAIN_SERVICE_11AC,
++ WMI_MAIN_SERVICE_BLOCKACK,
++ WMI_MAIN_SERVICE_PHYERR,
++ WMI_MAIN_SERVICE_BCN_FILTER,
++ WMI_MAIN_SERVICE_RTT,
++ WMI_MAIN_SERVICE_RATECTRL,
++ WMI_MAIN_SERVICE_WOW,
++ WMI_MAIN_SERVICE_RATECTRL_CACHE,
++ WMI_MAIN_SERVICE_IRAM_TIDS,
++ WMI_MAIN_SERVICE_ARPNS_OFFLOAD,
++ WMI_MAIN_SERVICE_NLO,
++ WMI_MAIN_SERVICE_GTK_OFFLOAD,
++ WMI_MAIN_SERVICE_SCAN_SCH,
++ WMI_MAIN_SERVICE_CSA_OFFLOAD,
++ WMI_MAIN_SERVICE_CHATTER,
++ WMI_MAIN_SERVICE_COEX_FREQAVOID,
++ WMI_MAIN_SERVICE_PACKET_POWER_SAVE,
++ WMI_MAIN_SERVICE_FORCE_FW_HANG,
++ WMI_MAIN_SERVICE_GPIO,
++ WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
++ WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
++ WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
++ WMI_MAIN_SERVICE_STA_KEEP_ALIVE,
++ WMI_MAIN_SERVICE_TX_ENCAP,
+ };
+
+ static inline char *wmi_service_name(int service_id)
+ {
++#define SVCSTR(x) case x: return #x
++
+ switch (service_id) {
+- case WMI_SERVICE_BEACON_OFFLOAD:
+- return "BEACON_OFFLOAD";
+- case WMI_SERVICE_SCAN_OFFLOAD:
+- return "SCAN_OFFLOAD";
+- case WMI_SERVICE_ROAM_OFFLOAD:
+- return "ROAM_OFFLOAD";
+- case WMI_SERVICE_BCN_MISS_OFFLOAD:
+- return "BCN_MISS_OFFLOAD";
+- case WMI_SERVICE_STA_PWRSAVE:
+- return "STA_PWRSAVE";
+- case WMI_SERVICE_STA_ADVANCED_PWRSAVE:
+- return "STA_ADVANCED_PWRSAVE";
+- case WMI_SERVICE_AP_UAPSD:
+- return "AP_UAPSD";
+- case WMI_SERVICE_AP_DFS:
+- return "AP_DFS";
+- case WMI_SERVICE_11AC:
+- return "11AC";
+- case WMI_SERVICE_BLOCKACK:
+- return "BLOCKACK";
+- case WMI_SERVICE_PHYERR:
+- return "PHYERR";
+- case WMI_SERVICE_BCN_FILTER:
+- return "BCN_FILTER";
+- case WMI_SERVICE_RTT:
+- return "RTT";
+- case WMI_SERVICE_RATECTRL:
+- return "RATECTRL";
+- case WMI_SERVICE_WOW:
+- return "WOW";
+- case WMI_SERVICE_RATECTRL_CACHE:
+- return "RATECTRL CACHE";
+- case WMI_SERVICE_IRAM_TIDS:
+- return "IRAM TIDS";
+- case WMI_SERVICE_ARPNS_OFFLOAD:
+- return "ARPNS_OFFLOAD";
+- case WMI_SERVICE_NLO:
+- return "NLO";
+- case WMI_SERVICE_GTK_OFFLOAD:
+- return "GTK_OFFLOAD";
+- case WMI_SERVICE_SCAN_SCH:
+- return "SCAN_SCH";
+- case WMI_SERVICE_CSA_OFFLOAD:
+- return "CSA_OFFLOAD";
+- case WMI_SERVICE_CHATTER:
+- return "CHATTER";
+- case WMI_SERVICE_COEX_FREQAVOID:
+- return "COEX_FREQAVOID";
+- case WMI_SERVICE_PACKET_POWER_SAVE:
+- return "PACKET_POWER_SAVE";
+- case WMI_SERVICE_FORCE_FW_HANG:
+- return "FORCE FW HANG";
+- case WMI_SERVICE_GPIO:
+- return "GPIO";
+- case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM:
+- return "MODULATED DTIM";
+- case WMI_STA_UAPSD_BASIC_AUTO_TRIG:
+- return "BASIC UAPSD";
+- case WMI_STA_UAPSD_VAR_AUTO_TRIG:
+- return "VAR UAPSD";
+- case WMI_SERVICE_STA_KEEP_ALIVE:
+- return "STA KEEP ALIVE";
+- case WMI_SERVICE_TX_ENCAP:
+- return "TX ENCAP";
++ SVCSTR(WMI_SERVICE_BEACON_OFFLOAD);
++ SVCSTR(WMI_SERVICE_SCAN_OFFLOAD);
++ SVCSTR(WMI_SERVICE_ROAM_OFFLOAD);
++ SVCSTR(WMI_SERVICE_BCN_MISS_OFFLOAD);
++ SVCSTR(WMI_SERVICE_STA_PWRSAVE);
++ SVCSTR(WMI_SERVICE_STA_ADVANCED_PWRSAVE);
++ SVCSTR(WMI_SERVICE_AP_UAPSD);
++ SVCSTR(WMI_SERVICE_AP_DFS);
++ SVCSTR(WMI_SERVICE_11AC);
++ SVCSTR(WMI_SERVICE_BLOCKACK);
++ SVCSTR(WMI_SERVICE_PHYERR);
++ SVCSTR(WMI_SERVICE_BCN_FILTER);
++ SVCSTR(WMI_SERVICE_RTT);
++ SVCSTR(WMI_SERVICE_RATECTRL);
++ SVCSTR(WMI_SERVICE_WOW);
++ SVCSTR(WMI_SERVICE_RATECTRL_CACHE);
++ SVCSTR(WMI_SERVICE_IRAM_TIDS);
++ SVCSTR(WMI_SERVICE_ARPNS_OFFLOAD);
++ SVCSTR(WMI_SERVICE_NLO);
++ SVCSTR(WMI_SERVICE_GTK_OFFLOAD);
++ SVCSTR(WMI_SERVICE_SCAN_SCH);
++ SVCSTR(WMI_SERVICE_CSA_OFFLOAD);
++ SVCSTR(WMI_SERVICE_CHATTER);
++ SVCSTR(WMI_SERVICE_COEX_FREQAVOID);
++ SVCSTR(WMI_SERVICE_PACKET_POWER_SAVE);
++ SVCSTR(WMI_SERVICE_FORCE_FW_HANG);
++ SVCSTR(WMI_SERVICE_GPIO);
++ SVCSTR(WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM);
++ SVCSTR(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG);
++ SVCSTR(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG);
++ SVCSTR(WMI_SERVICE_STA_KEEP_ALIVE);
++ SVCSTR(WMI_SERVICE_TX_ENCAP);
++ SVCSTR(WMI_SERVICE_BURST);
++ SVCSTR(WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT);
++ SVCSTR(WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT);
++ SVCSTR(WMI_SERVICE_ROAM_SCAN_OFFLOAD);
++ SVCSTR(WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC);
++ SVCSTR(WMI_SERVICE_EARLY_RX);
++ SVCSTR(WMI_SERVICE_STA_SMPS);
++ SVCSTR(WMI_SERVICE_FWTEST);
++ SVCSTR(WMI_SERVICE_STA_WMMAC);
++ SVCSTR(WMI_SERVICE_TDLS);
++ SVCSTR(WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE);
++ SVCSTR(WMI_SERVICE_ADAPTIVE_OCS);
++ SVCSTR(WMI_SERVICE_BA_SSN_SUPPORT);
++ SVCSTR(WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE);
++ SVCSTR(WMI_SERVICE_WLAN_HB);
++ SVCSTR(WMI_SERVICE_LTE_ANT_SHARE_SUPPORT);
++ SVCSTR(WMI_SERVICE_BATCH_SCAN);
++ SVCSTR(WMI_SERVICE_QPOWER);
++ SVCSTR(WMI_SERVICE_PLMREQ);
++ SVCSTR(WMI_SERVICE_THERMAL_MGMT);
++ SVCSTR(WMI_SERVICE_RMC);
++ SVCSTR(WMI_SERVICE_MHF_OFFLOAD);
++ SVCSTR(WMI_SERVICE_COEX_SAR);
++ SVCSTR(WMI_SERVICE_BCN_TXRATE_OVERRIDE);
++ SVCSTR(WMI_SERVICE_NAN);
++ SVCSTR(WMI_SERVICE_L1SS_STAT);
++ SVCSTR(WMI_SERVICE_ESTIMATE_LINKSPEED);
++ SVCSTR(WMI_SERVICE_OBSS_SCAN);
++ SVCSTR(WMI_SERVICE_TDLS_OFFCHAN);
++ SVCSTR(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA);
++ SVCSTR(WMI_SERVICE_TDLS_UAPSD_SLEEP_STA);
++ SVCSTR(WMI_SERVICE_IBSS_PWRSAVE);
++ SVCSTR(WMI_SERVICE_LPASS);
++ SVCSTR(WMI_SERVICE_EXTSCAN);
++ SVCSTR(WMI_SERVICE_D0WOW);
++ SVCSTR(WMI_SERVICE_HSOFFLOAD);
++ SVCSTR(WMI_SERVICE_ROAM_HO_OFFLOAD);
++ SVCSTR(WMI_SERVICE_RX_FULL_REORDER);
++ SVCSTR(WMI_SERVICE_DHCP_OFFLOAD);
++ SVCSTR(WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT);
++ SVCSTR(WMI_SERVICE_MDNS_OFFLOAD);
++ SVCSTR(WMI_SERVICE_SAP_AUTH_OFFLOAD);
+ default:
+- return "UNKNOWN SERVICE\n";
++ return NULL;
+ }
++
++#undef SVCSTR
+ }
+
++#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
++ ((svc_id) < (len) && \
++ __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
++ BIT((svc_id)%(sizeof(u32))))
++
++#define SVCMAP(x, y, len) \
++ do { \
++ if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
++ __set_bit(y, out); \
++ } while (0)
++
++static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
++ size_t len)
++{
++ SVCMAP(WMI_10X_SERVICE_BEACON_OFFLOAD,
++ WMI_SERVICE_BEACON_OFFLOAD, len);
++ SVCMAP(WMI_10X_SERVICE_SCAN_OFFLOAD,
++ WMI_SERVICE_SCAN_OFFLOAD, len);
++ SVCMAP(WMI_10X_SERVICE_ROAM_OFFLOAD,
++ WMI_SERVICE_ROAM_OFFLOAD, len);
++ SVCMAP(WMI_10X_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
++ SVCMAP(WMI_10X_SERVICE_STA_PWRSAVE,
++ WMI_SERVICE_STA_PWRSAVE, len);
++ SVCMAP(WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
++ SVCMAP(WMI_10X_SERVICE_AP_UAPSD,
++ WMI_SERVICE_AP_UAPSD, len);
++ SVCMAP(WMI_10X_SERVICE_AP_DFS,
++ WMI_SERVICE_AP_DFS, len);
++ SVCMAP(WMI_10X_SERVICE_11AC,
++ WMI_SERVICE_11AC, len);
++ SVCMAP(WMI_10X_SERVICE_BLOCKACK,
++ WMI_SERVICE_BLOCKACK, len);
++ SVCMAP(WMI_10X_SERVICE_PHYERR,
++ WMI_SERVICE_PHYERR, len);
++ SVCMAP(WMI_10X_SERVICE_BCN_FILTER,
++ WMI_SERVICE_BCN_FILTER, len);
++ SVCMAP(WMI_10X_SERVICE_RTT,
++ WMI_SERVICE_RTT, len);
++ SVCMAP(WMI_10X_SERVICE_RATECTRL,
++ WMI_SERVICE_RATECTRL, len);
++ SVCMAP(WMI_10X_SERVICE_WOW,
++ WMI_SERVICE_WOW, len);
++ SVCMAP(WMI_10X_SERVICE_RATECTRL_CACHE,
++ WMI_SERVICE_RATECTRL_CACHE, len);
++ SVCMAP(WMI_10X_SERVICE_IRAM_TIDS,
++ WMI_SERVICE_IRAM_TIDS, len);
++ SVCMAP(WMI_10X_SERVICE_BURST,
++ WMI_SERVICE_BURST, len);
++ SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT,
++ WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, len);
++ SVCMAP(WMI_10X_SERVICE_FORCE_FW_HANG,
++ WMI_SERVICE_FORCE_FW_HANG, len);
++ SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
++ WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, len);
++}
++
++static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
++ size_t len)
++{
++ SVCMAP(WMI_MAIN_SERVICE_BEACON_OFFLOAD,
++ WMI_SERVICE_BEACON_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_SCAN_OFFLOAD,
++ WMI_SERVICE_SCAN_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_ROAM_OFFLOAD,
++ WMI_SERVICE_ROAM_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_STA_PWRSAVE,
++ WMI_SERVICE_STA_PWRSAVE, len);
++ SVCMAP(WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
++ SVCMAP(WMI_MAIN_SERVICE_AP_UAPSD,
++ WMI_SERVICE_AP_UAPSD, len);
++ SVCMAP(WMI_MAIN_SERVICE_AP_DFS,
++ WMI_SERVICE_AP_DFS, len);
++ SVCMAP(WMI_MAIN_SERVICE_11AC,
++ WMI_SERVICE_11AC, len);
++ SVCMAP(WMI_MAIN_SERVICE_BLOCKACK,
++ WMI_SERVICE_BLOCKACK, len);
++ SVCMAP(WMI_MAIN_SERVICE_PHYERR,
++ WMI_SERVICE_PHYERR, len);
++ SVCMAP(WMI_MAIN_SERVICE_BCN_FILTER,
++ WMI_SERVICE_BCN_FILTER, len);
++ SVCMAP(WMI_MAIN_SERVICE_RTT,
++ WMI_SERVICE_RTT, len);
++ SVCMAP(WMI_MAIN_SERVICE_RATECTRL,
++ WMI_SERVICE_RATECTRL, len);
++ SVCMAP(WMI_MAIN_SERVICE_WOW,
++ WMI_SERVICE_WOW, len);
++ SVCMAP(WMI_MAIN_SERVICE_RATECTRL_CACHE,
++ WMI_SERVICE_RATECTRL_CACHE, len);
++ SVCMAP(WMI_MAIN_SERVICE_IRAM_TIDS,
++ WMI_SERVICE_IRAM_TIDS, len);
++ SVCMAP(WMI_MAIN_SERVICE_ARPNS_OFFLOAD,
++ WMI_SERVICE_ARPNS_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_NLO,
++ WMI_SERVICE_NLO, len);
++ SVCMAP(WMI_MAIN_SERVICE_GTK_OFFLOAD,
++ WMI_SERVICE_GTK_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_SCAN_SCH,
++ WMI_SERVICE_SCAN_SCH, len);
++ SVCMAP(WMI_MAIN_SERVICE_CSA_OFFLOAD,
++ WMI_SERVICE_CSA_OFFLOAD, len);
++ SVCMAP(WMI_MAIN_SERVICE_CHATTER,
++ WMI_SERVICE_CHATTER, len);
++ SVCMAP(WMI_MAIN_SERVICE_COEX_FREQAVOID,
++ WMI_SERVICE_COEX_FREQAVOID, len);
++ SVCMAP(WMI_MAIN_SERVICE_PACKET_POWER_SAVE,
++ WMI_SERVICE_PACKET_POWER_SAVE, len);
++ SVCMAP(WMI_MAIN_SERVICE_FORCE_FW_HANG,
++ WMI_SERVICE_FORCE_FW_HANG, len);
++ SVCMAP(WMI_MAIN_SERVICE_GPIO,
++ WMI_SERVICE_GPIO, len);
++ SVCMAP(WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
++ WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
++ SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
++ WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
++ SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
++ WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
++ SVCMAP(WMI_MAIN_SERVICE_STA_KEEP_ALIVE,
++ WMI_SERVICE_STA_KEEP_ALIVE, len);
++ SVCMAP(WMI_MAIN_SERVICE_TX_ENCAP,
++ WMI_SERVICE_TX_ENCAP, len);
++}
+
+-#define WMI_SERVICE_BM_SIZE \
+- ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32))
++#undef SVCMAP
+
+ /* 2 word representation of MAC addr */
+ struct wmi_mac_addr {
+@@ -308,6 +550,8 @@ struct wmi_cmd_map {
+ u32 force_fw_hang_cmdid;
+ u32 gpio_config_cmdid;
+ u32 gpio_output_cmdid;
++ u32 pdev_get_temperature_cmdid;
++ u32 vdev_set_wmm_params_cmdid;
+ };
+
+ /*
+@@ -803,6 +1047,166 @@ enum wmi_10x_event_id {
+ WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1,
+ };
+
++enum wmi_10_2_cmd_id {
++ WMI_10_2_START_CMDID = 0x9000,
++ WMI_10_2_END_CMDID = 0x9FFF,
++ WMI_10_2_INIT_CMDID,
++ WMI_10_2_START_SCAN_CMDID = WMI_10_2_START_CMDID,
++ WMI_10_2_STOP_SCAN_CMDID,
++ WMI_10_2_SCAN_CHAN_LIST_CMDID,
++ WMI_10_2_ECHO_CMDID,
++ WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
++ WMI_10_2_PDEV_SET_CHANNEL_CMDID,
++ WMI_10_2_PDEV_SET_PARAM_CMDID,
++ WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID,
++ WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID,
++ WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID,
++ WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID,
++ WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID,
++ WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID,
++ WMI_10_2_PDEV_SET_QUIET_MODE_CMDID,
++ WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID,
++ WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID,
++ WMI_10_2_VDEV_CREATE_CMDID,
++ WMI_10_2_VDEV_DELETE_CMDID,
++ WMI_10_2_VDEV_START_REQUEST_CMDID,
++ WMI_10_2_VDEV_RESTART_REQUEST_CMDID,
++ WMI_10_2_VDEV_UP_CMDID,
++ WMI_10_2_VDEV_STOP_CMDID,
++ WMI_10_2_VDEV_DOWN_CMDID,
++ WMI_10_2_VDEV_STANDBY_RESPONSE_CMDID,
++ WMI_10_2_VDEV_RESUME_RESPONSE_CMDID,
++ WMI_10_2_VDEV_SET_PARAM_CMDID,
++ WMI_10_2_VDEV_INSTALL_KEY_CMDID,
++ WMI_10_2_VDEV_SET_DSCP_TID_MAP_CMDID,
++ WMI_10_2_PEER_CREATE_CMDID,
++ WMI_10_2_PEER_DELETE_CMDID,
++ WMI_10_2_PEER_FLUSH_TIDS_CMDID,
++ WMI_10_2_PEER_SET_PARAM_CMDID,
++ WMI_10_2_PEER_ASSOC_CMDID,
++ WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID,
++ WMI_10_2_PEER_UPDATE_WDS_ENTRY_CMDID,
++ WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID,
++ WMI_10_2_PEER_MCAST_GROUP_CMDID,
++ WMI_10_2_BCN_TX_CMDID,
++ WMI_10_2_BCN_PRB_TMPL_CMDID,
++ WMI_10_2_BCN_FILTER_RX_CMDID,
++ WMI_10_2_PRB_REQ_FILTER_RX_CMDID,
++ WMI_10_2_MGMT_TX_CMDID,
++ WMI_10_2_ADDBA_CLEAR_RESP_CMDID,
++ WMI_10_2_ADDBA_SEND_CMDID,
++ WMI_10_2_ADDBA_STATUS_CMDID,
++ WMI_10_2_DELBA_SEND_CMDID,
++ WMI_10_2_ADDBA_SET_RESP_CMDID,
++ WMI_10_2_SEND_SINGLEAMSDU_CMDID,
++ WMI_10_2_STA_POWERSAVE_MODE_CMDID,
++ WMI_10_2_STA_POWERSAVE_PARAM_CMDID,
++ WMI_10_2_STA_MIMO_PS_MODE_CMDID,
++ WMI_10_2_DBGLOG_CFG_CMDID,
++ WMI_10_2_PDEV_DFS_ENABLE_CMDID,
++ WMI_10_2_PDEV_DFS_DISABLE_CMDID,
++ WMI_10_2_PDEV_QVIT_CMDID,
++ WMI_10_2_ROAM_SCAN_MODE,
++ WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD,
++ WMI_10_2_ROAM_SCAN_PERIOD,
++ WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
++ WMI_10_2_ROAM_AP_PROFILE,
++ WMI_10_2_OFL_SCAN_ADD_AP_PROFILE,
++ WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE,
++ WMI_10_2_OFL_SCAN_PERIOD,
++ WMI_10_2_P2P_DEV_SET_DEVICE_INFO,
++ WMI_10_2_P2P_DEV_SET_DISCOVERABILITY,
++ WMI_10_2_P2P_GO_SET_BEACON_IE,
++ WMI_10_2_P2P_GO_SET_PROBE_RESP_IE,
++ WMI_10_2_AP_PS_PEER_PARAM_CMDID,
++ WMI_10_2_AP_PS_PEER_UAPSD_COEX_CMDID,
++ WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID,
++ WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID,
++ WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
++ WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
++ WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
++ WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
++ WMI_10_2_PDEV_SUSPEND_CMDID,
++ WMI_10_2_PDEV_RESUME_CMDID,
++ WMI_10_2_ADD_BCN_FILTER_CMDID,
++ WMI_10_2_RMV_BCN_FILTER_CMDID,
++ WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID,
++ WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID,
++ WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
++ WMI_10_2_WOW_ENABLE_CMDID,
++ WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
++ WMI_10_2_RTT_MEASREQ_CMDID,
++ WMI_10_2_RTT_TSF_CMDID,
++ WMI_10_2_RTT_KEEPALIVE_CMDID,
++ WMI_10_2_PDEV_SEND_BCN_CMDID,
++ WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
++ WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
++ WMI_10_2_REQUEST_STATS_CMDID,
++ WMI_10_2_GPIO_CONFIG_CMDID,
++ WMI_10_2_GPIO_OUTPUT_CMDID,
++ WMI_10_2_VDEV_RATEMASK_CMDID,
++ WMI_10_2_PDEV_SMART_ANT_ENABLE_CMDID,
++ WMI_10_2_PDEV_SMART_ANT_SET_RX_ANTENNA_CMDID,
++ WMI_10_2_PEER_SMART_ANT_SET_TX_ANTENNA_CMDID,
++ WMI_10_2_PEER_SMART_ANT_SET_TRAIN_INFO_CMDID,
++ WMI_10_2_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMDID,
++ WMI_10_2_FORCE_FW_HANG_CMDID,
++ WMI_10_2_PDEV_SET_ANTENNA_SWITCH_TABLE_CMDID,
++ WMI_10_2_PDEV_SET_CTL_TABLE_CMDID,
++ WMI_10_2_PDEV_SET_MIMOGAIN_TABLE_CMDID,
++ WMI_10_2_PDEV_RATEPWR_TABLE_CMDID,
++ WMI_10_2_PDEV_RATEPWR_CHAINMSK_TABLE_CMDID,
++ WMI_10_2_PDEV_GET_INFO,
++ WMI_10_2_VDEV_GET_INFO,
++ WMI_10_2_VDEV_ATF_REQUEST_CMDID,
++ WMI_10_2_PEER_ATF_REQUEST_CMDID,
++ WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
++ WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1,
++};
++
++enum wmi_10_2_event_id {
++ WMI_10_2_SERVICE_READY_EVENTID = 0x8000,
++ WMI_10_2_READY_EVENTID,
++ WMI_10_2_DEBUG_MESG_EVENTID,
++ WMI_10_2_START_EVENTID = 0x9000,
++ WMI_10_2_END_EVENTID = 0x9FFF,
++ WMI_10_2_SCAN_EVENTID = WMI_10_2_START_EVENTID,
++ WMI_10_2_ECHO_EVENTID,
++ WMI_10_2_UPDATE_STATS_EVENTID,
++ WMI_10_2_INST_RSSI_STATS_EVENTID,
++ WMI_10_2_VDEV_START_RESP_EVENTID,
++ WMI_10_2_VDEV_STANDBY_REQ_EVENTID,
++ WMI_10_2_VDEV_RESUME_REQ_EVENTID,
++ WMI_10_2_VDEV_STOPPED_EVENTID,
++ WMI_10_2_PEER_STA_KICKOUT_EVENTID,
++ WMI_10_2_HOST_SWBA_EVENTID,
++ WMI_10_2_TBTTOFFSET_UPDATE_EVENTID,
++ WMI_10_2_MGMT_RX_EVENTID,
++ WMI_10_2_CHAN_INFO_EVENTID,
++ WMI_10_2_PHYERR_EVENTID,
++ WMI_10_2_ROAM_EVENTID,
++ WMI_10_2_PROFILE_MATCH,
++ WMI_10_2_DEBUG_PRINT_EVENTID,
++ WMI_10_2_PDEV_QVIT_EVENTID,
++ WMI_10_2_WLAN_PROFILE_DATA_EVENTID,
++ WMI_10_2_RTT_MEASUREMENT_REPORT_EVENTID,
++ WMI_10_2_TSF_MEASUREMENT_REPORT_EVENTID,
++ WMI_10_2_RTT_ERROR_REPORT_EVENTID,
++ WMI_10_2_RTT_KEEPALIVE_EVENTID,
++ WMI_10_2_WOW_WAKEUP_HOST_EVENTID,
++ WMI_10_2_DCS_INTERFERENCE_EVENTID,
++ WMI_10_2_PDEV_TPC_CONFIG_EVENTID,
++ WMI_10_2_GPIO_INPUT_EVENTID,
++ WMI_10_2_PEER_RATECODE_LIST_EVENTID,
++ WMI_10_2_GENERIC_BUFFER_EVENTID,
++ WMI_10_2_MCAST_BUF_RELEASE_EVENTID,
++ WMI_10_2_MCAST_LIST_AGEOUT_EVENTID,
++ WMI_10_2_WDS_PEER_EVENTID,
++ WMI_10_2_PEER_STA_PS_STATECHG_EVENTID,
++ WMI_10_2_PDEV_TEMPERATURE_EVENTID,
++ WMI_10_2_PDEV_UTF_EVENTID = WMI_10_2_END_EVENTID - 1,
++};
++
+ enum wmi_phy_mode {
+ MODE_11A = 0, /* 11a Mode */
+ MODE_11G = 1, /* 11b/g Mode */
+@@ -955,7 +1359,6 @@ enum wmi_channel_change_cause {
+ WMI_HT_CAP_RX_STBC | \
+ WMI_HT_CAP_LDPC)
+
+-
+ /*
+ * WMI_VHT_CAP_* these maps to ieee 802.11ac vht capability information
+ * field. The fields not defined here are not supported, or reserved.
+@@ -1076,10 +1479,6 @@ struct wlan_host_mem_req {
+ __le32 num_units;
+ } __packed;
+
+-#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
+- ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+- (1 << ((svc_id)%(sizeof(u32))))) != 0)
+-
+ /*
+ * The following struct holds optional payload for
+ * wmi_service_ready_event,e.g., 11ac pass some of the
+@@ -1093,7 +1492,7 @@ struct wmi_service_ready_event {
+ __le32 phy_capability;
+ /* Maximum number of frag table entries that SW will populate less 1 */
+ __le32 max_frag_entry;
+- __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
++ __le32 wmi_service_bitmap[16];
+ __le32 num_rf_chains;
+ /*
+ * The following field is only valid for service type
+@@ -1119,11 +1518,11 @@ struct wmi_service_ready_event {
+ * where FW can access this memory directly (or) by DMA.
+ */
+ __le32 num_mem_reqs;
+- struct wlan_host_mem_req mem_reqs[1];
++ struct wlan_host_mem_req mem_reqs[0];
+ } __packed;
+
+ /* This is the definition from 10.X firmware branch */
+-struct wmi_service_ready_event_10x {
++struct wmi_10x_service_ready_event {
+ __le32 sw_version;
+ __le32 abi_version;
+
+@@ -1132,7 +1531,7 @@ struct wmi_service_ready_event_10x {
+
+ /* Maximum number of frag table entries that SW will populate less 1 */
+ __le32 max_frag_entry;
+- __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
++ __le32 wmi_service_bitmap[16];
+ __le32 num_rf_chains;
+
+ /*
+@@ -1158,10 +1557,9 @@ struct wmi_service_ready_event_10x {
+ */
+ __le32 num_mem_reqs;
+
+- struct wlan_host_mem_req mem_reqs[1];
++ struct wlan_host_mem_req mem_reqs[0];
+ } __packed;
+
+-
+ #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
+ #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
+
+@@ -1255,7 +1653,7 @@ struct wmi_resource_config {
+ */
+ __le32 rx_decap_mode;
+
+- /* what is the maximum scan requests than can be queued */
++ /* what is the maximum number of scan requests that can be queued */
+ __le32 scan_max_pending_reqs;
+
+ /* maximum VDEV that could use BMISS offload */
+@@ -1440,7 +1838,7 @@ struct wmi_resource_config_10x {
+ */
+ __le32 rx_decap_mode;
+
+- /* what is the maximum scan requests than can be queued */
++ /* what is the maximum number of scan requests that can be queued */
+ __le32 scan_max_pending_reqs;
+
+ /* maximum VDEV that could use BMISS offload */
+@@ -1551,6 +1949,21 @@ struct wmi_resource_config_10x {
+ __le32 max_frag_entries;
+ } __packed;
+
++enum wmi_10_2_feature_mask {
++ WMI_10_2_RX_BATCH_MODE = BIT(0),
++ WMI_10_2_ATF_CONFIG = BIT(1),
++};
++
++struct wmi_resource_config_10_2 {
++ struct wmi_resource_config_10x common;
++ __le32 max_peer_ext_stats;
++ __le32 smart_ant_cap; /* 0-disable, 1-enable */
++ __le32 bk_min_free;
++ __le32 be_min_free;
++ __le32 vi_min_free;
++ __le32 vo_min_free;
++ __le32 feature_mask;
++} __packed;
+
+ #define NUM_UNITS_IS_NUM_VDEVS 0x1
+ #define NUM_UNITS_IS_NUM_PEERS 0x2
+@@ -1565,34 +1978,39 @@ struct host_memory_chunk {
+ __le32 size;
+ } __packed;
+
++struct wmi_host_mem_chunks {
++ __le32 count;
++ /* some fw revisions require at least 1 chunk regardless of count */
++ struct host_memory_chunk items[1];
++} __packed;
++
+ struct wmi_init_cmd {
+ struct wmi_resource_config resource_config;
+- __le32 num_host_mem_chunks;
+-
+- /*
+- * variable number of host memory chunks.
+- * This should be the last element in the structure
+- */
+- struct host_memory_chunk host_mem_chunks[1];
++ struct wmi_host_mem_chunks mem_chunks;
+ } __packed;
+
+ /* _10x stucture is from 10.X FW API */
+ struct wmi_init_cmd_10x {
+ struct wmi_resource_config_10x resource_config;
+- __le32 num_host_mem_chunks;
++ struct wmi_host_mem_chunks mem_chunks;
++} __packed;
+
+- /*
+- * variable number of host memory chunks.
+- * This should be the last element in the structure
+- */
+- struct host_memory_chunk host_mem_chunks[1];
++struct wmi_init_cmd_10_2 {
++ struct wmi_resource_config_10_2 resource_config;
++ struct wmi_host_mem_chunks mem_chunks;
++} __packed;
++
++struct wmi_chan_list_entry {
++ __le16 freq;
++ u8 phy_mode; /* valid for 10.2 only */
++ u8 reserved;
+ } __packed;
+
+ /* TLV for channel list */
+ struct wmi_chan_list {
+ __le32 tag; /* WMI_CHAN_LIST_TAG */
+ __le32 num_chan;
+- __le32 channel_list[0];
++ struct wmi_chan_list_entry channel_list[0];
+ } __packed;
+
+ struct wmi_bssid_list {
+@@ -1629,6 +2047,11 @@ struct wmi_ssid_list {
+ #define WLAN_SCAN_PARAMS_MAX_BSSID 4
+ #define WLAN_SCAN_PARAMS_MAX_IE_LEN 256
+
++/* Values lower than this may be refused by some firmware revisions with a scan
++ * completion with a timedout reason.
++ */
++#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40
++
+ /* Scan priority numbers must be sequential, starting with 0 */
+ enum wmi_scan_priority {
+ WMI_SCAN_PRIORITY_VERY_LOW = 0,
+@@ -1639,7 +2062,7 @@ enum wmi_scan_priority {
+ WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */
+ };
+
+-struct wmi_start_scan_cmd {
++struct wmi_start_scan_common {
+ /* Scan ID */
+ __le32 scan_id;
+ /* Scan requestor ID */
+@@ -1697,97 +2120,26 @@ struct wmi_start_scan_cmd {
+ __le32 probe_delay;
+ /* Scan control flags */
+ __le32 scan_ctrl_flags;
+-
+- /* Burst duration time in msecs */
+- __le32 burst_duration;
+- /*
+- * TLV (tag length value ) paramerters follow the scan_cmd structure.
+- * TLV can contain channel list, bssid list, ssid list and
+- * ie. the TLV tags are defined above;
+- */
+ } __packed;
+
+-/* This is the definition from 10.X firmware branch */
+-struct wmi_start_scan_cmd_10x {
+- /* Scan ID */
+- __le32 scan_id;
+-
+- /* Scan requestor ID */
+- __le32 scan_req_id;
+-
+- /* VDEV id(interface) that is requesting scan */
+- __le32 vdev_id;
+-
+- /* Scan Priority, input to scan scheduler */
+- __le32 scan_priority;
+-
+- /* Scan events subscription */
+- __le32 notify_scan_events;
+-
+- /* dwell time in msec on active channels */
+- __le32 dwell_time_active;
+-
+- /* dwell time in msec on passive channels */
+- __le32 dwell_time_passive;
+-
+- /*
+- * min time in msec on the BSS channel,only valid if atleast one
+- * VDEV is active
+- */
+- __le32 min_rest_time;
+-
+- /*
+- * max rest time in msec on the BSS channel,only valid if at least
+- * one VDEV is active
+- */
+- /*
+- * the scanner will rest on the bss channel at least min_rest_time
+- * after min_rest_time the scanner will start checking for tx/rx
+- * activity on all VDEVs. if there is no activity the scanner will
+- * switch to off channel. if there is activity the scanner will let
+- * the radio on the bss channel until max_rest_time expires.at
+- * max_rest_time scanner will switch to off channel irrespective of
+- * activity. activity is determined by the idle_time parameter.
+- */
+- __le32 max_rest_time;
+-
+- /*
+- * time before sending next set of probe requests.
+- * The scanner keeps repeating probe requests transmission with
+- * period specified by repeat_probe_time.
+- * The number of probe requests specified depends on the ssid_list
+- * and bssid_list
+- */
+- __le32 repeat_probe_time;
+-
+- /* time in msec between 2 consequetive probe requests with in a set. */
+- __le32 probe_spacing_time;
+-
+- /*
+- * data inactivity time in msec on bss channel that will be used by
+- * scanner for measuring the inactivity.
++struct wmi_start_scan_tlvs {
++ /* TLV parameters. These includes channel list, ssid list, bssid list,
++ * extra ies.
+ */
+- __le32 idle_time;
+-
+- /* maximum time in msec allowed for scan */
+- __le32 max_scan_time;
+-
+- /*
+- * delay in msec before sending first probe request after switching
+- * to a channel
+- */
+- __le32 probe_delay;
+-
+- /* Scan control flags */
+- __le32 scan_ctrl_flags;
++ u8 tlvs[0];
++} __packed;
+
+- /*
+- * TLV (tag length value ) paramerters follow the scan_cmd structure.
+- * TLV can contain channel list, bssid list, ssid list and
+- * ie. the TLV tags are defined above;
+- */
++struct wmi_start_scan_cmd {
++ struct wmi_start_scan_common common;
++ __le32 burst_duration_ms;
++ struct wmi_start_scan_tlvs tlvs;
+ } __packed;
+
++/* This is the definition from 10.X firmware branch */
++struct wmi_10x_start_scan_cmd {
++ struct wmi_start_scan_common common;
++ struct wmi_start_scan_tlvs tlvs;
++} __packed;
+
+ struct wmi_ssid_arg {
+ int len;
+@@ -1821,7 +2173,7 @@ struct wmi_start_scan_arg {
+ u32 n_bssids;
+
+ u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN];
+- u32 channels[64];
++ u16 channels[64];
+ struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID];
+ struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID];
+ };
+@@ -1849,7 +2201,6 @@ struct wmi_start_scan_arg {
+ /* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */
+ #define WMI_SCAN_CLASS_MASK 0xFF000000
+
+-
+ enum wmi_stop_scan_type {
+ WMI_SCAN_STOP_ONE = 0x00000000, /* stop by scan_id */
+ WMI_SCAN_STOP_VDEV_ALL = 0x01000000, /* stop by vdev_id */
+@@ -1973,100 +2324,31 @@ struct wmi_mgmt_rx_event_v2 {
+ #define PHY_ERROR_FALSE_RADAR_EXT 0x24
+ #define PHY_ERROR_RADAR 0x05
+
+-struct wmi_single_phyerr_rx_hdr {
+- /* TSF timestamp */
++struct wmi_phyerr {
+ __le32 tsf_timestamp;
+-
+- /*
+- * Current freq1, freq2
+- *
+- * [7:0]: freq1[lo]
+- * [15:8] : freq1[hi]
+- * [23:16]: freq2[lo]
+- * [31:24]: freq2[hi]
+- */
+ __le16 freq1;
+ __le16 freq2;
+-
+- /*
+- * Combined RSSI over all chains and channel width for this PHY error
+- *
+- * [7:0]: RSSI combined
+- * [15:8]: Channel width (MHz)
+- * [23:16]: PHY error code
+- * [24:16]: reserved (future use)
+- */
+ u8 rssi_combined;
+ u8 chan_width_mhz;
+ u8 phy_err_code;
+ u8 rsvd0;
+-
+- /*
+- * RSSI on chain 0 through 3
+- *
+- * This is formatted the same as the PPDU_START RX descriptor
+- * field:
+- *
+- * [7:0]: pri20
+- * [15:8]: sec20
+- * [23:16]: sec40
+- * [31:24]: sec80
+- */
+-
+- __le32 rssi_chain0;
+- __le32 rssi_chain1;
+- __le32 rssi_chain2;
+- __le32 rssi_chain3;
+-
+- /*
+- * Last calibrated NF value for chain 0 through 3
+- *
+- * nf_list_1:
+- *
+- * + [15:0] - chain 0
+- * + [31:16] - chain 1
+- *
+- * nf_list_2:
+- *
+- * + [15:0] - chain 2
+- * + [31:16] - chain 3
+- */
+- __le32 nf_list_1;
+- __le32 nf_list_2;
+-
+-
+- /* Length of the frame */
++ __le32 rssi_chains[4];
++ __le16 nf_chains[4];
+ __le32 buf_len;
++ u8 buf[0];
+ } __packed;
+
+-struct wmi_single_phyerr_rx_event {
+- /* Phy error event header */
+- struct wmi_single_phyerr_rx_hdr hdr;
+- /* frame buffer */
+- u8 bufp[0];
+-} __packed;
+-
+-struct wmi_comb_phyerr_rx_hdr {
+- /* Phy error phy error count */
+- __le32 num_phyerr_events;
++struct wmi_phyerr_event {
++ __le32 num_phyerrs;
+ __le32 tsf_l32;
+ __le32 tsf_u32;
+-} __packed;
+-
+-struct wmi_comb_phyerr_rx_event {
+- /* Phy error phy error count */
+- struct wmi_comb_phyerr_rx_hdr hdr;
+- /*
+- * frame buffer - contains multiple payloads in the order:
+- * header - payload, header - payload...
+- * (The header is of type: wmi_single_phyerr_rx_hdr)
+- */
+- u8 bufp[0];
++ struct wmi_phyerr phyerrs[0];
+ } __packed;
+
+ #define PHYERR_TLV_SIG 0xBB
+ #define PHYERR_TLV_TAG_SEARCH_FFT_REPORT 0xFB
+ #define PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY 0xF8
++#define PHYERR_TLV_TAG_SPECTRAL_SUMMARY_REPORT 0xF9
+
+ struct phyerr_radar_report {
+ __le32 reg0; /* RADAR_REPORT_REG0_* */
+@@ -2135,7 +2417,6 @@ struct phyerr_fft_report {
+ #define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_MASK 0x000000FF
+ #define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_LSB 0
+
+-
+ struct phyerr_tlv {
+ __le16 len;
+ u8 tag;
+@@ -2166,7 +2447,6 @@ struct wmi_echo_cmd {
+ __le32 value;
+ } __packed;
+
+-
+ struct wmi_pdev_set_regdomain_cmd {
+ __le32 reg_domain;
+ __le32 reg_domain_2G;
+@@ -2215,7 +2495,6 @@ struct wmi_pdev_set_quiet_cmd {
+ __le32 enabled;
+ } __packed;
+
+-
+ /*
+ * 802.11g protection mode.
+ */
+@@ -2318,14 +2597,15 @@ struct wmi_pdev_param_map {
+ u32 fast_channel_reset;
+ u32 burst_dur;
+ u32 burst_enable;
++ u32 cal_period;
+ };
+
+ #define WMI_PDEV_PARAM_UNSUPPORTED 0
+
+ enum wmi_pdev_param {
+- /* TX chian mask */
++ /* TX chain mask */
+ WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+- /* RX chian mask */
++ /* RX chain mask */
+ WMI_PDEV_PARAM_RX_CHAIN_MASK,
+ /* TX power limit for 2G Radio */
+ WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+@@ -2515,6 +2795,22 @@ enum wmi_10x_pdev_param {
+ WMI_10X_PDEV_PARAM_BURST_DUR,
+ /* Set Bursting Enable*/
+ WMI_10X_PDEV_PARAM_BURST_ENABLE,
++
++ /* following are available as of firmware 10.2 */
++ WMI_10X_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA,
++ WMI_10X_PDEV_PARAM_IGMPMLD_OVERRIDE,
++ WMI_10X_PDEV_PARAM_IGMPMLD_TID,
++ WMI_10X_PDEV_PARAM_ANTENNA_GAIN,
++ WMI_10X_PDEV_PARAM_RX_DECAP_MODE,
++ WMI_10X_PDEV_PARAM_RX_FILTER,
++ WMI_10X_PDEV_PARAM_SET_MCAST_TO_UCAST_TID,
++ WMI_10X_PDEV_PARAM_PROXY_STA_MODE,
++ WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_MODE,
++ WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_BUFFER,
++ WMI_10X_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER,
++ WMI_10X_PDEV_PARAM_PEER_STA_PS_STATECHG_ENABLE,
++ WMI_10X_PDEV_PARAM_RTS_FIXED_RATE,
++ WMI_10X_PDEV_PARAM_CAL_PERIOD
+ };
+
+ struct wmi_pdev_set_param_cmd {
+@@ -2522,6 +2818,9 @@ struct wmi_pdev_set_param_cmd {
+ __le32 param_value;
+ } __packed;
+
++/* valid period is 1 ~ 60000ms, unit in millisecond */
++#define WMI_PDEV_PARAM_CAL_PERIOD_MAX 60000
++
+ struct wmi_pdev_get_tpc_config_cmd {
+ /* parameter */
+ __le32 param;
+@@ -2565,11 +2864,6 @@ enum wmi_tp_scale {
+ WMI_TP_SCALE_SIZE = 5, /* max num of enum */
+ };
+
+-struct wmi_set_channel_cmd {
+- /* channel (only frequency and mode info are used) */
+- struct wmi_channel chan;
+-} __packed;
+-
+ struct wmi_pdev_chanlist_update_event {
+ /* number of channels */
+ __le32 num_chan;
+@@ -2600,6 +2894,10 @@ struct wmi_pdev_set_channel_cmd {
+ struct wmi_channel chan;
+ } __packed;
+
++struct wmi_pdev_pktlog_enable_cmd {
++ __le32 ev_bitmap;
++} __packed;
++
+ /* Customize the DSCP (bit) to TID (0-7) mapping for QOS */
+ #define WMI_DSCP_MAP_MAX (64)
+ struct wmi_pdev_set_dscp_tid_map_cmd {
+@@ -2642,14 +2940,14 @@ struct wmi_wmm_params_arg {
+ u32 no_ack;
+ };
+
+-struct wmi_pdev_set_wmm_params_arg {
++struct wmi_wmm_params_all_arg {
+ struct wmi_wmm_params_arg ac_be;
+ struct wmi_wmm_params_arg ac_bk;
+ struct wmi_wmm_params_arg ac_vi;
+ struct wmi_wmm_params_arg ac_vo;
+ };
+
+-struct wal_dbg_tx_stats {
++struct wmi_pdev_stats_tx {
+ /* Num HTT cookies queued to dispatch list */
+ __le32 comp_queued;
+
+@@ -2719,7 +3017,7 @@ struct wal_dbg_tx_stats {
+ __le32 txop_ovf;
+ } __packed;
+
+-struct wal_dbg_rx_stats {
++struct wmi_pdev_stats_rx {
+ /* Cnts any change in ring routing mid-ppdu */
+ __le32 mid_ppdu_route_change;
+
+@@ -2753,20 +3051,18 @@ struct wal_dbg_rx_stats {
+ __le32 mpdu_errs;
+ } __packed;
+
+-struct wal_dbg_peer_stats {
++struct wmi_pdev_stats_peer {
+ /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+ __le32 dummy;
+ } __packed;
+
+-struct wal_dbg_stats {
+- struct wal_dbg_tx_stats tx;
+- struct wal_dbg_rx_stats rx;
+- struct wal_dbg_peer_stats peer;
+-} __packed;
+-
+ enum wmi_stats_id {
+- WMI_REQUEST_PEER_STAT = 0x01,
+- WMI_REQUEST_AP_STAT = 0x02
++ WMI_STAT_PEER = BIT(0),
++ WMI_STAT_AP = BIT(1),
++ WMI_STAT_PDEV = BIT(2),
++ WMI_STAT_VDEV = BIT(3),
++ WMI_STAT_BCNFLT = BIT(4),
++ WMI_STAT_VDEV_RATE = BIT(5),
+ };
+
+ struct wlan_inst_rssi_args {
+@@ -2801,7 +3097,7 @@ struct wmi_pdev_suspend_cmd {
+ } __packed;
+
+ struct wmi_stats_event {
+- __le32 stats_id; /* %WMI_REQUEST_ */
++ __le32 stats_id; /* WMI_STAT_ */
+ /*
+ * number of pdev stats event structures
+ * (wmi_pdev_stats) 0 or 1
+@@ -2830,30 +3126,38 @@ struct wmi_stats_event {
+ u8 data[0];
+ } __packed;
+
++struct wmi_10_2_stats_event {
++ __le32 stats_id; /* %WMI_REQUEST_ */
++ __le32 num_pdev_stats;
++ __le32 num_pdev_ext_stats;
++ __le32 num_vdev_stats;
++ __le32 num_peer_stats;
++ __le32 num_bcnflt_stats;
++ u8 data[0];
++} __packed;
++
+ /*
+ * PDEV statistics
+ * TODO: add all PDEV stats here
+ */
+-struct wmi_pdev_stats_old {
+- __le32 chan_nf; /* Channel noise floor */
+- __le32 tx_frame_count; /* TX frame count */
+- __le32 rx_frame_count; /* RX frame count */
+- __le32 rx_clear_count; /* rx clear count */
+- __le32 cycle_count; /* cycle count */
+- __le32 phy_err_count; /* Phy error count */
+- __le32 chan_tx_pwr; /* channel tx power */
+- struct wal_dbg_stats wal; /* WAL dbg stats */
+-} __packed;
+-
+-struct wmi_pdev_stats_10x {
+- __le32 chan_nf; /* Channel noise floor */
+- __le32 tx_frame_count; /* TX frame count */
+- __le32 rx_frame_count; /* RX frame count */
+- __le32 rx_clear_count; /* rx clear count */
+- __le32 cycle_count; /* cycle count */
+- __le32 phy_err_count; /* Phy error count */
+- __le32 chan_tx_pwr; /* channel tx power */
+- struct wal_dbg_stats wal; /* WAL dbg stats */
++struct wmi_pdev_stats_base {
++ __le32 chan_nf;
++ __le32 tx_frame_count;
++ __le32 rx_frame_count;
++ __le32 rx_clear_count;
++ __le32 cycle_count;
++ __le32 phy_err_count;
++ __le32 chan_tx_pwr;
++} __packed;
++
++struct wmi_pdev_stats {
++ struct wmi_pdev_stats_base base;
++ struct wmi_pdev_stats_tx tx;
++ struct wmi_pdev_stats_rx rx;
++ struct wmi_pdev_stats_peer peer;
++} __packed;
++
++struct wmi_pdev_stats_extra {
+ __le32 ack_rx_bad;
+ __le32 rts_bad;
+ __le32 rts_good;
+@@ -2862,6 +3166,30 @@ struct wmi_pdev_stats_10x {
+ __le32 mib_int_count;
+ } __packed;
+
++struct wmi_10x_pdev_stats {
++ struct wmi_pdev_stats_base base;
++ struct wmi_pdev_stats_tx tx;
++ struct wmi_pdev_stats_rx rx;
++ struct wmi_pdev_stats_peer peer;
++ struct wmi_pdev_stats_extra extra;
++} __packed;
++
++struct wmi_pdev_stats_mem {
++ __le32 dram_free;
++ __le32 iram_free;
++} __packed;
++
++struct wmi_10_2_pdev_stats {
++ struct wmi_pdev_stats_base base;
++ struct wmi_pdev_stats_tx tx;
++ __le32 mc_drop;
++ struct wmi_pdev_stats_rx rx;
++ __le32 pdev_rx_timeout;
++ struct wmi_pdev_stats_mem mem;
++ struct wmi_pdev_stats_peer peer;
++ struct wmi_pdev_stats_extra extra;
++} __packed;
++
+ /*
+ * VDEV statistics
+ * TODO: add all VDEV stats here
+@@ -2874,19 +3202,43 @@ struct wmi_vdev_stats {
+ * peer statistics.
+ * TODO: add more stats
+ */
+-struct wmi_peer_stats_old {
++struct wmi_peer_stats {
+ struct wmi_mac_addr peer_macaddr;
+ __le32 peer_rssi;
+ __le32 peer_tx_rate;
+ } __packed;
+
+-struct wmi_peer_stats_10x {
+- struct wmi_mac_addr peer_macaddr;
+- __le32 peer_rssi;
+- __le32 peer_tx_rate;
++struct wmi_10x_peer_stats {
++ struct wmi_peer_stats old;
+ __le32 peer_rx_rate;
+ } __packed;
+
++struct wmi_10_2_peer_stats {
++ struct wmi_peer_stats old;
++ __le32 peer_rx_rate;
++ __le32 current_per;
++ __le32 retries;
++ __le32 tx_rate_count;
++ __le32 max_4ms_frame_len;
++ __le32 total_sub_frames;
++ __le32 tx_bytes;
++ __le32 num_pkt_loss_overflow[4];
++ __le32 num_pkt_loss_excess_retry[4];
++} __packed;
++
++struct wmi_10_2_4_peer_stats {
++ struct wmi_10_2_peer_stats common;
++ __le32 unknown_value; /* FIXME: what is this word? */
++} __packed;
++
++struct wmi_10_2_pdev_ext_stats {
++ __le32 rx_rssi_comb;
++ __le32 rx_rssi[4];
++ __le32 rx_mcs[10];
++ __le32 tx_mcs[10];
++ __le32 ack_rssi;
++} __packed;
++
+ struct wmi_vdev_create_cmd {
+ __le32 vdev_id;
+ __le32 vdev_type;
+@@ -3387,8 +3739,21 @@ enum wmi_10x_vdev_param {
+ WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
+
+ WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
++
++ /* following are available as of firmware 10.2 */
++ WMI_10X_VDEV_PARAM_TX_ENCAP_TYPE,
++ WMI_10X_VDEV_PARAM_CABQ_MAXDUR,
++ WMI_10X_VDEV_PARAM_MFPTEST_SET,
++ WMI_10X_VDEV_PARAM_RTS_FIXED_RATE,
++ WMI_10X_VDEV_PARAM_VHT_SGIMASK,
++ WMI_10X_VDEV_PARAM_VHT80_RATEMASK,
+ };
+
++#define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0)
++#define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1)
++#define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2)
++#define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3)
++
+ /* slot time long */
+ #define WMI_VDEV_SLOT_TIME_LONG 0x1
+ /* slot time short */
+@@ -3444,6 +3809,98 @@ struct wmi_vdev_simple_event {
+ /* unsupported VDEV combination */
+ #define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED 0x2
+
++/* TODO: please add more comments if you have in-depth information */
++struct wmi_vdev_spectral_conf_cmd {
++ __le32 vdev_id;
++
++ /* number of fft samples to send (0 for infinite) */
++ __le32 scan_count;
++ __le32 scan_period;
++ __le32 scan_priority;
++
++ /* number of bins in the FFT: 2^(fft_size - bin_scale) */
++ __le32 scan_fft_size;
++ __le32 scan_gc_ena;
++ __le32 scan_restart_ena;
++ __le32 scan_noise_floor_ref;
++ __le32 scan_init_delay;
++ __le32 scan_nb_tone_thr;
++ __le32 scan_str_bin_thr;
++ __le32 scan_wb_rpt_mode;
++ __le32 scan_rssi_rpt_mode;
++ __le32 scan_rssi_thr;
++ __le32 scan_pwr_format;
++
++ /* rpt_mode: Format of FFT report to software for spectral scan
++ * triggered FFTs:
++ * 0: No FFT report (only spectral scan summary report)
++ * 1: 2-dword summary of metrics for each completed FFT + spectral
++ * scan summary report
++ * 2: 2-dword summary of metrics for each completed FFT +
++ * 1x- oversampled bins(in-band) per FFT + spectral scan summary
++ * report
++ * 3: 2-dword summary of metrics for each completed FFT +
++ * 2x- oversampled bins (all) per FFT + spectral scan summary
++ */
++ __le32 scan_rpt_mode;
++ __le32 scan_bin_scale;
++ __le32 scan_dbm_adj;
++ __le32 scan_chn_mask;
++} __packed;
++
++struct wmi_vdev_spectral_conf_arg {
++ u32 vdev_id;
++ u32 scan_count;
++ u32 scan_period;
++ u32 scan_priority;
++ u32 scan_fft_size;
++ u32 scan_gc_ena;
++ u32 scan_restart_ena;
++ u32 scan_noise_floor_ref;
++ u32 scan_init_delay;
++ u32 scan_nb_tone_thr;
++ u32 scan_str_bin_thr;
++ u32 scan_wb_rpt_mode;
++ u32 scan_rssi_rpt_mode;
++ u32 scan_rssi_thr;
++ u32 scan_pwr_format;
++ u32 scan_rpt_mode;
++ u32 scan_bin_scale;
++ u32 scan_dbm_adj;
++ u32 scan_chn_mask;
++};
++
++#define WMI_SPECTRAL_ENABLE_DEFAULT 0
++#define WMI_SPECTRAL_COUNT_DEFAULT 0
++#define WMI_SPECTRAL_PERIOD_DEFAULT 35
++#define WMI_SPECTRAL_PRIORITY_DEFAULT 1
++#define WMI_SPECTRAL_FFT_SIZE_DEFAULT 7
++#define WMI_SPECTRAL_GC_ENA_DEFAULT 1
++#define WMI_SPECTRAL_RESTART_ENA_DEFAULT 0
++#define WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT -96
++#define WMI_SPECTRAL_INIT_DELAY_DEFAULT 80
++#define WMI_SPECTRAL_NB_TONE_THR_DEFAULT 12
++#define WMI_SPECTRAL_STR_BIN_THR_DEFAULT 8
++#define WMI_SPECTRAL_WB_RPT_MODE_DEFAULT 0
++#define WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT 0
++#define WMI_SPECTRAL_RSSI_THR_DEFAULT 0xf0
++#define WMI_SPECTRAL_PWR_FORMAT_DEFAULT 0
++#define WMI_SPECTRAL_RPT_MODE_DEFAULT 2
++#define WMI_SPECTRAL_BIN_SCALE_DEFAULT 1
++#define WMI_SPECTRAL_DBM_ADJ_DEFAULT 1
++#define WMI_SPECTRAL_CHN_MASK_DEFAULT 1
++
++struct wmi_vdev_spectral_enable_cmd {
++ __le32 vdev_id;
++ __le32 trigger_cmd;
++ __le32 enable_cmd;
++} __packed;
++
++#define WMI_SPECTRAL_TRIGGER_CMD_TRIGGER 1
++#define WMI_SPECTRAL_TRIGGER_CMD_CLEAR 2
++#define WMI_SPECTRAL_ENABLE_CMD_ENABLE 1
++#define WMI_SPECTRAL_ENABLE_CMD_DISABLE 2
++
+ /* Beacon processing related command and event structures */
+ struct wmi_bcn_tx_hdr {
+ __le32 vdev_id;
+@@ -3470,6 +3927,11 @@ enum wmi_bcn_tx_ref_flags {
+ WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2,
+ };
+
++/* TODO: It is unclear why "no antenna" works while any other seemingly valid
++ * chainmask yields no beacons on the air at all.
++ */
++#define WMI_BCN_TX_REF_DEF_ANTENNA 0
++
+ struct wmi_bcn_tx_ref_cmd {
+ __le32 vdev_id;
+ __le32 data_len;
+@@ -3481,6 +3943,8 @@ struct wmi_bcn_tx_ref_cmd {
+ __le32 frame_control;
+ /* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */
+ __le32 flags;
++ /* introduced in 10.2 */
++ __le32 antenna_mask;
+ } __packed;
+
+ /* Beacon filter */
+@@ -3633,6 +4097,13 @@ enum wmi_sta_ps_param_pspoll_count {
+ * Values greater than 0 indicate the maximum numer of PS-Poll frames
+ * FW will send before waking up.
+ */
++
++ /* When u-APSD is enabled the firmware will be very reluctant to exit
++ * STA PS. This could result in very poor Rx performance with STA doing
++ * PS-Poll for each and every buffered frame. This value is a bit
++ * arbitrary.
++ */
++ WMI_STA_PS_PSPOLL_COUNT_UAPSD = 3,
+ };
+
+ /*
+@@ -3658,6 +4129,30 @@ enum wmi_sta_ps_param_uapsd {
+ WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7),
+ };
+
++#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX
++
++struct wmi_sta_uapsd_auto_trig_param {
++ __le32 wmm_ac;
++ __le32 user_priority;
++ __le32 service_interval;
++ __le32 suspend_interval;
++ __le32 delay_interval;
++};
++
++struct wmi_sta_uapsd_auto_trig_cmd_fixed_param {
++ __le32 vdev_id;
++ struct wmi_mac_addr peer_macaddr;
++ __le32 num_ac;
++};
++
++struct wmi_sta_uapsd_auto_trig_arg {
++ u32 wmm_ac;
++ u32 user_priority;
++ u32 service_interval;
++ u32 suspend_interval;
++ u32 delay_interval;
++};
++
+ enum wmi_sta_powersave_param {
+ /*
+ * Controls how frames are retrievd from AP while STA is sleeping
+@@ -3823,7 +4318,7 @@ struct wmi_bcn_info {
+
+ struct wmi_host_swba_event {
+ __le32 vdev_map;
+- struct wmi_bcn_info bcn_info[1];
++ struct wmi_bcn_info bcn_info[0];
+ } __packed;
+
+ #define WMI_MAX_AP_VDEV 16
+@@ -3833,7 +4328,6 @@ struct wmi_tbtt_offset_event {
+ __le32 tbttoffset_list[WMI_MAX_AP_VDEV];
+ } __packed;
+
+-
+ struct wmi_peer_create_cmd {
+ __le32 vdev_id;
+ struct wmi_mac_addr peer_macaddr;
+@@ -3951,7 +4445,8 @@ enum wmi_peer_param {
+ WMI_PEER_AUTHORIZE = 0x3,
+ WMI_PEER_CHAN_WIDTH = 0x4,
+ WMI_PEER_NSS = 0x5,
+- WMI_PEER_USE_4ADDR = 0x6
++ WMI_PEER_USE_4ADDR = 0x6,
++ WMI_PEER_DUMMY_VAR = 0xff, /* dummy parameter for STA PS workaround */
+ };
+
+ struct wmi_peer_set_param_cmd {
+@@ -4029,7 +4524,7 @@ struct wmi_peer_set_q_empty_callback_cmd
+ #define WMI_PEER_SPATIAL_MUX 0x00200000
+ #define WMI_PEER_VHT 0x02000000
+ #define WMI_PEER_80MHZ 0x04000000
+-#define WMI_PEER_PMF 0x08000000
++#define WMI_PEER_VHT_2G 0x08000000
+
+ /*
+ * Peer rate capabilities.
+@@ -4053,7 +4548,7 @@ struct wmi_peer_set_q_empty_callback_cmd
+ /* Maximum listen interval supported by hw in units of beacon interval */
+ #define ATH10K_MAX_HW_LISTEN_INTERVAL 5
+
+-struct wmi_peer_assoc_complete_cmd {
++struct wmi_common_peer_assoc_complete_cmd {
+ struct wmi_mac_addr peer_macaddr;
+ __le32 vdev_id;
+ __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */
+@@ -4071,11 +4566,30 @@ struct wmi_peer_assoc_complete_cmd {
+ __le32 peer_vht_caps;
+ __le32 peer_phymode;
+ struct wmi_vht_rate_set peer_vht_rates;
++};
++
++struct wmi_main_peer_assoc_complete_cmd {
++ struct wmi_common_peer_assoc_complete_cmd cmd;
++
+ /* HT Operation Element of the peer. Five bytes packed in 2
+ * INT32 array and filled from lsb to msb. */
+ __le32 peer_ht_info[2];
+ } __packed;
+
++struct wmi_10_1_peer_assoc_complete_cmd {
++ struct wmi_common_peer_assoc_complete_cmd cmd;
++} __packed;
++
++#define WMI_PEER_ASSOC_INFO0_MAX_MCS_IDX_LSB 0
++#define WMI_PEER_ASSOC_INFO0_MAX_MCS_IDX_MASK 0x0f
++#define WMI_PEER_ASSOC_INFO0_MAX_NSS_LSB 4
++#define WMI_PEER_ASSOC_INFO0_MAX_NSS_MASK 0xf0
++
++struct wmi_10_2_peer_assoc_complete_cmd {
++ struct wmi_common_peer_assoc_complete_cmd cmd;
++ __le32 info0; /* WMI_PEER_ASSOC_INFO0_ */
++} __packed;
++
+ struct wmi_peer_assoc_complete_arg {
+ u8 addr[ETH_ALEN];
+ u32 vdev_id;
+@@ -4161,6 +4675,11 @@ enum wmi_sta_keepalive_method {
+ WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2,
+ };
+
++#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0
++
++/* Firmware crashes if keepalive interval exceeds this limit */
++#define WMI_STA_KEEPALIVE_INTERVAL_MAX_SECONDS 0xffff
++
+ /* note: ip4 addresses are in network byte order, i.e. big endian */
+ struct wmi_sta_keepalive_arp_resp {
+ __be32 src_ip4_addr;
+@@ -4176,6 +4695,16 @@ struct wmi_sta_keepalive_cmd {
+ struct wmi_sta_keepalive_arp_resp arp_resp;
+ } __packed;
+
++struct wmi_sta_keepalive_arg {
++ u32 vdev_id;
++ u32 enabled;
++ u32 method;
++ u32 interval;
++ __be32 src_ip4_addr;
++ __be32 dest_ip4_addr;
++ const u8 dest_mac_addr[ETH_ALEN];
++};
++
+ enum wmi_force_fw_hang_type {
+ WMI_FORCE_FW_HANG_ASSERT = 1,
+ WMI_FORCE_FW_HANG_NO_DETECT,
+@@ -4240,7 +4769,6 @@ struct wmi_dbglog_cfg_cmd {
+ __le32 config_valid;
+ } __packed;
+
+-#define ATH10K_RTS_MAX 2347
+ #define ATH10K_FRAGMT_THRESHOLD_MIN 540
+ #define ATH10K_FRAGMT_THRESHOLD_MAX 2346
+
+@@ -4251,72 +4779,170 @@ struct wmi_dbglog_cfg_cmd {
+ /* By default disable power save for IBSS */
+ #define ATH10K_DEFAULT_ATIM 0
+
++#define WMI_MAX_MEM_REQS 16
++
++struct wmi_scan_ev_arg {
++ __le32 event_type; /* %WMI_SCAN_EVENT_ */
++ __le32 reason; /* %WMI_SCAN_REASON_ */
++ __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */
++ __le32 scan_req_id;
++ __le32 scan_id;
++ __le32 vdev_id;
++};
++
++struct wmi_mgmt_rx_ev_arg {
++ __le32 channel;
++ __le32 snr;
++ __le32 rate;
++ __le32 phy_mode;
++ __le32 buf_len;
++ __le32 status; /* %WMI_RX_STATUS_ */
++};
++
++struct wmi_ch_info_ev_arg {
++ __le32 err_code;
++ __le32 freq;
++ __le32 cmd_flags;
++ __le32 noise_floor;
++ __le32 rx_clear_count;
++ __le32 cycle_count;
++};
++
++struct wmi_vdev_start_ev_arg {
++ __le32 vdev_id;
++ __le32 req_id;
++ __le32 resp_type; /* %WMI_VDEV_RESP_ */
++ __le32 status;
++};
++
++struct wmi_peer_kick_ev_arg {
++ const u8 *mac_addr;
++};
++
++struct wmi_swba_ev_arg {
++ __le32 vdev_map;
++ const struct wmi_tim_info *tim_info[WMI_MAX_AP_VDEV];
++ const struct wmi_p2p_noa_info *noa_info[WMI_MAX_AP_VDEV];
++};
++
++struct wmi_phyerr_ev_arg {
++ __le32 num_phyerrs;
++ __le32 tsf_l32;
++ __le32 tsf_u32;
++ __le32 buf_len;
++ const struct wmi_phyerr *phyerrs;
++};
++
++struct wmi_svc_rdy_ev_arg {
++ __le32 min_tx_power;
++ __le32 max_tx_power;
++ __le32 ht_cap;
++ __le32 vht_cap;
++ __le32 sw_ver0;
++ __le32 sw_ver1;
++ __le32 fw_build;
++ __le32 phy_capab;
++ __le32 num_rf_chains;
++ __le32 eeprom_rd;
++ __le32 num_mem_reqs;
++ const __le32 *service_map;
++ size_t service_map_len;
++ const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
++};
++
++struct wmi_rdy_ev_arg {
++ __le32 sw_version;
++ __le32 abi_version;
++ __le32 status;
++ const u8 *mac_addr;
++};
++
++struct wmi_pdev_temperature_event {
++ /* temperature value in Celcius degree */
++ __le32 temperature;
++} __packed;
++
+ struct ath10k;
+ struct ath10k_vif;
++struct ath10k_fw_stats_pdev;
++struct ath10k_fw_stats_peer;
+
+ int ath10k_wmi_attach(struct ath10k *ar);
+ void ath10k_wmi_detach(struct ath10k *ar);
+ int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
+ int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
+
+-int ath10k_wmi_connect_htc_service(struct ath10k *ar);
+-int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+- const struct wmi_channel_arg *);
+-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
+-int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
+-int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+- u16 rd5g, u16 ctl2g, u16 ctl5g,
+- enum wmi_dfs_region dfs_reg);
+-int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value);
+-int ath10k_wmi_cmd_init(struct ath10k *ar);
+-int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
++struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len);
++int ath10k_wmi_connect(struct ath10k *ar);
++
++struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len);
++int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
++int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
++ u32 cmd_id);
+ void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
+-int ath10k_wmi_stop_scan(struct ath10k *ar,
+- const struct wmi_stop_scan_arg *arg);
+-int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+- enum wmi_vdev_type type,
+- enum wmi_vdev_subtype subtype,
+- const u8 macaddr[ETH_ALEN]);
+-int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id);
+-int ath10k_wmi_vdev_start(struct ath10k *ar,
+- const struct wmi_vdev_start_request_arg *);
+-int ath10k_wmi_vdev_restart(struct ath10k *ar,
+- const struct wmi_vdev_start_request_arg *);
+-int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id);
+-int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
+- const u8 *bssid);
+-int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
+-int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+- u32 param_id, u32 param_value);
+-int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+- const struct wmi_vdev_install_key_arg *arg);
+-int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+- const u8 peer_addr[ETH_ALEN]);
+-int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+- const u8 peer_addr[ETH_ALEN]);
+-int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+- const u8 peer_addr[ETH_ALEN], u32 tid_bitmap);
+-int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+- const u8 *peer_addr,
+- enum wmi_peer_param param_id, u32 param_value);
+-int ath10k_wmi_peer_assoc(struct ath10k *ar,
+- const struct wmi_peer_assoc_complete_arg *arg);
+-int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+- enum wmi_sta_ps_mode psmode);
+-int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+- enum wmi_sta_powersave_param param_id,
+- u32 value);
+-int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+- enum wmi_ap_ps_peer_param param_id, u32 value);
+-int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+- const struct wmi_scan_chan_list_arg *arg);
+-int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif);
+-int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+- const struct wmi_pdev_set_wmm_params_arg *arg);
+-int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+-int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+- enum wmi_force_fw_hang_type type, u32 delay_ms);
+-int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
+-int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
++
++void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
++ struct ath10k_fw_stats_pdev *dst);
++void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src,
++ struct ath10k_fw_stats_pdev *dst);
++void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src,
++ struct ath10k_fw_stats_pdev *dst);
++void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src,
++ struct ath10k_fw_stats_pdev *dst);
++void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
++ struct ath10k_fw_stats_peer *dst);
++void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
++ struct wmi_host_mem_chunks *chunks);
++void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
++ const struct wmi_start_scan_arg *arg);
++void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params,
++ const struct wmi_wmm_params_arg *arg);
++void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
++ const struct wmi_channel_arg *arg);
++int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg);
++
++int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb);
++int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb);
++int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_dfs(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr, u64 tsf);
++void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr,
++ u64 tsf);
++void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
++ struct sk_buff *skb);
++void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
++ struct sk_buff *skb);
++void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
++ struct sk_buff *skb);
++void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
++ struct sk_buff *skb);
++void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb);
++void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb);
++int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb);
+
+ #endif /* _WMI_H_ */
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/spectral.c
+@@ -0,0 +1,552 @@
++/*
++ * Copyright (c) 2013 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/relay.h>
++#include "core.h"
++#include "debug.h"
++#include "wmi-ops.h"
++
++static void send_fft_sample(struct ath10k *ar,
++ const struct fft_sample_tlv *fft_sample_tlv)
++{
++ int length;
++
++ if (!ar->spectral.rfs_chan_spec_scan)
++ return;
++
++ length = __be16_to_cpu(fft_sample_tlv->length) +
++ sizeof(*fft_sample_tlv);
++ relay_write(ar->spectral.rfs_chan_spec_scan, fft_sample_tlv, length);
++}
++
++static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len,
++ u8 *data)
++{
++ int dc_pos;
++ u8 max_exp;
++
++ dc_pos = bin_len / 2;
++
++ /* peak index outside of bins */
++ if (dc_pos < max_index || -dc_pos >= max_index)
++ return 0;
++
++ for (max_exp = 0; max_exp < 8; max_exp++) {
++ if (data[dc_pos + max_index] == (max_magnitude >> max_exp))
++ break;
++ }
++
++ /* max_exp not found */
++ if (data[dc_pos + max_index] != (max_magnitude >> max_exp))
++ return 0;
++
++ return max_exp;
++}
++
++int ath10k_spectral_process_fft(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr,
++ const struct phyerr_fft_report *fftr,
++ size_t bin_len, u64 tsf)
++{
++ struct fft_sample_ath10k *fft_sample;
++ u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS];
++ u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag;
++ u32 reg0, reg1;
++ u8 chain_idx, *bins;
++ int dc_pos;
++
++ fft_sample = (struct fft_sample_ath10k *)&buf;
++
++ if (bin_len < 64 || bin_len > SPECTRAL_ATH10K_MAX_NUM_BINS)
++ return -EINVAL;
++
++ reg0 = __le32_to_cpu(fftr->reg0);
++ reg1 = __le32_to_cpu(fftr->reg1);
++
++ length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + bin_len;
++ fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH10K;
++ fft_sample->tlv.length = __cpu_to_be16(length);
++
++ /* TODO: there might be a reason why the hardware reports 20/40/80 MHz,
++ * but the results/plots suggest that its actually 22/44/88 MHz.
++ */
++ switch (phyerr->chan_width_mhz) {
++ case 20:
++ fft_sample->chan_width_mhz = 22;
++ break;
++ case 40:
++ fft_sample->chan_width_mhz = 44;
++ break;
++ case 80:
++ /* TODO: As experiments with an analogue sender and various
++ * configuaritions (fft-sizes of 64/128/256 and 20/40/80 Mhz)
++ * show, the particular configuration of 80 MHz/64 bins does
++ * not match with the other smaples at all. Until the reason
++ * for that is found, don't report these samples.
++ */
++ if (bin_len == 64)
++ return -EINVAL;
++ fft_sample->chan_width_mhz = 88;
++ break;
++ default:
++ fft_sample->chan_width_mhz = phyerr->chan_width_mhz;
++ }
++
++ fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB);
++ fft_sample->avgpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB);
++
++ peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
++ fft_sample->max_magnitude = __cpu_to_be16(peak_mag);
++ fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX);
++ fft_sample->rssi = phyerr->rssi_combined;
++
++ total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB);
++ base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB);
++ fft_sample->total_gain_db = __cpu_to_be16(total_gain_db);
++ fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db);
++
++ freq1 = __le16_to_cpu(phyerr->freq1);
++ freq2 = __le16_to_cpu(phyerr->freq2);
++ fft_sample->freq1 = __cpu_to_be16(freq1);
++ fft_sample->freq2 = __cpu_to_be16(freq2);
++
++ chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX);
++
++ fft_sample->noise = __cpu_to_be16(
++ __le16_to_cpu(phyerr->nf_chains[chain_idx]));
++
++ bins = (u8 *)fftr;
++ bins += sizeof(*fftr);
++
++ fft_sample->tsf = __cpu_to_be64(tsf);
++
++ /* max_exp has been directly reported by previous hardware (ath9k),
++ * maybe its possible to get it by other means?
++ */
++ fft_sample->max_exp = get_max_exp(fft_sample->max_index, peak_mag,
++ bin_len, bins);
++
++ memcpy(fft_sample->data, bins, bin_len);
++
++ /* DC value (value in the middle) is the blind spot of the spectral
++ * sample and invalid, interpolate it.
++ */
++ dc_pos = bin_len / 2;
++ fft_sample->data[dc_pos] = (fft_sample->data[dc_pos + 1] +
++ fft_sample->data[dc_pos - 1]) / 2;
++
++ send_fft_sample(ar, &fft_sample->tlv);
++
++ return 0;
++}
++
++static struct ath10k_vif *ath10k_get_spectral_vdev(struct ath10k *ar)
++{
++ struct ath10k_vif *arvif;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ if (list_empty(&ar->arvifs))
++ return NULL;
++
++ /* if there already is a vif doing spectral, return that. */
++ list_for_each_entry(arvif, &ar->arvifs, list)
++ if (arvif->spectral_enabled)
++ return arvif;
++
++ /* otherwise, return the first vif. */
++ return list_first_entry(&ar->arvifs, typeof(*arvif), list);
++}
++
++static int ath10k_spectral_scan_trigger(struct ath10k *ar)
++{
++ struct ath10k_vif *arvif;
++ int res;
++ int vdev_id;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ arvif = ath10k_get_spectral_vdev(ar);
++ if (!arvif)
++ return -ENODEV;
++ vdev_id = arvif->vdev_id;
++
++ if (ar->spectral.mode == SPECTRAL_DISABLED)
++ return 0;
++
++ res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
++ WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
++ WMI_SPECTRAL_ENABLE_CMD_ENABLE);
++ if (res < 0)
++ return res;
++
++ res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
++ WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
++ WMI_SPECTRAL_ENABLE_CMD_ENABLE);
++ if (res < 0)
++ return res;
++
++ return 0;
++}
++
++static int ath10k_spectral_scan_config(struct ath10k *ar,
++ enum ath10k_spectral_mode mode)
++{
++ struct wmi_vdev_spectral_conf_arg arg;
++ struct ath10k_vif *arvif;
++ int vdev_id, count, res = 0;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ arvif = ath10k_get_spectral_vdev(ar);
++ if (!arvif)
++ return -ENODEV;
++
++ vdev_id = arvif->vdev_id;
++
++ arvif->spectral_enabled = (mode != SPECTRAL_DISABLED);
++ ar->spectral.mode = mode;
++
++ res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
++ WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
++ WMI_SPECTRAL_ENABLE_CMD_DISABLE);
++ if (res < 0) {
++ ath10k_warn(ar, "failed to enable spectral scan: %d\n", res);
++ return res;
++ }
++
++ if (mode == SPECTRAL_DISABLED)
++ return 0;
++
++ if (mode == SPECTRAL_BACKGROUND)
++ count = WMI_SPECTRAL_COUNT_DEFAULT;
++ else
++ count = max_t(u8, 1, ar->spectral.config.count);
++
++ arg.vdev_id = vdev_id;
++ arg.scan_count = count;
++ arg.scan_period = WMI_SPECTRAL_PERIOD_DEFAULT;
++ arg.scan_priority = WMI_SPECTRAL_PRIORITY_DEFAULT;
++ arg.scan_fft_size = ar->spectral.config.fft_size;
++ arg.scan_gc_ena = WMI_SPECTRAL_GC_ENA_DEFAULT;
++ arg.scan_restart_ena = WMI_SPECTRAL_RESTART_ENA_DEFAULT;
++ arg.scan_noise_floor_ref = WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
++ arg.scan_init_delay = WMI_SPECTRAL_INIT_DELAY_DEFAULT;
++ arg.scan_nb_tone_thr = WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
++ arg.scan_str_bin_thr = WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
++ arg.scan_wb_rpt_mode = WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
++ arg.scan_rssi_rpt_mode = WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
++ arg.scan_rssi_thr = WMI_SPECTRAL_RSSI_THR_DEFAULT;
++ arg.scan_pwr_format = WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
++ arg.scan_rpt_mode = WMI_SPECTRAL_RPT_MODE_DEFAULT;
++ arg.scan_bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
++ arg.scan_dbm_adj = WMI_SPECTRAL_DBM_ADJ_DEFAULT;
++ arg.scan_chn_mask = WMI_SPECTRAL_CHN_MASK_DEFAULT;
++
++ res = ath10k_wmi_vdev_spectral_conf(ar, &arg);
++ if (res < 0) {
++ ath10k_warn(ar, "failed to configure spectral scan: %d\n", res);
++ return res;
++ }
++
++ return 0;
++}
++
++static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ char *mode = "";
++ unsigned int len;
++ enum ath10k_spectral_mode spectral_mode;
++
++ mutex_lock(&ar->conf_mutex);
++ spectral_mode = ar->spectral.mode;
++ mutex_unlock(&ar->conf_mutex);
++
++ switch (spectral_mode) {
++ case SPECTRAL_DISABLED:
++ mode = "disable";
++ break;
++ case SPECTRAL_BACKGROUND:
++ mode = "background";
++ break;
++ case SPECTRAL_MANUAL:
++ mode = "manual";
++ break;
++ }
++
++ len = strlen(mode);
++ return simple_read_from_buffer(user_buf, count, ppos, mode, len);
++}
++
++static ssize_t write_file_spec_scan_ctl(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ char buf[32];
++ ssize_t len;
++ int res;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (strncmp("trigger", buf, 7) == 0) {
++ if (ar->spectral.mode == SPECTRAL_MANUAL ||
++ ar->spectral.mode == SPECTRAL_BACKGROUND) {
++ /* reset the configuration to adopt possibly changed
++ * debugfs parameters
++ */
++ res = ath10k_spectral_scan_config(ar,
++ ar->spectral.mode);
++ if (res < 0) {
++ ath10k_warn(ar, "failed to reconfigure spectral scan: %d\n",
++ res);
++ }
++ res = ath10k_spectral_scan_trigger(ar);
++ if (res < 0) {
++ ath10k_warn(ar, "failed to trigger spectral scan: %d\n",
++ res);
++ }
++ } else {
++ res = -EINVAL;
++ }
++ } else if (strncmp("background", buf, 9) == 0) {
++ res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND);
++ } else if (strncmp("manual", buf, 6) == 0) {
++ res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
++ } else if (strncmp("disable", buf, 7) == 0) {
++ res = ath10k_spectral_scan_config(ar, SPECTRAL_DISABLED);
++ } else {
++ res = -EINVAL;
++ }
++
++ mutex_unlock(&ar->conf_mutex);
++
++ if (res < 0)
++ return res;
++
++ return count;
++}
++
++static const struct file_operations fops_spec_scan_ctl = {
++ .read = read_file_spec_scan_ctl,
++ .write = write_file_spec_scan_ctl,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t read_file_spectral_count(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ char buf[32];
++ unsigned int len;
++ u8 spectral_count;
++
++ mutex_lock(&ar->conf_mutex);
++ spectral_count = ar->spectral.config.count;
++ mutex_unlock(&ar->conf_mutex);
++
++ len = sprintf(buf, "%d\n", spectral_count);
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_spectral_count(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ unsigned long val;
++ char buf[32];
++ ssize_t len;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ if (kstrtoul(buf, 0, &val))
++ return -EINVAL;
++
++ if (val < 0 || val > 255)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++ ar->spectral.config.count = val;
++ mutex_unlock(&ar->conf_mutex);
++
++ return count;
++}
++
++static const struct file_operations fops_spectral_count = {
++ .read = read_file_spectral_count,
++ .write = write_file_spectral_count,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t read_file_spectral_bins(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ char buf[32];
++ unsigned int len, bins, fft_size, bin_scale;
++
++ mutex_lock(&ar->conf_mutex);
++
++ fft_size = ar->spectral.config.fft_size;
++ bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
++ bins = 1 << (fft_size - bin_scale);
++
++ mutex_unlock(&ar->conf_mutex);
++
++ len = sprintf(buf, "%d\n", bins);
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t write_file_spectral_bins(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ath10k *ar = file->private_data;
++ unsigned long val;
++ char buf[32];
++ ssize_t len;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ if (kstrtoul(buf, 0, &val))
++ return -EINVAL;
++
++ if (val < 64 || val > SPECTRAL_ATH10K_MAX_NUM_BINS)
++ return -EINVAL;
++
++ if (!is_power_of_2(val))
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++ ar->spectral.config.fft_size = ilog2(val);
++ ar->spectral.config.fft_size += WMI_SPECTRAL_BIN_SCALE_DEFAULT;
++ mutex_unlock(&ar->conf_mutex);
++
++ return count;
++}
++
++static const struct file_operations fops_spectral_bins = {
++ .read = read_file_spectral_bins,
++ .write = write_file_spectral_bins,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static struct dentry *create_buf_file_handler(const char *filename,
++ struct dentry *parent,
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
++ umode_t mode,
++#else
++ int mode,
++#endif
++ struct rchan_buf *buf,
++ int *is_global)
++{
++ struct dentry *buf_file;
++
++ buf_file = debugfs_create_file(filename, mode, parent, buf,
++ &relay_file_operations);
++ *is_global = 1;
++ return buf_file;
++}
++
++static int remove_buf_file_handler(struct dentry *dentry)
++{
++ debugfs_remove(dentry);
++
++ return 0;
++}
++
++static struct rchan_callbacks rfs_spec_scan_cb = {
++ .create_buf_file = create_buf_file_handler,
++ .remove_buf_file = remove_buf_file_handler,
++};
++
++int ath10k_spectral_start(struct ath10k *ar)
++{
++ struct ath10k_vif *arvif;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ list_for_each_entry(arvif, &ar->arvifs, list)
++ arvif->spectral_enabled = 0;
++
++ ar->spectral.mode = SPECTRAL_DISABLED;
++ ar->spectral.config.count = WMI_SPECTRAL_COUNT_DEFAULT;
++ ar->spectral.config.fft_size = WMI_SPECTRAL_FFT_SIZE_DEFAULT;
++
++ return 0;
++}
++
++int ath10k_spectral_vif_stop(struct ath10k_vif *arvif)
++{
++ if (!arvif->spectral_enabled)
++ return 0;
++
++ return ath10k_spectral_scan_config(arvif->ar, SPECTRAL_DISABLED);
++}
++
++int ath10k_spectral_create(struct ath10k *ar)
++{
++ ar->spectral.rfs_chan_spec_scan = relay_open("spectral_scan",
++ ar->debug.debugfs_phy,
++ 1024, 256,
++ &rfs_spec_scan_cb, NULL);
++ debugfs_create_file("spectral_scan_ctl",
++ S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar,
++ &fops_spec_scan_ctl);
++ debugfs_create_file("spectral_count",
++ S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar,
++ &fops_spectral_count);
++ debugfs_create_file("spectral_bins",
++ S_IRUSR | S_IWUSR,
++ ar->debug.debugfs_phy, ar,
++ &fops_spectral_bins);
++
++ return 0;
++}
++
++void ath10k_spectral_destroy(struct ath10k *ar)
++{
++ if (ar->spectral.rfs_chan_spec_scan) {
++ relay_close(ar->spectral.rfs_chan_spec_scan);
++ ar->spectral.rfs_chan_spec_scan = NULL;
++ }
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/spectral.h
+@@ -0,0 +1,90 @@
++/*
++ * Copyright (c) 2013 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef SPECTRAL_H
++#define SPECTRAL_H
++
++#include "../spectral_common.h"
++
++/**
++ * struct ath10k_spec_scan - parameters for Atheros spectral scan
++ *
++ * @count: number of scan results requested for manual mode
++ * @fft_size: number of bins to be requested = 2^(fft_size - bin_scale)
++ */
++struct ath10k_spec_scan {
++ u8 count;
++ u8 fft_size;
++};
++
++/* enum ath10k_spectral_mode:
++ *
++ * @SPECTRAL_DISABLED: spectral mode is disabled
++ * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
++ * something else.
++ * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
++ * is performed manually.
++ */
++enum ath10k_spectral_mode {
++ SPECTRAL_DISABLED = 0,
++ SPECTRAL_BACKGROUND,
++ SPECTRAL_MANUAL,
++};
++
++#ifdef CPTCFG_ATH10K_DEBUGFS
++
++int ath10k_spectral_process_fft(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr,
++ const struct phyerr_fft_report *fftr,
++ size_t bin_len, u64 tsf);
++int ath10k_spectral_start(struct ath10k *ar);
++int ath10k_spectral_vif_stop(struct ath10k_vif *arvif);
++int ath10k_spectral_create(struct ath10k *ar);
++void ath10k_spectral_destroy(struct ath10k *ar);
++
++#else
++
++static inline int
++ath10k_spectral_process_fft(struct ath10k *ar,
++ const struct wmi_phyerr *phyerr,
++ const struct phyerr_fft_report *fftr,
++ size_t bin_len, u64 tsf)
++{
++ return 0;
++}
++
++static inline int ath10k_spectral_start(struct ath10k *ar)
++{
++ return 0;
++}
++
++static inline int ath10k_spectral_vif_stop(struct ath10k_vif *arvif)
++{
++ return 0;
++}
++
++static inline int ath10k_spectral_create(struct ath10k *ar)
++{
++ return 0;
++}
++
++static inline void ath10k_spectral_destroy(struct ath10k *ar)
++{
++}
++
++#endif /* CPTCFG_ATH10K_DEBUGFS */
++
++#endif /* SPECTRAL_H */
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/testmode.c
+@@ -0,0 +1,385 @@
++/*
++ * Copyright (c) 2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include "testmode.h"
++
++#include <net/netlink.h>
++#include <linux/firmware.h>
++
++#include "debug.h"
++#include "wmi.h"
++#include "hif.h"
++#include "hw.h"
++
++#include "testmode_i.h"
++
++static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
++ [ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },
++ [ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,
++ .len = ATH10K_TM_DATA_MAX_LEN },
++ [ATH10K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
++ [ATH10K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
++ [ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
++};
++
++/* Returns true if callee consumes the skb and the skb should be discarded.
++ * Returns false if skb is not used. Does not sleep.
++ */
++bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
++{
++ struct sk_buff *nl_skb;
++ bool consumed;
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
++ "testmode event wmi cmd_id %d skb %p skb->len %d\n",
++ cmd_id, skb, skb->len);
++
++ ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
++
++ spin_lock_bh(&ar->data_lock);
++
++ if (!ar->testmode.utf_monitor) {
++ consumed = false;
++ goto out;
++ }
++
++ /* Only testmode.c should be handling events from utf firmware,
++ * otherwise all sort of problems will arise as mac80211 operations
++ * are not initialised.
++ */
++ consumed = true;
++
++ nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
++ 2 * sizeof(u32) + skb->len,
++ GFP_ATOMIC);
++ if (!nl_skb) {
++ ath10k_warn(ar,
++ "failed to allocate skb for testmode wmi event\n");
++ goto out;
++ }
++
++ ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
++ if (ret) {
++ ath10k_warn(ar,
++ "failed to to put testmode wmi event cmd attribute: %d\n",
++ ret);
++ kfree_skb(nl_skb);
++ goto out;
++ }
++
++ ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
++ if (ret) {
++ ath10k_warn(ar,
++ "failed to to put testmode wmi even cmd_id: %d\n",
++ ret);
++ kfree_skb(nl_skb);
++ goto out;
++ }
++
++ ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
++ if (ret) {
++ ath10k_warn(ar,
++ "failed to copy skb to testmode wmi event: %d\n",
++ ret);
++ kfree_skb(nl_skb);
++ goto out;
++ }
++
++ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
++
++out:
++ spin_unlock_bh(&ar->data_lock);
++
++ return consumed;
++}
++
++static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
++{
++ struct sk_buff *skb;
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
++ "testmode cmd get version_major %d version_minor %d\n",
++ ATH10K_TESTMODE_VERSION_MAJOR,
++ ATH10K_TESTMODE_VERSION_MINOR);
++
++ skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
++ nla_total_size(sizeof(u32)));
++ if (!skb)
++ return -ENOMEM;
++
++ ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,
++ ATH10K_TESTMODE_VERSION_MAJOR);
++ if (ret) {
++ kfree_skb(skb);
++ return ret;
++ }
++
++ ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,
++ ATH10K_TESTMODE_VERSION_MINOR);
++ if (ret) {
++ kfree_skb(skb);
++ return ret;
++ }
++
++ return cfg80211_testmode_reply(skb);
++}
++
++static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
++{
++ char filename[100];
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state == ATH10K_STATE_UTF) {
++ ret = -EALREADY;
++ goto err;
++ }
++
++ /* start utf only when the driver is not in use */
++ if (ar->state != ATH10K_STATE_OFF) {
++ ret = -EBUSY;
++ goto err;
++ }
++
++ if (WARN_ON(ar->testmode.utf != NULL)) {
++ /* utf image is already downloaded, it shouldn't be */
++ ret = -EEXIST;
++ goto err;
++ }
++
++ snprintf(filename, sizeof(filename), "%s/%s",
++ ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
++
++ /* load utf firmware image */
++ ret = request_firmware(&ar->testmode.utf, filename, ar->dev);
++ if (ret) {
++ ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
++ filename, ret);
++ goto err;
++ }
++
++ spin_lock_bh(&ar->data_lock);
++
++ ar->testmode.utf_monitor = true;
++
++ spin_unlock_bh(&ar->data_lock);
++
++ BUILD_BUG_ON(sizeof(ar->fw_features) !=
++ sizeof(ar->testmode.orig_fw_features));
++
++ memcpy(ar->testmode.orig_fw_features, ar->fw_features,
++ sizeof(ar->fw_features));
++ ar->testmode.orig_wmi_op_version = ar->wmi.op_version;
++
++ /* utf.bin firmware image does not advertise firmware features. Do
++ * an ugly hack where we force the firmware features so that wmi.c
++ * will use the correct WMI interface.
++ */
++ memset(ar->fw_features, 0, sizeof(ar->fw_features));
++ ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
++
++ ret = ath10k_hif_power_up(ar);
++ if (ret) {
++ ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);
++ ar->state = ATH10K_STATE_OFF;
++ goto err_fw_features;
++ }
++
++ ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF);
++ if (ret) {
++ ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
++ ar->state = ATH10K_STATE_OFF;
++ goto err_power_down;
++ }
++
++ ar->state = ATH10K_STATE_UTF;
++
++ ath10k_info(ar, "UTF firmware started\n");
++
++ mutex_unlock(&ar->conf_mutex);
++
++ return 0;
++
++err_power_down:
++ ath10k_hif_power_down(ar);
++
++err_fw_features:
++ /* return the original firmware features */
++ memcpy(ar->fw_features, ar->testmode.orig_fw_features,
++ sizeof(ar->fw_features));
++ ar->wmi.op_version = ar->testmode.orig_wmi_op_version;
++
++ release_firmware(ar->testmode.utf);
++ ar->testmode.utf = NULL;
++
++err:
++ mutex_unlock(&ar->conf_mutex);
++
++ return ret;
++}
++
++static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
++{
++ lockdep_assert_held(&ar->conf_mutex);
++
++ ath10k_core_stop(ar);
++ ath10k_hif_power_down(ar);
++
++ spin_lock_bh(&ar->data_lock);
++
++ ar->testmode.utf_monitor = false;
++
++ spin_unlock_bh(&ar->data_lock);
++
++ /* return the original firmware features */
++ memcpy(ar->fw_features, ar->testmode.orig_fw_features,
++ sizeof(ar->fw_features));
++ ar->wmi.op_version = ar->testmode.orig_wmi_op_version;
++
++ release_firmware(ar->testmode.utf);
++ ar->testmode.utf = NULL;
++
++ ar->state = ATH10K_STATE_OFF;
++}
++
++static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])
++{
++ int ret;
++
++ ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto out;
++ }
++
++ __ath10k_tm_cmd_utf_stop(ar);
++
++ ret = 0;
++
++ ath10k_info(ar, "UTF firmware stopped\n");
++
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
++{
++ struct sk_buff *skb;
++ int ret, buf_len;
++ u32 cmd_id;
++ void *buf;
++
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_UTF) {
++ ret = -ENETDOWN;
++ goto out;
++ }
++
++ if (!tb[ATH10K_TM_ATTR_DATA]) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
++ buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
++ cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
++
++ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
++ "testmode cmd wmi cmd_id %d buf %p buf_len %d\n",
++ cmd_id, buf, buf_len);
++
++ ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
++
++ skb = ath10k_wmi_alloc_skb(ar, buf_len);
++ if (!skb) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ memcpy(skb->data, buf, buf_len);
++
++ ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
++ if (ret) {
++ ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",
++ ret);
++ goto out;
++ }
++
++ ret = 0;
++
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ void *data, int len)
++{
++ struct ath10k *ar = hw->priv;
++ struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];
++ int ret;
++
++ ret = nla_parse(tb, ATH10K_TM_ATTR_MAX, data, len,
++ ath10k_tm_policy);
++ if (ret)
++ return ret;
++
++ if (!tb[ATH10K_TM_ATTR_CMD])
++ return -EINVAL;
++
++ switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
++ case ATH10K_TM_CMD_GET_VERSION:
++ return ath10k_tm_cmd_get_version(ar, tb);
++ case ATH10K_TM_CMD_UTF_START:
++ return ath10k_tm_cmd_utf_start(ar, tb);
++ case ATH10K_TM_CMD_UTF_STOP:
++ return ath10k_tm_cmd_utf_stop(ar, tb);
++ case ATH10K_TM_CMD_WMI:
++ return ath10k_tm_cmd_wmi(ar, tb);
++ default:
++ return -EOPNOTSUPP;
++ }
++}
++
++void ath10k_testmode_destroy(struct ath10k *ar)
++{
++ mutex_lock(&ar->conf_mutex);
++
++ if (ar->state != ATH10K_STATE_UTF) {
++ /* utf firmware is not running, nothing to do */
++ goto out;
++ }
++
++ __ath10k_tm_cmd_utf_stop(ar);
++
++out:
++ mutex_unlock(&ar->conf_mutex);
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/testmode.h
+@@ -0,0 +1,46 @@
++/*
++ * Copyright (c) 2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include "core.h"
++
++#ifdef CPTCFG_NL80211_TESTMODE
++
++void ath10k_testmode_destroy(struct ath10k *ar);
++
++bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb);
++int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ void *data, int len);
++
++#else
++
++static inline void ath10k_testmode_destroy(struct ath10k *ar)
++{
++}
++
++static inline bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id,
++ struct sk_buff *skb)
++{
++ return false;
++}
++
++static inline int ath10k_tm_cmd(struct ieee80211_hw *hw,
++ struct ieee80211_vif *vif,
++ void *data, int len)
++{
++ return 0;
++}
++
++#endif
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/testmode_i.h
+@@ -0,0 +1,70 @@
++/*
++ * Copyright (c) 2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++/* "API" level of the ath10k testmode interface. Bump it after every
++ * incompatible interface change.
++ */
++#define ATH10K_TESTMODE_VERSION_MAJOR 1
++
++/* Bump this after every _compatible_ interface change, for example
++ * addition of a new command or an attribute.
++ */
++#define ATH10K_TESTMODE_VERSION_MINOR 0
++
++#define ATH10K_TM_DATA_MAX_LEN 5000
++
++enum ath10k_tm_attr {
++ __ATH10K_TM_ATTR_INVALID = 0,
++ ATH10K_TM_ATTR_CMD = 1,
++ ATH10K_TM_ATTR_DATA = 2,
++ ATH10K_TM_ATTR_WMI_CMDID = 3,
++ ATH10K_TM_ATTR_VERSION_MAJOR = 4,
++ ATH10K_TM_ATTR_VERSION_MINOR = 5,
++
++ /* keep last */
++ __ATH10K_TM_ATTR_AFTER_LAST,
++ ATH10K_TM_ATTR_MAX = __ATH10K_TM_ATTR_AFTER_LAST - 1,
++};
++
++/* All ath10k testmode interface commands specified in
++ * ATH10K_TM_ATTR_CMD
++ */
++enum ath10k_tm_cmd {
++ /* Returns the supported ath10k testmode interface version in
++ * ATH10K_TM_ATTR_VERSION. Always guaranteed to work. User space
++ * uses this to verify it's using the correct version of the
++ * testmode interface
++ */
++ ATH10K_TM_CMD_GET_VERSION = 0,
++
++ /* Boots the UTF firmware, the netdev interface must be down at the
++ * time.
++ */
++ ATH10K_TM_CMD_UTF_START = 1,
++
++ /* Shuts down the UTF firmware and puts the driver back into OFF
++ * state.
++ */
++ ATH10K_TM_CMD_UTF_STOP = 2,
++
++ /* The command used to transmit a WMI command to the firmware and
++ * the event to receive WMI events from the firmware. Without
++ * struct wmi_cmd_hdr header, only the WMI payload. Command id is
++ * provided with ATH10K_TM_ATTR_WMI_CMDID and payload in
++ * ATH10K_TM_ATTR_DATA.
++ */
++ ATH10K_TM_CMD_WMI = 3,
++};
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c
+@@ -0,0 +1,243 @@
++/*
++ * Copyright (c) 2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include "core.h"
++#include "wmi-ops.h"
++#include "debug.h"
++
++static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ieee80211_sta *sta = file->private_data;
++ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
++ struct ath10k *ar = arsta->arvif->ar;
++ char buf[32];
++ int len = 0;
++
++ mutex_lock(&ar->conf_mutex);
++ len = scnprintf(buf, sizeof(buf) - len, "aggregation mode: %s\n",
++ (arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ?
++ "auto" : "manual");
++ mutex_unlock(&ar->conf_mutex);
++
++ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t ath10k_dbg_sta_write_aggr_mode(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ieee80211_sta *sta = file->private_data;
++ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
++ struct ath10k *ar = arsta->arvif->ar;
++ u32 aggr_mode;
++ int ret;
++
++ if (kstrtouint_from_user(user_buf, count, 0, &aggr_mode))
++ return -EINVAL;
++
++ if (aggr_mode >= ATH10K_DBG_AGGR_MODE_MAX)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++ if ((ar->state != ATH10K_STATE_ON) ||
++ (aggr_mode == arsta->aggr_mode)) {
++ ret = count;
++ goto out;
++ }
++
++ ret = ath10k_wmi_addba_clear_resp(ar, arsta->arvif->vdev_id, sta->addr);
++ if (ret) {
++ ath10k_warn(ar, "failed to clear addba session ret: %d\n", ret);
++ goto out;
++ }
++
++ arsta->aggr_mode = aggr_mode;
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static const struct file_operations fops_aggr_mode = {
++ .read = ath10k_dbg_sta_read_aggr_mode,
++ .write = ath10k_dbg_sta_write_aggr_mode,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_dbg_sta_write_addba(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ieee80211_sta *sta = file->private_data;
++ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
++ struct ath10k *ar = arsta->arvif->ar;
++ u32 tid, buf_size;
++ int ret;
++ char buf[64];
++
++ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
++
++ /* make sure that buf is null terminated */
++ buf[sizeof(buf) - 1] = '\0';
++
++ ret = sscanf(buf, "%u %u", &tid, &buf_size);
++ if (ret != 2)
++ return -EINVAL;
++
++ /* Valid TID values are 0 through 15 */
++ if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++ if ((ar->state != ATH10K_STATE_ON) ||
++ (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
++ ret = count;
++ goto out;
++ }
++
++ ret = ath10k_wmi_addba_send(ar, arsta->arvif->vdev_id, sta->addr,
++ tid, buf_size);
++ if (ret) {
++ ath10k_warn(ar, "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n",
++ arsta->arvif->vdev_id, sta->addr, tid, buf_size);
++ }
++
++ ret = count;
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static const struct file_operations fops_addba = {
++ .write = ath10k_dbg_sta_write_addba,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_dbg_sta_write_addba_resp(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ieee80211_sta *sta = file->private_data;
++ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
++ struct ath10k *ar = arsta->arvif->ar;
++ u32 tid, status;
++ int ret;
++ char buf[64];
++
++ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
++
++ /* make sure that buf is null terminated */
++ buf[sizeof(buf) - 1] = '\0';
++
++ ret = sscanf(buf, "%u %u", &tid, &status);
++ if (ret != 2)
++ return -EINVAL;
++
++ /* Valid TID values are 0 through 15 */
++ if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++ if ((ar->state != ATH10K_STATE_ON) ||
++ (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
++ ret = count;
++ goto out;
++ }
++
++ ret = ath10k_wmi_addba_set_resp(ar, arsta->arvif->vdev_id, sta->addr,
++ tid, status);
++ if (ret) {
++ ath10k_warn(ar, "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n",
++ arsta->arvif->vdev_id, sta->addr, tid, status);
++ }
++ ret = count;
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static const struct file_operations fops_addba_resp = {
++ .write = ath10k_dbg_sta_write_addba_resp,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++static ssize_t ath10k_dbg_sta_write_delba(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct ieee80211_sta *sta = file->private_data;
++ struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
++ struct ath10k *ar = arsta->arvif->ar;
++ u32 tid, initiator, reason;
++ int ret;
++ char buf[64];
++
++ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
++
++ /* make sure that buf is null terminated */
++ buf[sizeof(buf) - 1] = '\0';
++
++ ret = sscanf(buf, "%u %u %u", &tid, &initiator, &reason);
++ if (ret != 3)
++ return -EINVAL;
++
++ /* Valid TID values are 0 through 15 */
++ if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
++ return -EINVAL;
++
++ mutex_lock(&ar->conf_mutex);
++ if ((ar->state != ATH10K_STATE_ON) ||
++ (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
++ ret = count;
++ goto out;
++ }
++
++ ret = ath10k_wmi_delba_send(ar, arsta->arvif->vdev_id, sta->addr,
++ tid, initiator, reason);
++ if (ret) {
++ ath10k_warn(ar, "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n",
++ arsta->arvif->vdev_id, sta->addr, tid, initiator,
++ reason);
++ }
++ ret = count;
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static const struct file_operations fops_delba = {
++ .write = ath10k_dbg_sta_write_delba,
++ .open = simple_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++ struct ieee80211_sta *sta, struct dentry *dir)
++{
++ debugfs_create_file("aggr_mode", S_IRUGO | S_IWUSR, dir, sta,
++ &fops_aggr_mode);
++ debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba);
++ debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp);
++ debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba);
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/hw.c
+@@ -0,0 +1,58 @@
++/*
++ * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/types.h>
++#include "hw.h"
++
++const struct ath10k_hw_regs qca988x_regs = {
++ .rtc_state_cold_reset_mask = 0x00000400,
++ .rtc_soc_base_address = 0x00004000,
++ .rtc_wmac_base_address = 0x00005000,
++ .soc_core_base_address = 0x00009000,
++ .ce_wrapper_base_address = 0x00057000,
++ .ce0_base_address = 0x00057400,
++ .ce1_base_address = 0x00057800,
++ .ce2_base_address = 0x00057c00,
++ .ce3_base_address = 0x00058000,
++ .ce4_base_address = 0x00058400,
++ .ce5_base_address = 0x00058800,
++ .ce6_base_address = 0x00058c00,
++ .ce7_base_address = 0x00059000,
++ .soc_reset_control_si0_rst_mask = 0x00000001,
++ .soc_reset_control_ce_rst_mask = 0x00040000,
++ .soc_chip_id_address = 0x00ec,
++ .scratch_3_address = 0x0030,
++};
++
++const struct ath10k_hw_regs qca6174_regs = {
++ .rtc_state_cold_reset_mask = 0x00002000,
++ .rtc_soc_base_address = 0x00000800,
++ .rtc_wmac_base_address = 0x00001000,
++ .soc_core_base_address = 0x0003a000,
++ .ce_wrapper_base_address = 0x00034000,
++ .ce0_base_address = 0x00034400,
++ .ce1_base_address = 0x00034800,
++ .ce2_base_address = 0x00034c00,
++ .ce3_base_address = 0x00035000,
++ .ce4_base_address = 0x00035400,
++ .ce5_base_address = 0x00035800,
++ .ce6_base_address = 0x00035c00,
++ .ce7_base_address = 0x00036000,
++ .soc_reset_control_si0_rst_mask = 0x00000000,
++ .soc_reset_control_ce_rst_mask = 0x00000001,
++ .soc_chip_id_address = 0x000f0,
++ .scratch_3_address = 0x0028,
++};
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/thermal.c
+@@ -0,0 +1,244 @@
++/*
++ * Copyright (c) 2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#include <linux/device.h>
++#include <linux/sysfs.h>
++#include <linux/thermal.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include "core.h"
++#include "debug.h"
++#include "wmi-ops.h"
++
++static int ath10k_thermal_get_active_vifs(struct ath10k *ar,
++ enum wmi_vdev_type type)
++{
++ struct ath10k_vif *arvif;
++ int count = 0;
++
++ lockdep_assert_held(&ar->conf_mutex);
++
++ list_for_each_entry(arvif, &ar->arvifs, list) {
++ if (!arvif->is_started)
++ continue;
++
++ if (!arvif->is_up)
++ continue;
++
++ if (arvif->vdev_type != type)
++ continue;
++
++ count++;
++ }
++ return count;
++}
++
++static int ath10k_thermal_get_max_dutycycle(struct thermal_cooling_device *cdev,
++ unsigned long *state)
++{
++ *state = ATH10K_QUIET_DUTY_CYCLE_MAX;
++
++ return 0;
++}
++
++static int ath10k_thermal_get_cur_dutycycle(struct thermal_cooling_device *cdev,
++ unsigned long *state)
++{
++ struct ath10k *ar = cdev->devdata;
++
++ mutex_lock(&ar->conf_mutex);
++ *state = ar->thermal.duty_cycle;
++ mutex_unlock(&ar->conf_mutex);
++
++ return 0;
++}
++
++static int ath10k_thermal_set_cur_dutycycle(struct thermal_cooling_device *cdev,
++ unsigned long duty_cycle)
++{
++ struct ath10k *ar = cdev->devdata;
++ u32 period, duration, enabled;
++ int num_bss, ret = 0;
++
++ mutex_lock(&ar->conf_mutex);
++ if (ar->state != ATH10K_STATE_ON) {
++ ret = -ENETDOWN;
++ goto out;
++ }
++
++ if (duty_cycle > ATH10K_QUIET_DUTY_CYCLE_MAX) {
++ ath10k_warn(ar, "duty cycle %ld is exceeding the limit %d\n",
++ duty_cycle, ATH10K_QUIET_DUTY_CYCLE_MAX);
++ ret = -EINVAL;
++ goto out;
++ }
++ /* TODO: Right now, thermal mitigation is handled only for single/multi
++ * vif AP mode. Since quiet param is not validated in STA mode, it needs
++ * to be investigated further to handle multi STA and multi-vif (AP+STA)
++ * mode properly.
++ */
++ num_bss = ath10k_thermal_get_active_vifs(ar, WMI_VDEV_TYPE_AP);
++ if (!num_bss) {
++ ath10k_warn(ar, "no active AP interfaces\n");
++ ret = -ENETDOWN;
++ goto out;
++ }
++ period = max(ATH10K_QUIET_PERIOD_MIN,
++ (ATH10K_QUIET_PERIOD_DEFAULT / num_bss));
++ duration = (period * duty_cycle) / 100;
++ enabled = duration ? 1 : 0;
++
++ ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration,
++ ATH10K_QUIET_START_OFFSET,
++ enabled);
++ if (ret) {
++ ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n",
++ period, duration, enabled, ret);
++ goto out;
++ }
++ ar->thermal.duty_cycle = duty_cycle;
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++static struct thermal_cooling_device_ops ath10k_thermal_ops = {
++ .get_max_state = ath10k_thermal_get_max_dutycycle,
++ .get_cur_state = ath10k_thermal_get_cur_dutycycle,
++ .set_cur_state = ath10k_thermal_set_cur_dutycycle,
++};
++
++static ssize_t ath10k_thermal_show_temp(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct ath10k *ar = dev_get_drvdata(dev);
++ int ret, temperature;
++
++ mutex_lock(&ar->conf_mutex);
++
++ /* Can't get temperature when the card is off */
++ if (ar->state != ATH10K_STATE_ON) {
++ ret = -ENETDOWN;
++ goto out;
++ }
++
++ reinit_completion(&ar->thermal.wmi_sync);
++ ret = ath10k_wmi_pdev_get_temperature(ar);
++ if (ret) {
++ ath10k_warn(ar, "failed to read temperature %d\n", ret);
++ goto out;
++ }
++
++ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
++ ret = -ESHUTDOWN;
++ goto out;
++ }
++
++ ret = wait_for_completion_timeout(&ar->thermal.wmi_sync,
++ ATH10K_THERMAL_SYNC_TIMEOUT_HZ);
++ if (ret == 0) {
++ ath10k_warn(ar, "failed to synchronize thermal read\n");
++ ret = -ETIMEDOUT;
++ goto out;
++ }
++
++ spin_lock_bh(&ar->data_lock);
++ temperature = ar->thermal.temperature;
++ spin_unlock_bh(&ar->data_lock);
++
++ /* display in millidegree celcius */
++ ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
++out:
++ mutex_unlock(&ar->conf_mutex);
++ return ret;
++}
++
++void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature)
++{
++ spin_lock_bh(&ar->data_lock);
++ ar->thermal.temperature = temperature;
++ spin_unlock_bh(&ar->data_lock);
++ complete(&ar->thermal.wmi_sync);
++}
++
++static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp,
++ NULL, 0);
++
++static struct attribute *ath10k_hwmon_attrs[] = {
++ &sensor_dev_attr_temp1_input.dev_attr.attr,
++ NULL,
++};
++ATTRIBUTE_GROUPS(ath10k_hwmon);
++
++int ath10k_thermal_register(struct ath10k *ar)
++{
++ struct thermal_cooling_device *cdev;
++ struct device *hwmon_dev;
++ int ret;
++
++ cdev = thermal_cooling_device_register("ath10k_thermal", ar,
++ &ath10k_thermal_ops);
++
++ if (IS_ERR(cdev)) {
++ ath10k_err(ar, "failed to setup thermal device result: %ld\n",
++ PTR_ERR(cdev));
++ return -EINVAL;
++ }
++
++ ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj,
++ "cooling_device");
++ if (ret) {
++ ath10k_err(ar, "failed to create thermal symlink\n");
++ goto err_cooling_destroy;
++ }
++
++ ar->thermal.cdev = cdev;
++
++ /* Do not register hwmon device when temperature reading is not
++ * supported by firmware
++ */
++ if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4)
++ return 0;
++
++ /* Avoid linking error on devm_hwmon_device_register_with_groups, I
++ * guess linux/hwmon.h is missing proper stubs. */
++ if (!config_enabled(CPTCFG_HWMON))
++ return 0;
++
++ hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,
++ "ath10k_hwmon", ar,
++ ath10k_hwmon_groups);
++ if (IS_ERR(hwmon_dev)) {
++ ath10k_err(ar, "failed to register hwmon device: %ld\n",
++ PTR_ERR(hwmon_dev));
++ ret = -EINVAL;
++ goto err_remove_link;
++ }
++ return 0;
++
++err_remove_link:
++ sysfs_remove_link(&ar->dev->kobj, "thermal_sensor");
++err_cooling_destroy:
++ thermal_cooling_device_unregister(cdev);
++ return ret;
++}
++
++void ath10k_thermal_unregister(struct ath10k *ar)
++{
++ thermal_cooling_device_unregister(ar->thermal.cdev);
++ sysfs_remove_link(&ar->dev->kobj, "cooling_device");
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/thermal.h
+@@ -0,0 +1,58 @@
++/*
++ * Copyright (c) 2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++#ifndef _THERMAL_
++#define _THERMAL_
++
++#define ATH10K_QUIET_PERIOD_DEFAULT 100
++#define ATH10K_QUIET_PERIOD_MIN 25
++#define ATH10K_QUIET_START_OFFSET 10
++#define ATH10K_QUIET_DUTY_CYCLE_MAX 70
++#define ATH10K_HWMON_NAME_LEN 15
++#define ATH10K_THERMAL_SYNC_TIMEOUT_HZ (5*HZ)
++
++struct ath10k_thermal {
++ struct thermal_cooling_device *cdev;
++ struct completion wmi_sync;
++
++ /* protected by conf_mutex */
++ u32 duty_cycle;
++ /* temperature value in Celcius degree
++ * protected by data_lock
++ */
++ int temperature;
++};
++
++#ifdef CONFIG_THERMAL
++int ath10k_thermal_register(struct ath10k *ar);
++void ath10k_thermal_unregister(struct ath10k *ar);
++void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
++#else
++static inline int ath10k_thermal_register(struct ath10k *ar)
++{
++ return 0;
++}
++
++static inline void ath10k_thermal_unregister(struct ath10k *ar)
++{
++}
++
++static inline void ath10k_thermal_event_temperature(struct ath10k *ar,
++ int temperature)
++{
++}
++
++#endif
++#endif /* _THERMAL_ */
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
+@@ -0,0 +1,1063 @@
++/*
++ * Copyright (c) 2005-2011 Atheros Communications Inc.
++ * Copyright (c) 2011-2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++
++#ifndef _WMI_OPS_H_
++#define _WMI_OPS_H_
++
++struct ath10k;
++struct sk_buff;
++
++struct wmi_ops {
++ void (*rx)(struct ath10k *ar, struct sk_buff *skb);
++ void (*map_svc)(const __le32 *in, unsigned long *out, size_t len);
++
++ int (*pull_scan)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_scan_ev_arg *arg);
++ int (*pull_mgmt_rx)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_mgmt_rx_ev_arg *arg);
++ int (*pull_ch_info)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_ch_info_ev_arg *arg);
++ int (*pull_vdev_start)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_vdev_start_ev_arg *arg);
++ int (*pull_peer_kick)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_peer_kick_ev_arg *arg);
++ int (*pull_swba)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_swba_ev_arg *arg);
++ int (*pull_phyerr)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_phyerr_ev_arg *arg);
++ int (*pull_svc_rdy)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_svc_rdy_ev_arg *arg);
++ int (*pull_rdy)(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_rdy_ev_arg *arg);
++ int (*pull_fw_stats)(struct ath10k *ar, struct sk_buff *skb,
++ struct ath10k_fw_stats *stats);
++
++ struct sk_buff *(*gen_pdev_suspend)(struct ath10k *ar, u32 suspend_opt);
++ struct sk_buff *(*gen_pdev_resume)(struct ath10k *ar);
++ struct sk_buff *(*gen_pdev_set_rd)(struct ath10k *ar, u16 rd, u16 rd2g,
++ u16 rd5g, u16 ctl2g, u16 ctl5g,
++ enum wmi_dfs_region dfs_reg);
++ struct sk_buff *(*gen_pdev_set_param)(struct ath10k *ar, u32 id,
++ u32 value);
++ struct sk_buff *(*gen_init)(struct ath10k *ar);
++ struct sk_buff *(*gen_start_scan)(struct ath10k *ar,
++ const struct wmi_start_scan_arg *arg);
++ struct sk_buff *(*gen_stop_scan)(struct ath10k *ar,
++ const struct wmi_stop_scan_arg *arg);
++ struct sk_buff *(*gen_vdev_create)(struct ath10k *ar, u32 vdev_id,
++ enum wmi_vdev_type type,
++ enum wmi_vdev_subtype subtype,
++ const u8 macaddr[ETH_ALEN]);
++ struct sk_buff *(*gen_vdev_delete)(struct ath10k *ar, u32 vdev_id);
++ struct sk_buff *(*gen_vdev_start)(struct ath10k *ar,
++ const struct wmi_vdev_start_request_arg *arg,
++ bool restart);
++ struct sk_buff *(*gen_vdev_stop)(struct ath10k *ar, u32 vdev_id);
++ struct sk_buff *(*gen_vdev_up)(struct ath10k *ar, u32 vdev_id, u32 aid,
++ const u8 *bssid);
++ struct sk_buff *(*gen_vdev_down)(struct ath10k *ar, u32 vdev_id);
++ struct sk_buff *(*gen_vdev_set_param)(struct ath10k *ar, u32 vdev_id,
++ u32 param_id, u32 param_value);
++ struct sk_buff *(*gen_vdev_install_key)(struct ath10k *ar,
++ const struct wmi_vdev_install_key_arg *arg);
++ struct sk_buff *(*gen_vdev_spectral_conf)(struct ath10k *ar,
++ const struct wmi_vdev_spectral_conf_arg *arg);
++ struct sk_buff *(*gen_vdev_spectral_enable)(struct ath10k *ar, u32 vdev_id,
++ u32 trigger, u32 enable);
++ struct sk_buff *(*gen_vdev_wmm_conf)(struct ath10k *ar, u32 vdev_id,
++ const struct wmi_wmm_params_all_arg *arg);
++ struct sk_buff *(*gen_peer_create)(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN]);
++ struct sk_buff *(*gen_peer_delete)(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN]);
++ struct sk_buff *(*gen_peer_flush)(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN],
++ u32 tid_bitmap);
++ struct sk_buff *(*gen_peer_set_param)(struct ath10k *ar, u32 vdev_id,
++ const u8 *peer_addr,
++ enum wmi_peer_param param_id,
++ u32 param_value);
++ struct sk_buff *(*gen_peer_assoc)(struct ath10k *ar,
++ const struct wmi_peer_assoc_complete_arg *arg);
++ struct sk_buff *(*gen_set_psmode)(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_ps_mode psmode);
++ struct sk_buff *(*gen_set_sta_ps)(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_powersave_param param_id,
++ u32 value);
++ struct sk_buff *(*gen_set_ap_ps)(struct ath10k *ar, u32 vdev_id,
++ const u8 *mac,
++ enum wmi_ap_ps_peer_param param_id,
++ u32 value);
++ struct sk_buff *(*gen_scan_chan_list)(struct ath10k *ar,
++ const struct wmi_scan_chan_list_arg *arg);
++ struct sk_buff *(*gen_beacon_dma)(struct ath10k *ar, u32 vdev_id,
++ const void *bcn, size_t bcn_len,
++ u32 bcn_paddr, bool dtim_zero,
++ bool deliver_cab);
++ struct sk_buff *(*gen_pdev_set_wmm)(struct ath10k *ar,
++ const struct wmi_wmm_params_all_arg *arg);
++ struct sk_buff *(*gen_request_stats)(struct ath10k *ar, u32 stats_mask);
++ struct sk_buff *(*gen_force_fw_hang)(struct ath10k *ar,
++ enum wmi_force_fw_hang_type type,
++ u32 delay_ms);
++ struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb);
++ struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable,
++ u32 log_level);
++ struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter);
++ struct sk_buff *(*gen_pktlog_disable)(struct ath10k *ar);
++ struct sk_buff *(*gen_pdev_set_quiet_mode)(struct ath10k *ar,
++ u32 period, u32 duration,
++ u32 next_offset,
++ u32 enabled);
++ struct sk_buff *(*gen_pdev_get_temperature)(struct ath10k *ar);
++ struct sk_buff *(*gen_addba_clear_resp)(struct ath10k *ar, u32 vdev_id,
++ const u8 *mac);
++ struct sk_buff *(*gen_addba_send)(struct ath10k *ar, u32 vdev_id,
++ const u8 *mac, u32 tid, u32 buf_size);
++ struct sk_buff *(*gen_addba_set_resp)(struct ath10k *ar, u32 vdev_id,
++ const u8 *mac, u32 tid,
++ u32 status);
++ struct sk_buff *(*gen_delba_send)(struct ath10k *ar, u32 vdev_id,
++ const u8 *mac, u32 tid, u32 initiator,
++ u32 reason);
++ struct sk_buff *(*gen_bcn_tmpl)(struct ath10k *ar, u32 vdev_id,
++ u32 tim_ie_offset, struct sk_buff *bcn,
++ u32 prb_caps, u32 prb_erp,
++ void *prb_ies, size_t prb_ies_len);
++ struct sk_buff *(*gen_prb_tmpl)(struct ath10k *ar, u32 vdev_id,
++ struct sk_buff *bcn);
++ struct sk_buff *(*gen_p2p_go_bcn_ie)(struct ath10k *ar, u32 vdev_id,
++ const u8 *p2p_ie);
++ struct sk_buff *(*gen_vdev_sta_uapsd)(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN],
++ const struct wmi_sta_uapsd_auto_trig_arg *args,
++ u32 num_ac);
++ struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar,
++ const struct wmi_sta_keepalive_arg *arg);
++};
++
++int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
++
++static inline int
++ath10k_wmi_rx(struct ath10k *ar, struct sk_buff *skb)
++{
++ if (WARN_ON_ONCE(!ar->wmi.ops->rx))
++ return -EOPNOTSUPP;
++
++ ar->wmi.ops->rx(ar, skb);
++ return 0;
++}
++
++static inline int
++ath10k_wmi_map_svc(struct ath10k *ar, const __le32 *in, unsigned long *out,
++ size_t len)
++{
++ if (!ar->wmi.ops->map_svc)
++ return -EOPNOTSUPP;
++
++ ar->wmi.ops->map_svc(in, out, len);
++ return 0;
++}
++
++static inline int
++ath10k_wmi_pull_scan(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_scan_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_scan)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_scan(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_mgmt_rx(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_mgmt_rx_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_mgmt_rx)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_mgmt_rx(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_ch_info(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_ch_info_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_ch_info)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_ch_info(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_vdev_start(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_vdev_start_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_vdev_start)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_vdev_start(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_peer_kick(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_peer_kick_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_peer_kick)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_peer_kick(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_swba(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_swba_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_swba)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_swba(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_phyerr(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_phyerr_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_phyerr)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_phyerr(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_svc_rdy(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_svc_rdy_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_svc_rdy)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_svc_rdy(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_rdy(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_rdy_ev_arg *arg)
++{
++ if (!ar->wmi.ops->pull_rdy)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_rdy(ar, skb, arg);
++}
++
++static inline int
++ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
++ struct ath10k_fw_stats *stats)
++{
++ if (!ar->wmi.ops->pull_fw_stats)
++ return -EOPNOTSUPP;
++
++ return ar->wmi.ops->pull_fw_stats(ar, skb, stats);
++}
++
++static inline int
++ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
++{
++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
++ struct sk_buff *skb;
++ int ret;
++
++ if (!ar->wmi.ops->gen_mgmt_tx)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_mgmt_tx(ar, msdu);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ ret = ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->mgmt_tx_cmdid);
++ if (ret)
++ return ret;
++
++ /* FIXME There's no ACK event for Management Tx. This probably
++ * shouldn't be called here either. */
++ info->flags |= IEEE80211_TX_STAT_ACK;
++ ieee80211_tx_status_irqsafe(ar->hw, msdu);
++
++ return 0;
++}
++
++static inline int
++ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g,
++ u16 ctl2g, u16 ctl5g,
++ enum wmi_dfs_region dfs_reg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_set_rd)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_set_rd(ar, rd, rd2g, rd5g, ctl2g, ctl5g,
++ dfs_reg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->pdev_set_regdomain_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_suspend)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_suspend(ar, suspend_opt);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_resume_target(struct ath10k *ar)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_resume)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_resume(ar);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_set_param)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_set_param(ar, id, value);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
++}
++
++static inline int
++ath10k_wmi_cmd_init(struct ath10k *ar)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_init)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_init(ar);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->init_cmdid);
++}
++
++static inline int
++ath10k_wmi_start_scan(struct ath10k *ar,
++ const struct wmi_start_scan_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_start_scan)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_start_scan(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
++}
++
++static inline int
++ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_stop_scan)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_stop_scan(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
++ enum wmi_vdev_type type,
++ enum wmi_vdev_subtype subtype,
++ const u8 macaddr[ETH_ALEN])
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_create)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_create(ar, vdev_id, type, subtype, macaddr);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_delete)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_delete(ar, vdev_id);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_start(struct ath10k *ar,
++ const struct wmi_vdev_start_request_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_start)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_start(ar, arg, false);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->vdev_start_request_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_restart(struct ath10k *ar,
++ const struct wmi_vdev_start_request_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_start)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_start(ar, arg, true);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->vdev_restart_request_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_stop)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_stop(ar, vdev_id);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_up)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_up(ar, vdev_id, aid, bssid);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_down)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_down(ar, vdev_id);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, u32 param_id,
++ u32 param_value)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_set_param)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_set_param(ar, vdev_id, param_id,
++ param_value);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_install_key(struct ath10k *ar,
++ const struct wmi_vdev_install_key_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_vdev_install_key)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_install_key(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->vdev_install_key_cmdid);
++}
++
++static inline int
++ath10k_wmi_vdev_spectral_conf(struct ath10k *ar,
++ const struct wmi_vdev_spectral_conf_arg *arg)
++{
++ struct sk_buff *skb;
++ u32 cmd_id;
++
++ skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ cmd_id = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid;
++ return ath10k_wmi_cmd_send(ar, skb, cmd_id);
++}
++
++static inline int
++ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
++ u32 enable)
++{
++ struct sk_buff *skb;
++ u32 cmd_id;
++
++ skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger,
++ enable);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ cmd_id = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid;
++ return ath10k_wmi_cmd_send(ar, skb, cmd_id);
++}
++
++static inline int
++ath10k_wmi_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN],
++ const struct wmi_sta_uapsd_auto_trig_arg *args,
++ u32 num_ac)
++{
++ struct sk_buff *skb;
++ u32 cmd_id;
++
++ if (!ar->wmi.ops->gen_vdev_sta_uapsd)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_vdev_sta_uapsd(ar, vdev_id, peer_addr, args,
++ num_ac);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ cmd_id = ar->wmi.cmd->sta_uapsd_auto_trig_cmdid;
++ return ath10k_wmi_cmd_send(ar, skb, cmd_id);
++}
++
++static inline int
++ath10k_wmi_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
++ const struct wmi_wmm_params_all_arg *arg)
++{
++ struct sk_buff *skb;
++ u32 cmd_id;
++
++ skb = ar->wmi.ops->gen_vdev_wmm_conf(ar, vdev_id, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ cmd_id = ar->wmi.cmd->vdev_set_wmm_params_cmdid;
++ return ath10k_wmi_cmd_send(ar, skb, cmd_id);
++}
++
++static inline int
++ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN])
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_peer_create)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_peer_create(ar, vdev_id, peer_addr);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
++}
++
++static inline int
++ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN])
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_peer_delete)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_peer_delete(ar, vdev_id, peer_addr);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
++}
++
++static inline int
++ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_peer_flush)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_peer_flush(ar, vdev_id, peer_addr, tid_bitmap);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
++}
++
++static inline int
++ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, const u8 *peer_addr,
++ enum wmi_peer_param param_id, u32 param_value)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_peer_set_param)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_peer_set_param(ar, vdev_id, peer_addr, param_id,
++ param_value);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
++}
++
++static inline int
++ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_ps_mode psmode)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_set_psmode)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_set_psmode(ar, vdev_id, psmode);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->sta_powersave_mode_cmdid);
++}
++
++static inline int
++ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_powersave_param param_id, u32 value)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_set_sta_ps)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_set_sta_ps(ar, vdev_id, param_id, value);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->sta_powersave_param_cmdid);
++}
++
++static inline int
++ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ enum wmi_ap_ps_peer_param param_id, u32 value)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_set_ap_ps)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_set_ap_ps(ar, vdev_id, mac, param_id, value);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->ap_ps_peer_param_cmdid);
++}
++
++static inline int
++ath10k_wmi_scan_chan_list(struct ath10k *ar,
++ const struct wmi_scan_chan_list_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_scan_chan_list)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_scan_chan_list(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
++}
++
++static inline int
++ath10k_wmi_peer_assoc(struct ath10k *ar,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_peer_assoc)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_peer_assoc(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
++}
++
++static inline int
++ath10k_wmi_beacon_send_ref_nowait(struct ath10k *ar, u32 vdev_id,
++ const void *bcn, size_t bcn_len,
++ u32 bcn_paddr, bool dtim_zero,
++ bool deliver_cab)
++{
++ struct sk_buff *skb;
++ int ret;
++
++ if (!ar->wmi.ops->gen_beacon_dma)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_beacon_dma(ar, vdev_id, bcn, bcn_len, bcn_paddr,
++ dtim_zero, deliver_cab);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ ret = ath10k_wmi_cmd_send_nowait(ar, skb,
++ ar->wmi.cmd->pdev_send_bcn_cmdid);
++ if (ret) {
++ dev_kfree_skb(skb);
++ return ret;
++ }
++
++ return 0;
++}
++
++static inline int
++ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
++ const struct wmi_wmm_params_all_arg *arg)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_set_wmm)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_set_wmm(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->pdev_set_wmm_params_cmdid);
++}
++
++static inline int
++ath10k_wmi_request_stats(struct ath10k *ar, u32 stats_mask)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_request_stats)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_request_stats(ar, stats_mask);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
++}
++
++static inline int
++ath10k_wmi_force_fw_hang(struct ath10k *ar,
++ enum wmi_force_fw_hang_type type, u32 delay_ms)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_force_fw_hang)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_force_fw_hang(ar, type, delay_ms);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
++}
++
++static inline int
++ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable, u32 log_level)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_dbglog_cfg)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_dbglog_cfg(ar, module_enable, log_level);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 filter)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pktlog_enable)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pktlog_enable(ar, filter);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_pktlog_enable_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pktlog_disable)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pktlog_disable(ar);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->pdev_pktlog_disable_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_set_quiet_mode(struct ath10k *ar, u32 period, u32 duration,
++ u32 next_offset, u32 enabled)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_set_quiet_mode)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_set_quiet_mode(ar, period, duration,
++ next_offset, enabled);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->pdev_set_quiet_mode_cmdid);
++}
++
++static inline int
++ath10k_wmi_pdev_get_temperature(struct ath10k *ar)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_pdev_get_temperature)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_pdev_get_temperature(ar);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->pdev_get_temperature_cmdid);
++}
++
++static inline int
++ath10k_wmi_addba_clear_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_addba_clear_resp)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_addba_clear_resp(ar, vdev_id, mac);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->addba_clear_resp_cmdid);
++}
++
++static inline int
++ath10k_wmi_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ u32 tid, u32 buf_size)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_addba_send)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_addba_send(ar, vdev_id, mac, tid, buf_size);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->addba_send_cmdid);
++}
++
++static inline int
++ath10k_wmi_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ u32 tid, u32 status)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_addba_set_resp)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_addba_set_resp(ar, vdev_id, mac, tid, status);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->addba_set_resp_cmdid);
++}
++
++static inline int
++ath10k_wmi_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ u32 tid, u32 initiator, u32 reason)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_delba_send)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_delba_send(ar, vdev_id, mac, tid, initiator,
++ reason);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb,
++ ar->wmi.cmd->delba_send_cmdid);
++}
++
++static inline int
++ath10k_wmi_bcn_tmpl(struct ath10k *ar, u32 vdev_id, u32 tim_ie_offset,
++ struct sk_buff *bcn, u32 prb_caps, u32 prb_erp,
++ void *prb_ies, size_t prb_ies_len)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_bcn_tmpl)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_bcn_tmpl(ar, vdev_id, tim_ie_offset, bcn,
++ prb_caps, prb_erp, prb_ies,
++ prb_ies_len);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->bcn_tmpl_cmdid);
++}
++
++static inline int
++ath10k_wmi_prb_tmpl(struct ath10k *ar, u32 vdev_id, struct sk_buff *prb)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_prb_tmpl)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_prb_tmpl(ar, vdev_id, prb);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->prb_tmpl_cmdid);
++}
++
++static inline int
++ath10k_wmi_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id, const u8 *p2p_ie)
++{
++ struct sk_buff *skb;
++
++ if (!ar->wmi.ops->gen_p2p_go_bcn_ie)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_p2p_go_bcn_ie(ar, vdev_id, p2p_ie);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->p2p_go_set_beacon_ie);
++}
++
++static inline int
++ath10k_wmi_sta_keepalive(struct ath10k *ar,
++ const struct wmi_sta_keepalive_arg *arg)
++{
++ struct sk_buff *skb;
++ u32 cmd_id;
++
++ if (!ar->wmi.ops->gen_sta_keepalive)
++ return -EOPNOTSUPP;
++
++ skb = ar->wmi.ops->gen_sta_keepalive(ar, arg);
++ if (IS_ERR(skb))
++ return PTR_ERR(skb);
++
++ cmd_id = ar->wmi.cmd->sta_keepalive_cmd;
++ return ath10k_wmi_cmd_send(ar, skb, cmd_id);
++}
++
++#endif
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+@@ -0,0 +1,2796 @@
++/*
++ * Copyright (c) 2005-2011 Atheros Communications Inc.
++ * Copyright (c) 2011-2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++#include "core.h"
++#include "debug.h"
++#include "hw.h"
++#include "wmi.h"
++#include "wmi-ops.h"
++#include "wmi-tlv.h"
++
++/***************/
++/* TLV helpers */
++/**************/
++
++struct wmi_tlv_policy {
++ size_t min_len;
++};
++
++static const struct wmi_tlv_policy wmi_tlv_policies[] = {
++ [WMI_TLV_TAG_ARRAY_BYTE]
++ = { .min_len = sizeof(u8) },
++ [WMI_TLV_TAG_ARRAY_UINT32]
++ = { .min_len = sizeof(u32) },
++ [WMI_TLV_TAG_STRUCT_SCAN_EVENT]
++ = { .min_len = sizeof(struct wmi_scan_event) },
++ [WMI_TLV_TAG_STRUCT_MGMT_RX_HDR]
++ = { .min_len = sizeof(struct wmi_tlv_mgmt_rx_ev) },
++ [WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT]
++ = { .min_len = sizeof(struct wmi_chan_info_event) },
++ [WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT]
++ = { .min_len = sizeof(struct wmi_vdev_start_response_event) },
++ [WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT]
++ = { .min_len = sizeof(struct wmi_peer_sta_kickout_event) },
++ [WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT]
++ = { .min_len = sizeof(struct wmi_host_swba_event) },
++ [WMI_TLV_TAG_STRUCT_TIM_INFO]
++ = { .min_len = sizeof(struct wmi_tim_info) },
++ [WMI_TLV_TAG_STRUCT_P2P_NOA_INFO]
++ = { .min_len = sizeof(struct wmi_p2p_noa_info) },
++ [WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT]
++ = { .min_len = sizeof(struct wmi_tlv_svc_rdy_ev) },
++ [WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES]
++ = { .min_len = sizeof(struct hal_reg_capabilities) },
++ [WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ]
++ = { .min_len = sizeof(struct wlan_host_mem_req) },
++ [WMI_TLV_TAG_STRUCT_READY_EVENT]
++ = { .min_len = sizeof(struct wmi_tlv_rdy_ev) },
++ [WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT]
++ = { .min_len = sizeof(struct wmi_tlv_bcn_tx_status_ev) },
++ [WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT]
++ = { .min_len = sizeof(struct wmi_tlv_diag_data_ev) },
++};
++
++static int
++ath10k_wmi_tlv_iter(struct ath10k *ar, const void *ptr, size_t len,
++ int (*iter)(struct ath10k *ar, u16 tag, u16 len,
++ const void *ptr, void *data),
++ void *data)
++{
++ const void *begin = ptr;
++ const struct wmi_tlv *tlv;
++ u16 tlv_tag, tlv_len;
++ int ret;
++
++ while (len > 0) {
++ if (len < sizeof(*tlv)) {
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n",
++ ptr - begin, len, sizeof(*tlv));
++ return -EINVAL;
++ }
++
++ tlv = ptr;
++ tlv_tag = __le16_to_cpu(tlv->tag);
++ tlv_len = __le16_to_cpu(tlv->len);
++ ptr += sizeof(*tlv);
++ len -= sizeof(*tlv);
++
++ if (tlv_len > len) {
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n",
++ tlv_tag, ptr - begin, len, tlv_len);
++ return -EINVAL;
++ }
++
++ if (tlv_tag < ARRAY_SIZE(wmi_tlv_policies) &&
++ wmi_tlv_policies[tlv_tag].min_len &&
++ wmi_tlv_policies[tlv_tag].min_len > tlv_len) {
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi tlv parse failure of tag %hhu at byte %zd (%hhu bytes is less than min length %zu)\n",
++ tlv_tag, ptr - begin, tlv_len,
++ wmi_tlv_policies[tlv_tag].min_len);
++ return -EINVAL;
++ }
++
++ ret = iter(ar, tlv_tag, tlv_len, ptr, data);
++ if (ret)
++ return ret;
++
++ ptr += tlv_len;
++ len -= tlv_len;
++ }
++
++ return 0;
++}
++
++static int ath10k_wmi_tlv_iter_parse(struct ath10k *ar, u16 tag, u16 len,
++ const void *ptr, void *data)
++{
++ const void **tb = data;
++
++ if (tag < WMI_TLV_TAG_MAX)
++ tb[tag] = ptr;
++
++ return 0;
++}
++
++static int ath10k_wmi_tlv_parse(struct ath10k *ar, const void **tb,
++ const void *ptr, size_t len)
++{
++ return ath10k_wmi_tlv_iter(ar, ptr, len, ath10k_wmi_tlv_iter_parse,
++ (void *)tb);
++}
++
++static const void **
++ath10k_wmi_tlv_parse_alloc(struct ath10k *ar, const void *ptr,
++ size_t len, gfp_t gfp)
++{
++ const void **tb;
++ int ret;
++
++ tb = kzalloc(sizeof(*tb) * WMI_TLV_TAG_MAX, gfp);
++ if (!tb)
++ return ERR_PTR(-ENOMEM);
++
++ ret = ath10k_wmi_tlv_parse(ar, tb, ptr, len);
++ if (ret) {
++ kfree(tb);
++ return ERR_PTR(ret);
++ }
++
++ return tb;
++}
++
++static u16 ath10k_wmi_tlv_len(const void *ptr)
++{
++ return __le16_to_cpu((((const struct wmi_tlv *)ptr) - 1)->len);
++}
++
++/**************/
++/* TLV events */
++/**************/
++static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar,
++ struct sk_buff *skb)
++{
++ const void **tb;
++ const struct wmi_tlv_bcn_tx_status_ev *ev;
++ u32 vdev_id, tx_status;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT];
++ if (!ev) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ tx_status = __le32_to_cpu(ev->tx_status);
++ vdev_id = __le32_to_cpu(ev->vdev_id);
++
++ switch (tx_status) {
++ case WMI_TLV_BCN_TX_STATUS_OK:
++ break;
++ case WMI_TLV_BCN_TX_STATUS_XRETRY:
++ case WMI_TLV_BCN_TX_STATUS_DROP:
++ case WMI_TLV_BCN_TX_STATUS_FILTERED:
++ /* FIXME: It's probably worth telling mac80211 to stop the
++ * interface as it is crippled.
++ */
++ ath10k_warn(ar, "received bcn tmpl tx status on vdev %i: %d",
++ vdev_id, tx_status);
++ break;
++ }
++
++ kfree(tb);
++ return 0;
++}
++
++static int ath10k_wmi_tlv_event_diag_data(struct ath10k *ar,
++ struct sk_buff *skb)
++{
++ const void **tb;
++ const struct wmi_tlv_diag_data_ev *ev;
++ const struct wmi_tlv_diag_item *item;
++ const void *data;
++ int ret, num_items, len;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT];
++ data = tb[WMI_TLV_TAG_ARRAY_BYTE];
++ if (!ev || !data) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ num_items = __le32_to_cpu(ev->num_items);
++ len = ath10k_wmi_tlv_len(data);
++
++ while (num_items--) {
++ if (len == 0)
++ break;
++ if (len < sizeof(*item)) {
++ ath10k_warn(ar, "failed to parse diag data: can't fit item header\n");
++ break;
++ }
++
++ item = data;
++
++ if (len < sizeof(*item) + __le16_to_cpu(item->len)) {
++ ath10k_warn(ar, "failed to parse diag data: item is too long\n");
++ break;
++ }
++
++ trace_ath10k_wmi_diag_container(ar,
++ item->type,
++ __le32_to_cpu(item->timestamp),
++ __le32_to_cpu(item->code),
++ __le16_to_cpu(item->len),
++ item->payload);
++
++ len -= sizeof(*item);
++ len -= roundup(__le16_to_cpu(item->len), 4);
++
++ data += sizeof(*item);
++ data += roundup(__le16_to_cpu(item->len), 4);
++ }
++
++ if (num_items != -1 || len != 0)
++ ath10k_warn(ar, "failed to parse diag data event: num_items %d len %d\n",
++ num_items, len);
++
++ kfree(tb);
++ return 0;
++}
++
++static int ath10k_wmi_tlv_event_diag(struct ath10k *ar,
++ struct sk_buff *skb)
++{
++ const void **tb;
++ const void *data;
++ int ret, len;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ data = tb[WMI_TLV_TAG_ARRAY_BYTE];
++ if (!data) {
++ kfree(tb);
++ return -EPROTO;
++ }
++ len = ath10k_wmi_tlv_len(data);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv diag event len %d\n", len);
++ trace_ath10k_wmi_diag(ar, data, len);
++
++ kfree(tb);
++ return 0;
++}
++
++/***********/
++/* TLV ops */
++/***********/
++
++static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
++{
++ struct wmi_cmd_hdr *cmd_hdr;
++ enum wmi_tlv_event_id id;
++
++ cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
++ id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
++
++ if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
++ return;
++
++ trace_ath10k_wmi_event(ar, id, skb->data, skb->len);
++
++ switch (id) {
++ case WMI_TLV_MGMT_RX_EVENTID:
++ ath10k_wmi_event_mgmt_rx(ar, skb);
++ /* mgmt_rx() owns the skb now! */
++ return;
++ case WMI_TLV_SCAN_EVENTID:
++ ath10k_wmi_event_scan(ar, skb);
++ break;
++ case WMI_TLV_CHAN_INFO_EVENTID:
++ ath10k_wmi_event_chan_info(ar, skb);
++ break;
++ case WMI_TLV_ECHO_EVENTID:
++ ath10k_wmi_event_echo(ar, skb);
++ break;
++ case WMI_TLV_DEBUG_MESG_EVENTID:
++ ath10k_wmi_event_debug_mesg(ar, skb);
++ break;
++ case WMI_TLV_UPDATE_STATS_EVENTID:
++ ath10k_wmi_event_update_stats(ar, skb);
++ break;
++ case WMI_TLV_VDEV_START_RESP_EVENTID:
++ ath10k_wmi_event_vdev_start_resp(ar, skb);
++ break;
++ case WMI_TLV_VDEV_STOPPED_EVENTID:
++ ath10k_wmi_event_vdev_stopped(ar, skb);
++ break;
++ case WMI_TLV_PEER_STA_KICKOUT_EVENTID:
++ ath10k_wmi_event_peer_sta_kickout(ar, skb);
++ break;
++ case WMI_TLV_HOST_SWBA_EVENTID:
++ ath10k_wmi_event_host_swba(ar, skb);
++ break;
++ case WMI_TLV_TBTTOFFSET_UPDATE_EVENTID:
++ ath10k_wmi_event_tbttoffset_update(ar, skb);
++ break;
++ case WMI_TLV_PHYERR_EVENTID:
++ ath10k_wmi_event_phyerr(ar, skb);
++ break;
++ case WMI_TLV_ROAM_EVENTID:
++ ath10k_wmi_event_roam(ar, skb);
++ break;
++ case WMI_TLV_PROFILE_MATCH:
++ ath10k_wmi_event_profile_match(ar, skb);
++ break;
++ case WMI_TLV_DEBUG_PRINT_EVENTID:
++ ath10k_wmi_event_debug_print(ar, skb);
++ break;
++ case WMI_TLV_PDEV_QVIT_EVENTID:
++ ath10k_wmi_event_pdev_qvit(ar, skb);
++ break;
++ case WMI_TLV_WLAN_PROFILE_DATA_EVENTID:
++ ath10k_wmi_event_wlan_profile_data(ar, skb);
++ break;
++ case WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID:
++ ath10k_wmi_event_rtt_measurement_report(ar, skb);
++ break;
++ case WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID:
++ ath10k_wmi_event_tsf_measurement_report(ar, skb);
++ break;
++ case WMI_TLV_RTT_ERROR_REPORT_EVENTID:
++ ath10k_wmi_event_rtt_error_report(ar, skb);
++ break;
++ case WMI_TLV_WOW_WAKEUP_HOST_EVENTID:
++ ath10k_wmi_event_wow_wakeup_host(ar, skb);
++ break;
++ case WMI_TLV_DCS_INTERFERENCE_EVENTID:
++ ath10k_wmi_event_dcs_interference(ar, skb);
++ break;
++ case WMI_TLV_PDEV_TPC_CONFIG_EVENTID:
++ ath10k_wmi_event_pdev_tpc_config(ar, skb);
++ break;
++ case WMI_TLV_PDEV_FTM_INTG_EVENTID:
++ ath10k_wmi_event_pdev_ftm_intg(ar, skb);
++ break;
++ case WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID:
++ ath10k_wmi_event_gtk_offload_status(ar, skb);
++ break;
++ case WMI_TLV_GTK_REKEY_FAIL_EVENTID:
++ ath10k_wmi_event_gtk_rekey_fail(ar, skb);
++ break;
++ case WMI_TLV_TX_DELBA_COMPLETE_EVENTID:
++ ath10k_wmi_event_delba_complete(ar, skb);
++ break;
++ case WMI_TLV_TX_ADDBA_COMPLETE_EVENTID:
++ ath10k_wmi_event_addba_complete(ar, skb);
++ break;
++ case WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID:
++ ath10k_wmi_event_vdev_install_key_complete(ar, skb);
++ break;
++ case WMI_TLV_SERVICE_READY_EVENTID:
++ ath10k_wmi_event_service_ready(ar, skb);
++ break;
++ case WMI_TLV_READY_EVENTID:
++ ath10k_wmi_event_ready(ar, skb);
++ break;
++ case WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID:
++ ath10k_wmi_tlv_event_bcn_tx_status(ar, skb);
++ break;
++ case WMI_TLV_DIAG_DATA_CONTAINER_EVENTID:
++ ath10k_wmi_tlv_event_diag_data(ar, skb);
++ break;
++ case WMI_TLV_DIAG_EVENTID:
++ ath10k_wmi_tlv_event_diag(ar, skb);
++ break;
++ default:
++ ath10k_warn(ar, "Unknown eventid: %d\n", id);
++ break;
++ }
++
++ dev_kfree_skb(skb);
++}
++
++static int ath10k_wmi_tlv_op_pull_scan_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_scan_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_scan_event *ev;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_SCAN_EVENT];
++ if (!ev) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ arg->event_type = ev->event_type;
++ arg->reason = ev->reason;
++ arg->channel_freq = ev->channel_freq;
++ arg->scan_req_id = ev->scan_req_id;
++ arg->scan_id = ev->scan_id;
++ arg->vdev_id = ev->vdev_id;
++
++ kfree(tb);
++ return 0;
++}
++
++static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_mgmt_rx_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_tlv_mgmt_rx_ev *ev;
++ const u8 *frame;
++ u32 msdu_len;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_MGMT_RX_HDR];
++ frame = tb[WMI_TLV_TAG_ARRAY_BYTE];
++
++ if (!ev || !frame) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ arg->channel = ev->channel;
++ arg->buf_len = ev->buf_len;
++ arg->status = ev->status;
++ arg->snr = ev->snr;
++ arg->phy_mode = ev->phy_mode;
++ arg->rate = ev->rate;
++
++ msdu_len = __le32_to_cpu(arg->buf_len);
++
++ if (skb->len < (frame - skb->data) + msdu_len) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ /* shift the sk_buff to point to `frame` */
++ skb_trim(skb, 0);
++ skb_put(skb, frame - skb->data);
++ skb_pull(skb, frame - skb->data);
++ skb_put(skb, msdu_len);
++
++ kfree(tb);
++ return 0;
++}
++
++static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_ch_info_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_chan_info_event *ev;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT];
++ if (!ev) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ arg->err_code = ev->err_code;
++ arg->freq = ev->freq;
++ arg->cmd_flags = ev->cmd_flags;
++ arg->noise_floor = ev->noise_floor;
++ arg->rx_clear_count = ev->rx_clear_count;
++ arg->cycle_count = ev->cycle_count;
++
++ kfree(tb);
++ return 0;
++}
++
++static int
++ath10k_wmi_tlv_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb,
++ struct wmi_vdev_start_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_vdev_start_response_event *ev;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT];
++ if (!ev) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ skb_pull(skb, sizeof(*ev));
++ arg->vdev_id = ev->vdev_id;
++ arg->req_id = ev->req_id;
++ arg->resp_type = ev->resp_type;
++ arg->status = ev->status;
++
++ kfree(tb);
++ return 0;
++}
++
++static int ath10k_wmi_tlv_op_pull_peer_kick_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_peer_kick_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_peer_sta_kickout_event *ev;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT];
++ if (!ev) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ arg->mac_addr = ev->peer_macaddr.addr;
++
++ kfree(tb);
++ return 0;
++}
++
++struct wmi_tlv_swba_parse {
++ const struct wmi_host_swba_event *ev;
++ bool tim_done;
++ bool noa_done;
++ size_t n_tim;
++ size_t n_noa;
++ struct wmi_swba_ev_arg *arg;
++};
++
++static int ath10k_wmi_tlv_swba_tim_parse(struct ath10k *ar, u16 tag, u16 len,
++ const void *ptr, void *data)
++{
++ struct wmi_tlv_swba_parse *swba = data;
++
++ if (tag != WMI_TLV_TAG_STRUCT_TIM_INFO)
++ return -EPROTO;
++
++ if (swba->n_tim >= ARRAY_SIZE(swba->arg->tim_info))
++ return -ENOBUFS;
++
++ swba->arg->tim_info[swba->n_tim++] = ptr;
++ return 0;
++}
++
++static int ath10k_wmi_tlv_swba_noa_parse(struct ath10k *ar, u16 tag, u16 len,
++ const void *ptr, void *data)
++{
++ struct wmi_tlv_swba_parse *swba = data;
++
++ if (tag != WMI_TLV_TAG_STRUCT_P2P_NOA_INFO)
++ return -EPROTO;
++
++ if (swba->n_noa >= ARRAY_SIZE(swba->arg->noa_info))
++ return -ENOBUFS;
++
++ swba->arg->noa_info[swba->n_noa++] = ptr;
++ return 0;
++}
++
++static int ath10k_wmi_tlv_swba_parse(struct ath10k *ar, u16 tag, u16 len,
++ const void *ptr, void *data)
++{
++ struct wmi_tlv_swba_parse *swba = data;
++ int ret;
++
++ switch (tag) {
++ case WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT:
++ swba->ev = ptr;
++ break;
++ case WMI_TLV_TAG_ARRAY_STRUCT:
++ if (!swba->tim_done) {
++ swba->tim_done = true;
++ ret = ath10k_wmi_tlv_iter(ar, ptr, len,
++ ath10k_wmi_tlv_swba_tim_parse,
++ swba);
++ if (ret)
++ return ret;
++ } else if (!swba->noa_done) {
++ swba->noa_done = true;
++ ret = ath10k_wmi_tlv_iter(ar, ptr, len,
++ ath10k_wmi_tlv_swba_noa_parse,
++ swba);
++ if (ret)
++ return ret;
++ }
++ break;
++ default:
++ break;
++ }
++ return 0;
++}
++
++static int ath10k_wmi_tlv_op_pull_swba_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_swba_ev_arg *arg)
++{
++ struct wmi_tlv_swba_parse swba = { .arg = arg };
++ u32 map;
++ size_t n_vdevs;
++ int ret;
++
++ ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len,
++ ath10k_wmi_tlv_swba_parse, &swba);
++ if (ret) {
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ if (!swba.ev)
++ return -EPROTO;
++
++ arg->vdev_map = swba.ev->vdev_map;
++
++ for (map = __le32_to_cpu(arg->vdev_map), n_vdevs = 0; map; map >>= 1)
++ if (map & BIT(0))
++ n_vdevs++;
++
++ if (n_vdevs != swba.n_tim ||
++ n_vdevs != swba.n_noa)
++ return -EPROTO;
++
++ return 0;
++}
++
++static int ath10k_wmi_tlv_op_pull_phyerr_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_phyerr_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_tlv_phyerr_ev *ev;
++ const void *phyerrs;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR];
++ phyerrs = tb[WMI_TLV_TAG_ARRAY_BYTE];
++
++ if (!ev || !phyerrs) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ arg->num_phyerrs = ev->num_phyerrs;
++ arg->tsf_l32 = ev->tsf_l32;
++ arg->tsf_u32 = ev->tsf_u32;
++ arg->buf_len = ev->buf_len;
++ arg->phyerrs = phyerrs;
++
++ kfree(tb);
++ return 0;
++}
++
++#define WMI_TLV_ABI_VER_NS0 0x5F414351
++#define WMI_TLV_ABI_VER_NS1 0x00004C4D
++#define WMI_TLV_ABI_VER_NS2 0x00000000
++#define WMI_TLV_ABI_VER_NS3 0x00000000
++
++#define WMI_TLV_ABI_VER0_MAJOR 1
++#define WMI_TLV_ABI_VER0_MINOR 0
++#define WMI_TLV_ABI_VER0 ((((WMI_TLV_ABI_VER0_MAJOR) << 24) & 0xFF000000) | \
++ (((WMI_TLV_ABI_VER0_MINOR) << 0) & 0x00FFFFFF))
++#define WMI_TLV_ABI_VER1 53
++
++static int
++ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len,
++ const void *ptr, void *data)
++{
++ struct wmi_svc_rdy_ev_arg *arg = data;
++ int i;
++
++ if (tag != WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ)
++ return -EPROTO;
++
++ for (i = 0; i < ARRAY_SIZE(arg->mem_reqs); i++) {
++ if (!arg->mem_reqs[i]) {
++ arg->mem_reqs[i] = ptr;
++ return 0;
++ }
++ }
++
++ return -ENOMEM;
++}
++
++static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_svc_rdy_ev_arg *arg)
++{
++ const void **tb;
++ const struct hal_reg_capabilities *reg;
++ const struct wmi_tlv_svc_rdy_ev *ev;
++ const __le32 *svc_bmap;
++ const struct wlan_host_mem_req *mem_reqs;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT];
++ reg = tb[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES];
++ svc_bmap = tb[WMI_TLV_TAG_ARRAY_UINT32];
++ mem_reqs = tb[WMI_TLV_TAG_ARRAY_STRUCT];
++
++ if (!ev || !reg || !svc_bmap || !mem_reqs) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ /* This is an internal ABI compatibility check for WMI TLV so check it
++ * here instead of the generic WMI code.
++ */
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi tlv abi 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x\n",
++ __le32_to_cpu(ev->abi.abi_ver0), WMI_TLV_ABI_VER0,
++ __le32_to_cpu(ev->abi.abi_ver_ns0), WMI_TLV_ABI_VER_NS0,
++ __le32_to_cpu(ev->abi.abi_ver_ns1), WMI_TLV_ABI_VER_NS1,
++ __le32_to_cpu(ev->abi.abi_ver_ns2), WMI_TLV_ABI_VER_NS2,
++ __le32_to_cpu(ev->abi.abi_ver_ns3), WMI_TLV_ABI_VER_NS3);
++
++ if (__le32_to_cpu(ev->abi.abi_ver0) != WMI_TLV_ABI_VER0 ||
++ __le32_to_cpu(ev->abi.abi_ver_ns0) != WMI_TLV_ABI_VER_NS0 ||
++ __le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 ||
++ __le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 ||
++ __le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) {
++ kfree(tb);
++ return -ENOTSUPP;
++ }
++
++ arg->min_tx_power = ev->hw_min_tx_power;
++ arg->max_tx_power = ev->hw_max_tx_power;
++ arg->ht_cap = ev->ht_cap_info;
++ arg->vht_cap = ev->vht_cap_info;
++ arg->sw_ver0 = ev->abi.abi_ver0;
++ arg->sw_ver1 = ev->abi.abi_ver1;
++ arg->fw_build = ev->fw_build_vers;
++ arg->phy_capab = ev->phy_capability;
++ arg->num_rf_chains = ev->num_rf_chains;
++ arg->eeprom_rd = reg->eeprom_rd;
++ arg->num_mem_reqs = ev->num_mem_reqs;
++ arg->service_map = svc_bmap;
++ arg->service_map_len = ath10k_wmi_tlv_len(svc_bmap);
++
++ ret = ath10k_wmi_tlv_iter(ar, mem_reqs, ath10k_wmi_tlv_len(mem_reqs),
++ ath10k_wmi_tlv_parse_mem_reqs, arg);
++ if (ret) {
++ kfree(tb);
++ ath10k_warn(ar, "failed to parse mem_reqs tlv: %d\n", ret);
++ return ret;
++ }
++
++ kfree(tb);
++ return 0;
++}
++
++static int ath10k_wmi_tlv_op_pull_rdy_ev(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct wmi_rdy_ev_arg *arg)
++{
++ const void **tb;
++ const struct wmi_tlv_rdy_ev *ev;
++ int ret;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_READY_EVENT];
++ if (!ev) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ arg->sw_version = ev->abi.abi_ver0;
++ arg->abi_version = ev->abi.abi_ver1;
++ arg->status = ev->status;
++ arg->mac_addr = ev->mac_addr.addr;
++
++ kfree(tb);
++ return 0;
++}
++
++static void ath10k_wmi_tlv_pull_vdev_stats(const struct wmi_tlv_vdev_stats *src,
++ struct ath10k_fw_stats_vdev *dst)
++{
++ int i;
++
++ dst->vdev_id = __le32_to_cpu(src->vdev_id);
++ dst->beacon_snr = __le32_to_cpu(src->beacon_snr);
++ dst->data_snr = __le32_to_cpu(src->data_snr);
++ dst->num_rx_frames = __le32_to_cpu(src->num_rx_frames);
++ dst->num_rts_fail = __le32_to_cpu(src->num_rts_fail);
++ dst->num_rts_success = __le32_to_cpu(src->num_rts_success);
++ dst->num_rx_err = __le32_to_cpu(src->num_rx_err);
++ dst->num_rx_discard = __le32_to_cpu(src->num_rx_discard);
++ dst->num_tx_not_acked = __le32_to_cpu(src->num_tx_not_acked);
++
++ for (i = 0; i < ARRAY_SIZE(src->num_tx_frames); i++)
++ dst->num_tx_frames[i] =
++ __le32_to_cpu(src->num_tx_frames[i]);
++
++ for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_retries); i++)
++ dst->num_tx_frames_retries[i] =
++ __le32_to_cpu(src->num_tx_frames_retries[i]);
++
++ for (i = 0; i < ARRAY_SIZE(src->num_tx_frames_failures); i++)
++ dst->num_tx_frames_failures[i] =
++ __le32_to_cpu(src->num_tx_frames_failures[i]);
++
++ for (i = 0; i < ARRAY_SIZE(src->tx_rate_history); i++)
++ dst->tx_rate_history[i] =
++ __le32_to_cpu(src->tx_rate_history[i]);
++
++ for (i = 0; i < ARRAY_SIZE(src->beacon_rssi_history); i++)
++ dst->beacon_rssi_history[i] =
++ __le32_to_cpu(src->beacon_rssi_history[i]);
++}
++
++static int ath10k_wmi_tlv_op_pull_fw_stats(struct ath10k *ar,
++ struct sk_buff *skb,
++ struct ath10k_fw_stats *stats)
++{
++ const void **tb;
++ const struct wmi_tlv_stats_ev *ev;
++ const void *data;
++ u32 num_pdev_stats;
++ u32 num_vdev_stats;
++ u32 num_peer_stats;
++ u32 num_bcnflt_stats;
++ u32 num_chan_stats;
++ size_t data_len;
++ int ret;
++ int i;
++
++ tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
++ if (IS_ERR(tb)) {
++ ret = PTR_ERR(tb);
++ ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
++ return ret;
++ }
++
++ ev = tb[WMI_TLV_TAG_STRUCT_STATS_EVENT];
++ data = tb[WMI_TLV_TAG_ARRAY_BYTE];
++
++ if (!ev || !data) {
++ kfree(tb);
++ return -EPROTO;
++ }
++
++ data_len = ath10k_wmi_tlv_len(data);
++ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
++ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
++ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
++ num_bcnflt_stats = __le32_to_cpu(ev->num_bcnflt_stats);
++ num_chan_stats = __le32_to_cpu(ev->num_chan_stats);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi tlv stats update pdev %i vdev %i peer %i bcnflt %i chan %i\n",
++ num_pdev_stats, num_vdev_stats, num_peer_stats,
++ num_bcnflt_stats, num_chan_stats);
++
++ for (i = 0; i < num_pdev_stats; i++) {
++ const struct wmi_pdev_stats *src;
++ struct ath10k_fw_stats_pdev *dst;
++
++ src = data;
++ if (data_len < sizeof(*src))
++ return -EPROTO;
++
++ data += sizeof(*src);
++ data_len -= sizeof(*src);
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_pull_pdev_stats_base(&src->base, dst);
++ ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst);
++ ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst);
++ list_add_tail(&dst->list, &stats->pdevs);
++ }
++
++ for (i = 0; i < num_vdev_stats; i++) {
++ const struct wmi_tlv_vdev_stats *src;
++ struct ath10k_fw_stats_vdev *dst;
++
++ src = data;
++ if (data_len < sizeof(*src))
++ return -EPROTO;
++
++ data += sizeof(*src);
++ data_len -= sizeof(*src);
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_tlv_pull_vdev_stats(src, dst);
++ list_add_tail(&dst->list, &stats->vdevs);
++ }
++
++ for (i = 0; i < num_peer_stats; i++) {
++ const struct wmi_10x_peer_stats *src;
++ struct ath10k_fw_stats_peer *dst;
++
++ src = data;
++ if (data_len < sizeof(*src))
++ return -EPROTO;
++
++ data += sizeof(*src);
++ data_len -= sizeof(*src);
++
++ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
++ if (!dst)
++ continue;
++
++ ath10k_wmi_pull_peer_stats(&src->old, dst);
++ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
++ list_add_tail(&dst->list, &stats->peers);
++ }
++
++ kfree(tb);
++ return 0;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pdev_suspend(struct ath10k *ar, u32 opt)
++{
++ struct wmi_tlv_pdev_suspend *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->opt = __cpu_to_le32(opt);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev suspend\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pdev_resume(struct ath10k *ar)
++{
++ struct wmi_tlv_resume_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->reserved = __cpu_to_le32(0);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev resume\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pdev_set_rd(struct ath10k *ar,
++ u16 rd, u16 rd2g, u16 rd5g,
++ u16 ctl2g, u16 ctl5g,
++ enum wmi_dfs_region dfs_reg)
++{
++ struct wmi_tlv_pdev_set_rd_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->regd = __cpu_to_le32(rd);
++ cmd->regd_2ghz = __cpu_to_le32(rd2g);
++ cmd->regd_5ghz = __cpu_to_le32(rd5g);
++ cmd->conform_limit_2ghz = __cpu_to_le32(rd2g);
++ cmd->conform_limit_5ghz = __cpu_to_le32(rd5g);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set rd\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pdev_set_param(struct ath10k *ar, u32 param_id,
++ u32 param_value)
++{
++ struct wmi_tlv_pdev_set_param_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->param_id = __cpu_to_le32(param_id);
++ cmd->param_value = __cpu_to_le32(param_value);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set param\n");
++ return skb;
++}
++
++static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar)
++{
++ struct sk_buff *skb;
++ struct wmi_tlv *tlv;
++ struct wmi_tlv_init_cmd *cmd;
++ struct wmi_tlv_resource_config *cfg;
++ struct wmi_host_mem_chunks *chunks;
++ size_t len, chunks_len;
++ void *ptr;
++
++ chunks_len = ar->wmi.num_mem_chunks * sizeof(struct host_memory_chunk);
++ len = (sizeof(*tlv) + sizeof(*cmd)) +
++ (sizeof(*tlv) + sizeof(*cfg)) +
++ (sizeof(*tlv) + chunks_len);
++
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = skb->data;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_INIT_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG);
++ tlv->len = __cpu_to_le16(sizeof(*cfg));
++ cfg = (void *)tlv->value;
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cfg);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
++ tlv->len = __cpu_to_le16(chunks_len);
++ chunks = (void *)tlv->value;
++
++ ptr += sizeof(*tlv);
++ ptr += chunks_len;
++
++ cmd->abi.abi_ver0 = __cpu_to_le32(WMI_TLV_ABI_VER0);
++ cmd->abi.abi_ver1 = __cpu_to_le32(WMI_TLV_ABI_VER1);
++ cmd->abi.abi_ver_ns0 = __cpu_to_le32(WMI_TLV_ABI_VER_NS0);
++ cmd->abi.abi_ver_ns1 = __cpu_to_le32(WMI_TLV_ABI_VER_NS1);
++ cmd->abi.abi_ver_ns2 = __cpu_to_le32(WMI_TLV_ABI_VER_NS2);
++ cmd->abi.abi_ver_ns3 = __cpu_to_le32(WMI_TLV_ABI_VER_NS3);
++ cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
++
++ cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS);
++ cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS);
++
++ if (test_bit(WMI_SERVICE_RX_FULL_REORDER, ar->wmi.svc_map)) {
++ cfg->num_offload_peers = __cpu_to_le32(3);
++ cfg->num_offload_reorder_bufs = __cpu_to_le32(3);
++ } else {
++ cfg->num_offload_peers = __cpu_to_le32(0);
++ cfg->num_offload_reorder_bufs = __cpu_to_le32(0);
++ }
++
++ cfg->num_peer_keys = __cpu_to_le32(2);
++ cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS);
++ cfg->ast_skid_limit = __cpu_to_le32(0x10);
++ cfg->tx_chain_mask = __cpu_to_le32(0x7);
++ cfg->rx_chain_mask = __cpu_to_le32(0x7);
++ cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64);
++ cfg->rx_timeout_pri[1] = __cpu_to_le32(0x64);
++ cfg->rx_timeout_pri[2] = __cpu_to_le32(0x64);
++ cfg->rx_timeout_pri[3] = __cpu_to_le32(0x28);
++ cfg->rx_decap_mode = __cpu_to_le32(1);
++ cfg->scan_max_pending_reqs = __cpu_to_le32(4);
++ cfg->bmiss_offload_max_vdev = __cpu_to_le32(3);
++ cfg->roam_offload_max_vdev = __cpu_to_le32(3);
++ cfg->roam_offload_max_ap_profiles = __cpu_to_le32(8);
++ cfg->num_mcast_groups = __cpu_to_le32(0);
++ cfg->num_mcast_table_elems = __cpu_to_le32(0);
++ cfg->mcast2ucast_mode = __cpu_to_le32(0);
++ cfg->tx_dbg_log_size = __cpu_to_le32(0x400);
++ cfg->num_wds_entries = __cpu_to_le32(0x20);
++ cfg->dma_burst_size = __cpu_to_le32(0);
++ cfg->mac_aggr_delim = __cpu_to_le32(0);
++ cfg->rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(0);
++ cfg->vow_config = __cpu_to_le32(0);
++ cfg->gtk_offload_max_vdev = __cpu_to_le32(2);
++ cfg->num_msdu_desc = __cpu_to_le32(TARGET_TLV_NUM_MSDU_DESC);
++ cfg->max_frag_entries = __cpu_to_le32(2);
++ cfg->num_tdls_vdevs = __cpu_to_le32(1);
++ cfg->num_tdls_conn_table_entries = __cpu_to_le32(0x20);
++ cfg->beacon_tx_offload_max_vdev = __cpu_to_le32(2);
++ cfg->num_multicast_filter_entries = __cpu_to_le32(5);
++ cfg->num_wow_filters = __cpu_to_le32(0x16);
++ cfg->num_keep_alive_pattern = __cpu_to_le32(6);
++ cfg->keep_alive_pattern_size = __cpu_to_le32(0);
++ cfg->max_tdls_concurrent_sleep_sta = __cpu_to_le32(1);
++ cfg->max_tdls_concurrent_buffer_sta = __cpu_to_le32(1);
++
++ ath10k_wmi_put_host_mem_chunks(ar, chunks);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv init\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar,
++ const struct wmi_start_scan_arg *arg)
++{
++ struct wmi_tlv_start_scan_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len, chan_len, ssid_len, bssid_len, ie_len;
++ __le32 *chans;
++ struct wmi_ssid *ssids;
++ struct wmi_mac_addr *addrs;
++ void *ptr;
++ int i, ret;
++
++ ret = ath10k_wmi_start_scan_verify(arg);
++ if (ret)
++ return ERR_PTR(ret);
++
++ chan_len = arg->n_channels * sizeof(__le32);
++ ssid_len = arg->n_ssids * sizeof(struct wmi_ssid);
++ bssid_len = arg->n_bssids * sizeof(struct wmi_mac_addr);
++ ie_len = roundup(arg->ie_len, 4);
++ len = (sizeof(*tlv) + sizeof(*cmd)) +
++ (arg->n_channels ? sizeof(*tlv) + chan_len : 0) +
++ (arg->n_ssids ? sizeof(*tlv) + ssid_len : 0) +
++ (arg->n_bssids ? sizeof(*tlv) + bssid_len : 0) +
++ (arg->ie_len ? sizeof(*tlv) + ie_len : 0);
++
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_START_SCAN_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++
++ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
++ cmd->burst_duration_ms = __cpu_to_le32(0);
++ cmd->num_channels = __cpu_to_le32(arg->n_channels);
++ cmd->num_ssids = __cpu_to_le32(arg->n_ssids);
++ cmd->num_bssids = __cpu_to_le32(arg->n_bssids);
++ cmd->ie_len = __cpu_to_le32(arg->ie_len);
++ cmd->num_probes = __cpu_to_le32(3);
++
++ /* FIXME: There are some scan flag inconsistencies across firmwares,
++ * e.g. WMI-TLV inverts the logic behind the following flag.
++ */
++ cmd->common.scan_ctrl_flags ^= __cpu_to_le32(WMI_SCAN_FILTER_PROBE_REQ);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32);
++ tlv->len = __cpu_to_le16(chan_len);
++ chans = (void *)tlv->value;
++ for (i = 0; i < arg->n_channels; i++)
++ chans[i] = __cpu_to_le32(arg->channels[i]);
++
++ ptr += sizeof(*tlv);
++ ptr += chan_len;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT);
++ tlv->len = __cpu_to_le16(ssid_len);
++ ssids = (void *)tlv->value;
++ for (i = 0; i < arg->n_ssids; i++) {
++ ssids[i].ssid_len = __cpu_to_le32(arg->ssids[i].len);
++ memcpy(ssids[i].ssid, arg->ssids[i].ssid, arg->ssids[i].len);
++ }
++
++ ptr += sizeof(*tlv);
++ ptr += ssid_len;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT);
++ tlv->len = __cpu_to_le16(bssid_len);
++ addrs = (void *)tlv->value;
++ for (i = 0; i < arg->n_bssids; i++)
++ ether_addr_copy(addrs[i].addr, arg->bssids[i].bssid);
++
++ ptr += sizeof(*tlv);
++ ptr += bssid_len;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(ie_len);
++ memcpy(tlv->value, arg->ie, arg->ie_len);
++
++ ptr += sizeof(*tlv);
++ ptr += ie_len;
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv start scan\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_stop_scan(struct ath10k *ar,
++ const struct wmi_stop_scan_arg *arg)
++{
++ struct wmi_stop_scan_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ u32 scan_id;
++ u32 req_id;
++
++ if (arg->req_id > 0xFFF)
++ return ERR_PTR(-EINVAL);
++ if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
++ return ERR_PTR(-EINVAL);
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ scan_id = arg->u.scan_id;
++ scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
++
++ req_id = arg->req_id;
++ req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->req_type = __cpu_to_le32(arg->req_type);
++ cmd->vdev_id = __cpu_to_le32(arg->u.vdev_id);
++ cmd->scan_id = __cpu_to_le32(scan_id);
++ cmd->scan_req_id = __cpu_to_le32(req_id);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv stop scan\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_create(struct ath10k *ar,
++ u32 vdev_id,
++ enum wmi_vdev_type vdev_type,
++ enum wmi_vdev_subtype vdev_subtype,
++ const u8 mac_addr[ETH_ALEN])
++{
++ struct wmi_vdev_create_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->vdev_type = __cpu_to_le32(vdev_type);
++ cmd->vdev_subtype = __cpu_to_le32(vdev_subtype);
++ ether_addr_copy(cmd->vdev_macaddr.addr, mac_addr);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev create\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id)
++{
++ struct wmi_vdev_delete_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev delete\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_start(struct ath10k *ar,
++ const struct wmi_vdev_start_request_arg *arg,
++ bool restart)
++{
++ struct wmi_tlv_vdev_start_cmd *cmd;
++ struct wmi_channel *ch;
++ struct wmi_p2p_noa_descriptor *noa;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len;
++ void *ptr;
++ u32 flags = 0;
++
++ if (WARN_ON(arg->ssid && arg->ssid_len == 0))
++ return ERR_PTR(-EINVAL);
++ if (WARN_ON(arg->hidden_ssid && !arg->ssid))
++ return ERR_PTR(-EINVAL);
++ if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
++ return ERR_PTR(-EINVAL);
++
++ len = (sizeof(*tlv) + sizeof(*cmd)) +
++ (sizeof(*tlv) + sizeof(*ch)) +
++ (sizeof(*tlv) + 0);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ if (arg->hidden_ssid)
++ flags |= WMI_VDEV_START_HIDDEN_SSID;
++ if (arg->pmf_enabled)
++ flags |= WMI_VDEV_START_PMF_ENABLED;
++
++ ptr = (void *)skb->data;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
++ cmd->bcn_intval = __cpu_to_le32(arg->bcn_intval);
++ cmd->dtim_period = __cpu_to_le32(arg->dtim_period);
++ cmd->flags = __cpu_to_le32(flags);
++ cmd->bcn_tx_rate = __cpu_to_le32(arg->bcn_tx_rate);
++ cmd->bcn_tx_power = __cpu_to_le32(arg->bcn_tx_power);
++ cmd->disable_hw_ack = __cpu_to_le32(arg->disable_hw_ack);
++
++ if (arg->ssid) {
++ cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len);
++ memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
++ }
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL);
++ tlv->len = __cpu_to_le16(sizeof(*ch));
++ ch = (void *)tlv->value;
++ ath10k_wmi_put_wmi_channel(ch, &arg->channel);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*ch);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
++ tlv->len = 0;
++ noa = (void *)tlv->value;
++
++ /* Note: This is a nested TLV containing:
++ * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv]..
++ */
++
++ ptr += sizeof(*tlv);
++ ptr += 0;
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev start\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id)
++{
++ struct wmi_vdev_stop_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev stop\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
++ const u8 *bssid)
++
++{
++ struct wmi_vdev_up_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_UP_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->vdev_assoc_id = __cpu_to_le32(aid);
++ ether_addr_copy(cmd->vdev_bssid.addr, bssid);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev up\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id)
++{
++ struct wmi_vdev_down_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev down\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id,
++ u32 param_id, u32 param_value)
++{
++ struct wmi_vdev_set_param_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->param_id = __cpu_to_le32(param_id);
++ cmd->param_value = __cpu_to_le32(param_value);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev set param\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_install_key(struct ath10k *ar,
++ const struct wmi_vdev_install_key_arg *arg)
++{
++ struct wmi_vdev_install_key_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len;
++ void *ptr;
++
++ if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
++ return ERR_PTR(-EINVAL);
++ if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
++ return ERR_PTR(-EINVAL);
++
++ len = sizeof(*tlv) + sizeof(*cmd) +
++ sizeof(*tlv) + roundup(arg->key_len, sizeof(__le32));
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
++ cmd->key_idx = __cpu_to_le32(arg->key_idx);
++ cmd->key_flags = __cpu_to_le32(arg->key_flags);
++ cmd->key_cipher = __cpu_to_le32(arg->key_cipher);
++ cmd->key_len = __cpu_to_le32(arg->key_len);
++ cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len);
++ cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len);
++
++ if (arg->macaddr)
++ ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(roundup(arg->key_len, sizeof(__le32)));
++ if (arg->key_data)
++ memcpy(tlv->value, arg->key_data, arg->key_len);
++
++ ptr += sizeof(*tlv);
++ ptr += roundup(arg->key_len, sizeof(__le32));
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev install key\n");
++ return skb;
++}
++
++static void *ath10k_wmi_tlv_put_uapsd_ac(struct ath10k *ar, void *ptr,
++ const struct wmi_sta_uapsd_auto_trig_arg *arg)
++{
++ struct wmi_sta_uapsd_auto_trig_param *ac;
++ struct wmi_tlv *tlv;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM);
++ tlv->len = __cpu_to_le16(sizeof(*ac));
++ ac = (void *)tlv->value;
++
++ ac->wmm_ac = __cpu_to_le32(arg->wmm_ac);
++ ac->user_priority = __cpu_to_le32(arg->user_priority);
++ ac->service_interval = __cpu_to_le32(arg->service_interval);
++ ac->suspend_interval = __cpu_to_le32(arg->suspend_interval);
++ ac->delay_interval = __cpu_to_le32(arg->delay_interval);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI,
++ "wmi tlv vdev sta uapsd auto trigger ac %d prio %d svc int %d susp int %d delay int %d\n",
++ ac->wmm_ac, ac->user_priority, ac->service_interval,
++ ac->suspend_interval, ac->delay_interval);
++
++ return ptr + sizeof(*tlv) + sizeof(*ac);
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN],
++ const struct wmi_sta_uapsd_auto_trig_arg *args,
++ u32 num_ac)
++{
++ struct wmi_sta_uapsd_auto_trig_cmd_fixed_param *cmd;
++ struct wmi_sta_uapsd_auto_trig_param *ac;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len;
++ size_t ac_tlv_len;
++ void *ptr;
++ int i;
++
++ ac_tlv_len = num_ac * (sizeof(*tlv) + sizeof(*ac));
++ len = sizeof(*tlv) + sizeof(*cmd) +
++ sizeof(*tlv) + ac_tlv_len;
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->num_ac = __cpu_to_le32(num_ac);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
++ tlv->len = __cpu_to_le16(ac_tlv_len);
++ ac = (void *)tlv->value;
++
++ ptr += sizeof(*tlv);
++ for (i = 0; i < num_ac; i++)
++ ptr = ath10k_wmi_tlv_put_uapsd_ac(ar, ptr, &args[i]);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev sta uapsd auto trigger\n");
++ return skb;
++}
++
++static void *ath10k_wmi_tlv_put_wmm(void *ptr,
++ const struct wmi_wmm_params_arg *arg)
++{
++ struct wmi_wmm_params *wmm;
++ struct wmi_tlv *tlv;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_WMM_PARAMS);
++ tlv->len = __cpu_to_le16(sizeof(*wmm));
++ wmm = (void *)tlv->value;
++ ath10k_wmi_set_wmm_param(wmm, arg);
++
++ return ptr + sizeof(*tlv) + sizeof(*wmm);
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id,
++ const struct wmi_wmm_params_all_arg *arg)
++{
++ struct wmi_tlv_vdev_set_wmm_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len;
++ void *ptr;
++
++ len = sizeof(*tlv) + sizeof(*cmd);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++
++ ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[0].params, &arg->ac_be);
++ ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[1].params, &arg->ac_bk);
++ ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[2].params, &arg->ac_vi);
++ ath10k_wmi_set_wmm_param(&cmd->vdev_wmm_params[3].params, &arg->ac_vo);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev wmm conf\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_sta_keepalive(struct ath10k *ar,
++ const struct wmi_sta_keepalive_arg *arg)
++{
++ struct wmi_tlv_sta_keepalive_cmd *cmd;
++ struct wmi_sta_keepalive_arp_resp *arp;
++ struct sk_buff *skb;
++ struct wmi_tlv *tlv;
++ void *ptr;
++ size_t len;
++
++ len = sizeof(*tlv) + sizeof(*cmd) +
++ sizeof(*tlv) + sizeof(*arp);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
++ cmd->enabled = __cpu_to_le32(arg->enabled);
++ cmd->method = __cpu_to_le32(arg->method);
++ cmd->interval = __cpu_to_le32(arg->interval);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE);
++ tlv->len = __cpu_to_le16(sizeof(*arp));
++ arp = (void *)tlv->value;
++
++ arp->src_ip4_addr = arg->src_ip4_addr;
++ arp->dest_ip4_addr = arg->dest_ip4_addr;
++ ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv sta keepalive vdev %d enabled %d method %d inverval %d\n",
++ arg->vdev_id, arg->enabled, arg->method, arg->interval);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_peer_create(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN])
++{
++ struct wmi_tlv_peer_create_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->peer_type = __cpu_to_le32(WMI_TLV_PEER_TYPE_DEFAULT); /* FIXME */
++ ether_addr_copy(cmd->peer_addr.addr, peer_addr);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer create\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN])
++{
++ struct wmi_peer_delete_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer delete\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id,
++ const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
++{
++ struct wmi_peer_flush_tids_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer flush\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
++ const u8 *peer_addr,
++ enum wmi_peer_param param_id,
++ u32 param_value)
++{
++ struct wmi_peer_set_param_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->param_id = __cpu_to_le32(param_id);
++ cmd->param_value = __cpu_to_le32(param_value);
++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer set param\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_peer_assoc(struct ath10k *ar,
++ const struct wmi_peer_assoc_complete_arg *arg)
++{
++ struct wmi_tlv_peer_assoc_cmd *cmd;
++ struct wmi_vht_rate_set *vht_rate;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len, legacy_rate_len, ht_rate_len;
++ void *ptr;
++
++ if (arg->peer_mpdu_density > 16)
++ return ERR_PTR(-EINVAL);
++ if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
++ return ERR_PTR(-EINVAL);
++ if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
++ return ERR_PTR(-EINVAL);
++
++ legacy_rate_len = roundup(arg->peer_legacy_rates.num_rates,
++ sizeof(__le32));
++ ht_rate_len = roundup(arg->peer_ht_rates.num_rates, sizeof(__le32));
++ len = (sizeof(*tlv) + sizeof(*cmd)) +
++ (sizeof(*tlv) + legacy_rate_len) +
++ (sizeof(*tlv) + ht_rate_len) +
++ (sizeof(*tlv) + sizeof(*vht_rate));
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++
++ cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
++ cmd->new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1);
++ cmd->assoc_id = __cpu_to_le32(arg->peer_aid);
++ cmd->flags = __cpu_to_le32(arg->peer_flags);
++ cmd->caps = __cpu_to_le32(arg->peer_caps);
++ cmd->listen_intval = __cpu_to_le32(arg->peer_listen_intval);
++ cmd->ht_caps = __cpu_to_le32(arg->peer_ht_caps);
++ cmd->max_mpdu = __cpu_to_le32(arg->peer_max_mpdu);
++ cmd->mpdu_density = __cpu_to_le32(arg->peer_mpdu_density);
++ cmd->rate_caps = __cpu_to_le32(arg->peer_rate_caps);
++ cmd->nss = __cpu_to_le32(arg->peer_num_spatial_streams);
++ cmd->vht_caps = __cpu_to_le32(arg->peer_vht_caps);
++ cmd->phy_mode = __cpu_to_le32(arg->peer_phymode);
++ cmd->num_legacy_rates = __cpu_to_le32(arg->peer_legacy_rates.num_rates);
++ cmd->num_ht_rates = __cpu_to_le32(arg->peer_ht_rates.num_rates);
++ ether_addr_copy(cmd->mac_addr.addr, arg->addr);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(legacy_rate_len);
++ memcpy(tlv->value, arg->peer_legacy_rates.rates,
++ arg->peer_legacy_rates.num_rates);
++
++ ptr += sizeof(*tlv);
++ ptr += legacy_rate_len;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(ht_rate_len);
++ memcpy(tlv->value, arg->peer_ht_rates.rates,
++ arg->peer_ht_rates.num_rates);
++
++ ptr += sizeof(*tlv);
++ ptr += ht_rate_len;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VHT_RATE_SET);
++ tlv->len = __cpu_to_le16(sizeof(*vht_rate));
++ vht_rate = (void *)tlv->value;
++
++ vht_rate->rx_max_rate = __cpu_to_le32(arg->peer_vht_rates.rx_max_rate);
++ vht_rate->rx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set);
++ vht_rate->tx_max_rate = __cpu_to_le32(arg->peer_vht_rates.tx_max_rate);
++ vht_rate->tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*vht_rate);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer assoc\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_ps_mode psmode)
++{
++ struct wmi_sta_powersave_mode_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->sta_ps_mode = __cpu_to_le32(psmode);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set psmode\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id,
++ enum wmi_sta_powersave_param param_id,
++ u32 param_value)
++{
++ struct wmi_sta_powersave_param_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->param_id = __cpu_to_le32(param_id);
++ cmd->param_value = __cpu_to_le32(param_value);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set sta ps\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac,
++ enum wmi_ap_ps_peer_param param_id, u32 value)
++{
++ struct wmi_ap_ps_peer_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ if (!mac)
++ return ERR_PTR(-EINVAL);
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->param_id = __cpu_to_le32(param_id);
++ cmd->param_value = __cpu_to_le32(value);
++ ether_addr_copy(cmd->peer_macaddr.addr, mac);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv ap ps param\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_scan_chan_list(struct ath10k *ar,
++ const struct wmi_scan_chan_list_arg *arg)
++{
++ struct wmi_tlv_scan_chan_list_cmd *cmd;
++ struct wmi_channel *ci;
++ struct wmi_channel_arg *ch;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t chans_len, len;
++ int i;
++ void *ptr, *chans;
++
++ chans_len = arg->n_channels * (sizeof(*tlv) + sizeof(*ci));
++ len = (sizeof(*tlv) + sizeof(*cmd)) +
++ (sizeof(*tlv) + chans_len);
++
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
++ tlv->len = __cpu_to_le16(chans_len);
++ chans = (void *)tlv->value;
++
++ for (i = 0; i < arg->n_channels; i++) {
++ ch = &arg->channels[i];
++
++ tlv = chans;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL);
++ tlv->len = __cpu_to_le16(sizeof(*ci));
++ ci = (void *)tlv->value;
++
++ ath10k_wmi_put_wmi_channel(ci, ch);
++
++ chans += sizeof(*tlv);
++ chans += sizeof(*ci);
++ }
++
++ ptr += sizeof(*tlv);
++ ptr += chans_len;
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv scan chan list\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id,
++ const void *bcn, size_t bcn_len,
++ u32 bcn_paddr, bool dtim_zero,
++ bool deliver_cab)
++
++{
++ struct wmi_bcn_tx_ref_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ struct ieee80211_hdr *hdr;
++ u16 fc;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ hdr = (struct ieee80211_hdr *)bcn;
++ fc = le16_to_cpu(hdr->frame_control);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->data_len = __cpu_to_le32(bcn_len);
++ cmd->data_ptr = __cpu_to_le32(bcn_paddr);
++ cmd->msdu_id = 0;
++ cmd->frame_control = __cpu_to_le32(fc);
++ cmd->flags = 0;
++
++ if (dtim_zero)
++ cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
++
++ if (deliver_cab)
++ cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv beacon dma\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar,
++ const struct wmi_wmm_params_all_arg *arg)
++{
++ struct wmi_tlv_pdev_set_wmm_cmd *cmd;
++ struct wmi_wmm_params *wmm;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len;
++ void *ptr;
++
++ len = (sizeof(*tlv) + sizeof(*cmd)) +
++ (4 * (sizeof(*tlv) + sizeof(*wmm)));
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++
++ /* nothing to set here */
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be);
++ ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk);
++ ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi);
++ ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set wmm\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask)
++{
++ struct wmi_request_stats_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->stats_id = __cpu_to_le32(stats_mask);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv request stats\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar,
++ enum wmi_force_fw_hang_type type,
++ u32 delay_ms)
++{
++ struct wmi_force_fw_hang_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++
++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd));
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ tlv = (void *)skb->data;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->type = __cpu_to_le32(type);
++ cmd->delay_ms = __cpu_to_le32(delay_ms);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv force fw hang\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable,
++ u32 log_level) {
++ struct wmi_tlv_dbglog_cmd *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ size_t len, bmap_len;
++ u32 value;
++ void *ptr;
++
++ if (module_enable) {
++ value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(
++ module_enable,
++ WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE);
++ } else {
++ value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(
++ WMI_TLV_DBGLOG_ALL_MODULES,
++ WMI_TLV_DBGLOG_LOG_LEVEL_WARN);
++ }
++
++ bmap_len = 0;
++ len = sizeof(*tlv) + sizeof(*cmd) + sizeof(*tlv) + bmap_len;
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->param = __cpu_to_le32(WMI_TLV_DBGLOG_PARAM_LOG_LEVEL);
++ cmd->value = __cpu_to_le32(value);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32);
++ tlv->len = __cpu_to_le16(bmap_len);
++
++ /* nothing to do here */
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(bmap_len);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv dbglog value 0x%08x\n", value);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pktlog_enable(struct ath10k *ar, u32 filter)
++{
++ struct wmi_tlv_pktlog_enable *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ void *ptr;
++ size_t len;
++
++ len = sizeof(*tlv) + sizeof(*cmd);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->filter = __cpu_to_le32(filter);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog enable filter 0x%08x\n",
++ filter);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_pktlog_disable(struct ath10k *ar)
++{
++ struct wmi_tlv_pktlog_disable *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ void *ptr;
++ size_t len;
++
++ len = sizeof(*tlv) + sizeof(*cmd);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog disable\n");
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_bcn_tmpl(struct ath10k *ar, u32 vdev_id,
++ u32 tim_ie_offset, struct sk_buff *bcn,
++ u32 prb_caps, u32 prb_erp, void *prb_ies,
++ size_t prb_ies_len)
++{
++ struct wmi_tlv_bcn_tmpl_cmd *cmd;
++ struct wmi_tlv_bcn_prb_info *info;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ void *ptr;
++ size_t len;
++
++ if (WARN_ON(prb_ies_len > 0 && !prb_ies))
++ return ERR_PTR(-EINVAL);
++
++ len = sizeof(*tlv) + sizeof(*cmd) +
++ sizeof(*tlv) + sizeof(*info) + prb_ies_len +
++ sizeof(*tlv) + roundup(bcn->len, 4);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->tim_ie_offset = __cpu_to_le32(tim_ie_offset);
++ cmd->buf_len = __cpu_to_le32(bcn->len);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ /* FIXME: prb_ies_len should be probably aligned to 4byte boundary but
++ * then it is then impossible to pass original ie len.
++ * This chunk is not used yet so if setting probe resp template yields
++ * problems with beaconing or crashes firmware look here.
++ */
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO);
++ tlv->len = __cpu_to_le16(sizeof(*info) + prb_ies_len);
++ info = (void *)tlv->value;
++ info->caps = __cpu_to_le32(prb_caps);
++ info->erp = __cpu_to_le32(prb_erp);
++ memcpy(info->ies, prb_ies, prb_ies_len);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*info);
++ ptr += prb_ies_len;
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(roundup(bcn->len, 4));
++ memcpy(tlv->value, bcn->data, bcn->len);
++
++ /* FIXME: Adjust TSF? */
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv bcn tmpl vdev_id %i\n",
++ vdev_id);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_prb_tmpl(struct ath10k *ar, u32 vdev_id,
++ struct sk_buff *prb)
++{
++ struct wmi_tlv_prb_tmpl_cmd *cmd;
++ struct wmi_tlv_bcn_prb_info *info;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ void *ptr;
++ size_t len;
++
++ len = sizeof(*tlv) + sizeof(*cmd) +
++ sizeof(*tlv) + sizeof(*info) +
++ sizeof(*tlv) + roundup(prb->len, 4);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->buf_len = __cpu_to_le32(prb->len);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO);
++ tlv->len = __cpu_to_le16(sizeof(*info));
++ info = (void *)tlv->value;
++ info->caps = 0;
++ info->erp = 0;
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*info);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(roundup(prb->len, 4));
++ memcpy(tlv->value, prb->data, prb->len);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv prb tmpl vdev_id %i\n",
++ vdev_id);
++ return skb;
++}
++
++static struct sk_buff *
++ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id,
++ const u8 *p2p_ie)
++{
++ struct wmi_tlv_p2p_go_bcn_ie *cmd;
++ struct wmi_tlv *tlv;
++ struct sk_buff *skb;
++ void *ptr;
++ size_t len;
++
++ len = sizeof(*tlv) + sizeof(*cmd) +
++ sizeof(*tlv) + roundup(p2p_ie[1] + 2, 4);
++ skb = ath10k_wmi_alloc_skb(ar, len);
++ if (!skb)
++ return ERR_PTR(-ENOMEM);
++
++ ptr = (void *)skb->data;
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE);
++ tlv->len = __cpu_to_le16(sizeof(*cmd));
++ cmd = (void *)tlv->value;
++ cmd->vdev_id = __cpu_to_le32(vdev_id);
++ cmd->ie_len = __cpu_to_le32(p2p_ie[1] + 2);
++
++ ptr += sizeof(*tlv);
++ ptr += sizeof(*cmd);
++
++ tlv = ptr;
++ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE);
++ tlv->len = __cpu_to_le16(roundup(p2p_ie[1] + 2, 4));
++ memcpy(tlv->value, p2p_ie, p2p_ie[1] + 2);
++
++ ptr += sizeof(*tlv);
++ ptr += roundup(p2p_ie[1] + 2, 4);
++
++ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv p2p go bcn ie for vdev %i\n",
++ vdev_id);
++ return skb;
++}
++
++/****************/
++/* TLV mappings */
++/****************/
++
++static struct wmi_cmd_map wmi_tlv_cmd_map = {
++ .init_cmdid = WMI_TLV_INIT_CMDID,
++ .start_scan_cmdid = WMI_TLV_START_SCAN_CMDID,
++ .stop_scan_cmdid = WMI_TLV_STOP_SCAN_CMDID,
++ .scan_chan_list_cmdid = WMI_TLV_SCAN_CHAN_LIST_CMDID,
++ .scan_sch_prio_tbl_cmdid = WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID,
++ .pdev_set_regdomain_cmdid = WMI_TLV_PDEV_SET_REGDOMAIN_CMDID,
++ .pdev_set_channel_cmdid = WMI_TLV_PDEV_SET_CHANNEL_CMDID,
++ .pdev_set_param_cmdid = WMI_TLV_PDEV_SET_PARAM_CMDID,
++ .pdev_pktlog_enable_cmdid = WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID,
++ .pdev_pktlog_disable_cmdid = WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID,
++ .pdev_set_wmm_params_cmdid = WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID,
++ .pdev_set_ht_cap_ie_cmdid = WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID,
++ .pdev_set_vht_cap_ie_cmdid = WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID,
++ .pdev_set_dscp_tid_map_cmdid = WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID,
++ .pdev_set_quiet_mode_cmdid = WMI_TLV_PDEV_SET_QUIET_MODE_CMDID,
++ .pdev_green_ap_ps_enable_cmdid = WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID,
++ .pdev_get_tpc_config_cmdid = WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID,
++ .pdev_set_base_macaddr_cmdid = WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID,
++ .vdev_create_cmdid = WMI_TLV_VDEV_CREATE_CMDID,
++ .vdev_delete_cmdid = WMI_TLV_VDEV_DELETE_CMDID,
++ .vdev_start_request_cmdid = WMI_TLV_VDEV_START_REQUEST_CMDID,
++ .vdev_restart_request_cmdid = WMI_TLV_VDEV_RESTART_REQUEST_CMDID,
++ .vdev_up_cmdid = WMI_TLV_VDEV_UP_CMDID,
++ .vdev_stop_cmdid = WMI_TLV_VDEV_STOP_CMDID,
++ .vdev_down_cmdid = WMI_TLV_VDEV_DOWN_CMDID,
++ .vdev_set_param_cmdid = WMI_TLV_VDEV_SET_PARAM_CMDID,
++ .vdev_install_key_cmdid = WMI_TLV_VDEV_INSTALL_KEY_CMDID,
++ .peer_create_cmdid = WMI_TLV_PEER_CREATE_CMDID,
++ .peer_delete_cmdid = WMI_TLV_PEER_DELETE_CMDID,
++ .peer_flush_tids_cmdid = WMI_TLV_PEER_FLUSH_TIDS_CMDID,
++ .peer_set_param_cmdid = WMI_TLV_PEER_SET_PARAM_CMDID,
++ .peer_assoc_cmdid = WMI_TLV_PEER_ASSOC_CMDID,
++ .peer_add_wds_entry_cmdid = WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID,
++ .peer_remove_wds_entry_cmdid = WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID,
++ .peer_mcast_group_cmdid = WMI_TLV_PEER_MCAST_GROUP_CMDID,
++ .bcn_tx_cmdid = WMI_TLV_BCN_TX_CMDID,
++ .pdev_send_bcn_cmdid = WMI_TLV_PDEV_SEND_BCN_CMDID,
++ .bcn_tmpl_cmdid = WMI_TLV_BCN_TMPL_CMDID,
++ .bcn_filter_rx_cmdid = WMI_TLV_BCN_FILTER_RX_CMDID,
++ .prb_req_filter_rx_cmdid = WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
++ .mgmt_tx_cmdid = WMI_TLV_MGMT_TX_CMDID,
++ .prb_tmpl_cmdid = WMI_TLV_PRB_TMPL_CMDID,
++ .addba_clear_resp_cmdid = WMI_TLV_ADDBA_CLEAR_RESP_CMDID,
++ .addba_send_cmdid = WMI_TLV_ADDBA_SEND_CMDID,
++ .addba_status_cmdid = WMI_TLV_ADDBA_STATUS_CMDID,
++ .delba_send_cmdid = WMI_TLV_DELBA_SEND_CMDID,
++ .addba_set_resp_cmdid = WMI_TLV_ADDBA_SET_RESP_CMDID,
++ .send_singleamsdu_cmdid = WMI_TLV_SEND_SINGLEAMSDU_CMDID,
++ .sta_powersave_mode_cmdid = WMI_TLV_STA_POWERSAVE_MODE_CMDID,
++ .sta_powersave_param_cmdid = WMI_TLV_STA_POWERSAVE_PARAM_CMDID,
++ .sta_mimo_ps_mode_cmdid = WMI_TLV_STA_MIMO_PS_MODE_CMDID,
++ .pdev_dfs_enable_cmdid = WMI_TLV_PDEV_DFS_ENABLE_CMDID,
++ .pdev_dfs_disable_cmdid = WMI_TLV_PDEV_DFS_DISABLE_CMDID,
++ .roam_scan_mode = WMI_TLV_ROAM_SCAN_MODE,
++ .roam_scan_rssi_threshold = WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD,
++ .roam_scan_period = WMI_TLV_ROAM_SCAN_PERIOD,
++ .roam_scan_rssi_change_threshold =
++ WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
++ .roam_ap_profile = WMI_TLV_ROAM_AP_PROFILE,
++ .ofl_scan_add_ap_profile = WMI_TLV_ROAM_AP_PROFILE,
++ .ofl_scan_remove_ap_profile = WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE,
++ .ofl_scan_period = WMI_TLV_OFL_SCAN_PERIOD,
++ .p2p_dev_set_device_info = WMI_TLV_P2P_DEV_SET_DEVICE_INFO,
++ .p2p_dev_set_discoverability = WMI_TLV_P2P_DEV_SET_DISCOVERABILITY,
++ .p2p_go_set_beacon_ie = WMI_TLV_P2P_GO_SET_BEACON_IE,
++ .p2p_go_set_probe_resp_ie = WMI_TLV_P2P_GO_SET_PROBE_RESP_IE,
++ .p2p_set_vendor_ie_data_cmdid = WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID,
++ .ap_ps_peer_param_cmdid = WMI_TLV_AP_PS_PEER_PARAM_CMDID,
++ .ap_ps_peer_uapsd_coex_cmdid = WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID,
++ .peer_rate_retry_sched_cmdid = WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID,
++ .wlan_profile_trigger_cmdid = WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID,
++ .wlan_profile_set_hist_intvl_cmdid =
++ WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
++ .wlan_profile_get_profile_data_cmdid =
++ WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
++ .wlan_profile_enable_profile_id_cmdid =
++ WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
++ .wlan_profile_list_profile_id_cmdid =
++ WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
++ .pdev_suspend_cmdid = WMI_TLV_PDEV_SUSPEND_CMDID,
++ .pdev_resume_cmdid = WMI_TLV_PDEV_RESUME_CMDID,
++ .add_bcn_filter_cmdid = WMI_TLV_ADD_BCN_FILTER_CMDID,
++ .rmv_bcn_filter_cmdid = WMI_TLV_RMV_BCN_FILTER_CMDID,
++ .wow_add_wake_pattern_cmdid = WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID,
++ .wow_del_wake_pattern_cmdid = WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID,
++ .wow_enable_disable_wake_event_cmdid =
++ WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
++ .wow_enable_cmdid = WMI_TLV_WOW_ENABLE_CMDID,
++ .wow_hostwakeup_from_sleep_cmdid =
++ WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
++ .rtt_measreq_cmdid = WMI_TLV_RTT_MEASREQ_CMDID,
++ .rtt_tsf_cmdid = WMI_TLV_RTT_TSF_CMDID,
++ .vdev_spectral_scan_configure_cmdid = WMI_TLV_SPECTRAL_SCAN_CONF_CMDID,
++ .vdev_spectral_scan_enable_cmdid = WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID,
++ .request_stats_cmdid = WMI_TLV_REQUEST_STATS_CMDID,
++ .set_arp_ns_offload_cmdid = WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID,
++ .network_list_offload_config_cmdid =
++ WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID,
++ .gtk_offload_cmdid = WMI_TLV_GTK_OFFLOAD_CMDID,
++ .csa_offload_enable_cmdid = WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID,
++ .csa_offload_chanswitch_cmdid = WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID,
++ .chatter_set_mode_cmdid = WMI_TLV_CHATTER_SET_MODE_CMDID,
++ .peer_tid_addba_cmdid = WMI_TLV_PEER_TID_ADDBA_CMDID,
++ .peer_tid_delba_cmdid = WMI_TLV_PEER_TID_DELBA_CMDID,
++ .sta_dtim_ps_method_cmdid = WMI_TLV_STA_DTIM_PS_METHOD_CMDID,
++ .sta_uapsd_auto_trig_cmdid = WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID,
++ .sta_keepalive_cmd = WMI_TLV_STA_KEEPALIVE_CMDID,
++ .echo_cmdid = WMI_TLV_ECHO_CMDID,
++ .pdev_utf_cmdid = WMI_TLV_PDEV_UTF_CMDID,
++ .dbglog_cfg_cmdid = WMI_TLV_DBGLOG_CFG_CMDID,
++ .pdev_qvit_cmdid = WMI_TLV_PDEV_QVIT_CMDID,
++ .pdev_ftm_intg_cmdid = WMI_TLV_PDEV_FTM_INTG_CMDID,
++ .vdev_set_keepalive_cmdid = WMI_TLV_VDEV_SET_KEEPALIVE_CMDID,
++ .vdev_get_keepalive_cmdid = WMI_TLV_VDEV_GET_KEEPALIVE_CMDID,
++ .force_fw_hang_cmdid = WMI_TLV_FORCE_FW_HANG_CMDID,
++ .gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID,
++ .gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID,
++ .pdev_get_temperature_cmdid = WMI_TLV_CMD_UNSUPPORTED,
++ .vdev_set_wmm_params_cmdid = WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID,
++};
++
++static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = {
++ .tx_chain_mask = WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK,
++ .rx_chain_mask = WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK,
++ .txpower_limit2g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G,
++ .txpower_limit5g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G,
++ .txpower_scale = WMI_TLV_PDEV_PARAM_TXPOWER_SCALE,
++ .beacon_gen_mode = WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE,
++ .beacon_tx_mode = WMI_TLV_PDEV_PARAM_BEACON_TX_MODE,
++ .resmgr_offchan_mode = WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
++ .protection_mode = WMI_TLV_PDEV_PARAM_PROTECTION_MODE,
++ .dynamic_bw = WMI_TLV_PDEV_PARAM_DYNAMIC_BW,
++ .non_agg_sw_retry_th = WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
++ .agg_sw_retry_th = WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH,
++ .sta_kickout_th = WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH,
++ .ac_aggrsize_scaling = WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING,
++ .ltr_enable = WMI_TLV_PDEV_PARAM_LTR_ENABLE,
++ .ltr_ac_latency_be = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE,
++ .ltr_ac_latency_bk = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK,
++ .ltr_ac_latency_vi = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI,
++ .ltr_ac_latency_vo = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO,
++ .ltr_ac_latency_timeout = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
++ .ltr_sleep_override = WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
++ .ltr_rx_override = WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE,
++ .ltr_tx_activity_timeout = WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
++ .l1ss_enable = WMI_TLV_PDEV_PARAM_L1SS_ENABLE,
++ .dsleep_enable = WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE,
++ .pcielp_txbuf_flush = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
++ .pcielp_txbuf_watermark = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
++ .pcielp_txbuf_tmo_en = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
++ .pcielp_txbuf_tmo_value = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
++ .pdev_stats_update_period = WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
++ .vdev_stats_update_period = WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
++ .peer_stats_update_period = WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
++ .bcnflt_stats_update_period =
++ WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
++ .pmf_qos = WMI_TLV_PDEV_PARAM_PMF_QOS,
++ .arp_ac_override = WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE,
++ .dcs = WMI_TLV_PDEV_PARAM_DCS,
++ .ani_enable = WMI_TLV_PDEV_PARAM_ANI_ENABLE,
++ .ani_poll_period = WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD,
++ .ani_listen_period = WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD,
++ .ani_ofdm_level = WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL,
++ .ani_cck_level = WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL,
++ .dyntxchain = WMI_TLV_PDEV_PARAM_DYNTXCHAIN,
++ .proxy_sta = WMI_TLV_PDEV_PARAM_PROXY_STA,
++ .idle_ps_config = WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG,
++ .power_gating_sleep = WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP,
++ .fast_channel_reset = WMI_TLV_PDEV_PARAM_UNSUPPORTED,
++ .burst_dur = WMI_TLV_PDEV_PARAM_BURST_DUR,
++ .burst_enable = WMI_TLV_PDEV_PARAM_BURST_ENABLE,
++ .cal_period = WMI_PDEV_PARAM_UNSUPPORTED,
++};
++
++static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = {
++ .rts_threshold = WMI_TLV_VDEV_PARAM_RTS_THRESHOLD,
++ .fragmentation_threshold = WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
++ .beacon_interval = WMI_TLV_VDEV_PARAM_BEACON_INTERVAL,
++ .listen_interval = WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL,
++ .multicast_rate = WMI_TLV_VDEV_PARAM_MULTICAST_RATE,
++ .mgmt_tx_rate = WMI_TLV_VDEV_PARAM_MGMT_TX_RATE,
++ .slot_time = WMI_TLV_VDEV_PARAM_SLOT_TIME,
++ .preamble = WMI_TLV_VDEV_PARAM_PREAMBLE,
++ .swba_time = WMI_TLV_VDEV_PARAM_SWBA_TIME,
++ .wmi_vdev_stats_update_period = WMI_TLV_VDEV_STATS_UPDATE_PERIOD,
++ .wmi_vdev_pwrsave_ageout_time = WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME,
++ .wmi_vdev_host_swba_interval = WMI_TLV_VDEV_HOST_SWBA_INTERVAL,
++ .dtim_period = WMI_TLV_VDEV_PARAM_DTIM_PERIOD,
++ .wmi_vdev_oc_scheduler_air_time_limit =
++ WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
++ .wds = WMI_TLV_VDEV_PARAM_WDS,
++ .atim_window = WMI_TLV_VDEV_PARAM_ATIM_WINDOW,
++ .bmiss_count_max = WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX,
++ .bmiss_first_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT,
++ .bmiss_final_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT,
++ .feature_wmm = WMI_TLV_VDEV_PARAM_FEATURE_WMM,
++ .chwidth = WMI_TLV_VDEV_PARAM_CHWIDTH,
++ .chextoffset = WMI_TLV_VDEV_PARAM_CHEXTOFFSET,
++ .disable_htprotection = WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION,
++ .sta_quickkickout = WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT,
++ .mgmt_rate = WMI_TLV_VDEV_PARAM_MGMT_RATE,
++ .protection_mode = WMI_TLV_VDEV_PARAM_PROTECTION_MODE,
++ .fixed_rate = WMI_TLV_VDEV_PARAM_FIXED_RATE,
++ .sgi = WMI_TLV_VDEV_PARAM_SGI,
++ .ldpc = WMI_TLV_VDEV_PARAM_LDPC,
++ .tx_stbc = WMI_TLV_VDEV_PARAM_TX_STBC,
++ .rx_stbc = WMI_TLV_VDEV_PARAM_RX_STBC,
++ .intra_bss_fwd = WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD,
++ .def_keyid = WMI_TLV_VDEV_PARAM_DEF_KEYID,
++ .nss = WMI_TLV_VDEV_PARAM_NSS,
++ .bcast_data_rate = WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE,
++ .mcast_data_rate = WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE,
++ .mcast_indicate = WMI_TLV_VDEV_PARAM_MCAST_INDICATE,
++ .dhcp_indicate = WMI_TLV_VDEV_PARAM_DHCP_INDICATE,
++ .unknown_dest_indicate = WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
++ .ap_keepalive_min_idle_inactive_time_secs =
++ WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
++ .ap_keepalive_max_idle_inactive_time_secs =
++ WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
++ .ap_keepalive_max_unresponsive_time_secs =
++ WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
++ .ap_enable_nawds = WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS,
++ .mcast2ucast_set = WMI_TLV_VDEV_PARAM_UNSUPPORTED,
++ .enable_rtscts = WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS,
++ .txbf = WMI_TLV_VDEV_PARAM_TXBF,
++ .packet_powersave = WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE,
++ .drop_unencry = WMI_TLV_VDEV_PARAM_DROP_UNENCRY,
++ .tx_encap_type = WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE,
++ .ap_detect_out_of_sync_sleeping_sta_time_secs =
++ WMI_TLV_VDEV_PARAM_UNSUPPORTED,
++};
++
++static const struct wmi_ops wmi_tlv_ops = {
++ .rx = ath10k_wmi_tlv_op_rx,
++ .map_svc = wmi_tlv_svc_map,
++
++ .pull_scan = ath10k_wmi_tlv_op_pull_scan_ev,
++ .pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev,
++ .pull_ch_info = ath10k_wmi_tlv_op_pull_ch_info_ev,
++ .pull_vdev_start = ath10k_wmi_tlv_op_pull_vdev_start_ev,
++ .pull_peer_kick = ath10k_wmi_tlv_op_pull_peer_kick_ev,
++ .pull_swba = ath10k_wmi_tlv_op_pull_swba_ev,
++ .pull_phyerr = ath10k_wmi_tlv_op_pull_phyerr_ev,
++ .pull_svc_rdy = ath10k_wmi_tlv_op_pull_svc_rdy_ev,
++ .pull_rdy = ath10k_wmi_tlv_op_pull_rdy_ev,
++ .pull_fw_stats = ath10k_wmi_tlv_op_pull_fw_stats,
++
++ .gen_pdev_suspend = ath10k_wmi_tlv_op_gen_pdev_suspend,
++ .gen_pdev_resume = ath10k_wmi_tlv_op_gen_pdev_resume,
++ .gen_pdev_set_rd = ath10k_wmi_tlv_op_gen_pdev_set_rd,
++ .gen_pdev_set_param = ath10k_wmi_tlv_op_gen_pdev_set_param,
++ .gen_init = ath10k_wmi_tlv_op_gen_init,
++ .gen_start_scan = ath10k_wmi_tlv_op_gen_start_scan,
++ .gen_stop_scan = ath10k_wmi_tlv_op_gen_stop_scan,
++ .gen_vdev_create = ath10k_wmi_tlv_op_gen_vdev_create,
++ .gen_vdev_delete = ath10k_wmi_tlv_op_gen_vdev_delete,
++ .gen_vdev_start = ath10k_wmi_tlv_op_gen_vdev_start,
++ .gen_vdev_stop = ath10k_wmi_tlv_op_gen_vdev_stop,
++ .gen_vdev_up = ath10k_wmi_tlv_op_gen_vdev_up,
++ .gen_vdev_down = ath10k_wmi_tlv_op_gen_vdev_down,
++ .gen_vdev_set_param = ath10k_wmi_tlv_op_gen_vdev_set_param,
++ .gen_vdev_install_key = ath10k_wmi_tlv_op_gen_vdev_install_key,
++ .gen_vdev_wmm_conf = ath10k_wmi_tlv_op_gen_vdev_wmm_conf,
++ .gen_peer_create = ath10k_wmi_tlv_op_gen_peer_create,
++ .gen_peer_delete = ath10k_wmi_tlv_op_gen_peer_delete,
++ .gen_peer_flush = ath10k_wmi_tlv_op_gen_peer_flush,
++ .gen_peer_set_param = ath10k_wmi_tlv_op_gen_peer_set_param,
++ .gen_peer_assoc = ath10k_wmi_tlv_op_gen_peer_assoc,
++ .gen_set_psmode = ath10k_wmi_tlv_op_gen_set_psmode,
++ .gen_set_sta_ps = ath10k_wmi_tlv_op_gen_set_sta_ps,
++ .gen_set_ap_ps = ath10k_wmi_tlv_op_gen_set_ap_ps,
++ .gen_scan_chan_list = ath10k_wmi_tlv_op_gen_scan_chan_list,
++ .gen_beacon_dma = ath10k_wmi_tlv_op_gen_beacon_dma,
++ .gen_pdev_set_wmm = ath10k_wmi_tlv_op_gen_pdev_set_wmm,
++ .gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats,
++ .gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang,
++ /* .gen_mgmt_tx = not implemented; HTT is used */
++ .gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg,
++ .gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable,
++ .gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable,
++ /* .gen_pdev_set_quiet_mode not implemented */
++ /* .gen_pdev_get_temperature not implemented */
++ /* .gen_addba_clear_resp not implemented */
++ /* .gen_addba_send not implemented */
++ /* .gen_addba_set_resp not implemented */
++ /* .gen_delba_send not implemented */
++ .gen_bcn_tmpl = ath10k_wmi_tlv_op_gen_bcn_tmpl,
++ .gen_prb_tmpl = ath10k_wmi_tlv_op_gen_prb_tmpl,
++ .gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie,
++ .gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd,
++ .gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive,
++};
++
++/************/
++/* TLV init */
++/************/
++
++void ath10k_wmi_tlv_attach(struct ath10k *ar)
++{
++ ar->wmi.cmd = &wmi_tlv_cmd_map;
++ ar->wmi.vdev_param = &wmi_tlv_vdev_param_map;
++ ar->wmi.pdev_param = &wmi_tlv_pdev_param_map;
++ ar->wmi.ops = &wmi_tlv_ops;
++}
+--- /dev/null
++++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+@@ -0,0 +1,1459 @@
++/*
++ * Copyright (c) 2005-2011 Atheros Communications Inc.
++ * Copyright (c) 2011-2014 Qualcomm Atheros, Inc.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ */
++#ifndef _WMI_TLV_H
++#define _WMI_TLV_H
++
++#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1)
++#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1)
++#define WMI_TLV_CMD_UNSUPPORTED 0
++#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0
++#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0
++
++enum wmi_tlv_grp_id {
++ WMI_TLV_GRP_START = 0x3,
++ WMI_TLV_GRP_SCAN = WMI_TLV_GRP_START,
++ WMI_TLV_GRP_PDEV,
++ WMI_TLV_GRP_VDEV,
++ WMI_TLV_GRP_PEER,
++ WMI_TLV_GRP_MGMT,
++ WMI_TLV_GRP_BA_NEG,
++ WMI_TLV_GRP_STA_PS,
++ WMI_TLV_GRP_DFS,
++ WMI_TLV_GRP_ROAM,
++ WMI_TLV_GRP_OFL_SCAN,
++ WMI_TLV_GRP_P2P,
++ WMI_TLV_GRP_AP_PS,
++ WMI_TLV_GRP_RATECTL,
++ WMI_TLV_GRP_PROFILE,
++ WMI_TLV_GRP_SUSPEND,
++ WMI_TLV_GRP_BCN_FILTER,
++ WMI_TLV_GRP_WOW,
++ WMI_TLV_GRP_RTT,
++ WMI_TLV_GRP_SPECTRAL,
++ WMI_TLV_GRP_STATS,
++ WMI_TLV_GRP_ARP_NS_OFL,
++ WMI_TLV_GRP_NLO_OFL,
++ WMI_TLV_GRP_GTK_OFL,
++ WMI_TLV_GRP_CSA_OFL,
++ WMI_TLV_GRP_CHATTER,
++ WMI_TLV_GRP_TID_ADDBA,
++ WMI_TLV_GRP_MISC,
++ WMI_TLV_GRP_GPIO,
++ WMI_TLV_GRP_FWTEST,
++ WMI_TLV_GRP_TDLS,
++ WMI_TLV_GRP_RESMGR,
++ WMI_TLV_GRP_STA_SMPS,
++ WMI_TLV_GRP_WLAN_HB,
++ WMI_TLV_GRP_RMC,
++ WMI_TLV_GRP_MHF_OFL,
++ WMI_TLV_GRP_LOCATION_SCAN,
++ WMI_TLV_GRP_OEM,
++ WMI_TLV_GRP_NAN,
++ WMI_TLV_GRP_COEX,
++ WMI_TLV_GRP_OBSS_OFL,
++ WMI_TLV_GRP_LPI,
++ WMI_TLV_GRP_EXTSCAN,
++ WMI_TLV_GRP_DHCP_OFL,
++ WMI_TLV_GRP_IPA,
++ WMI_TLV_GRP_MDNS_OFL,
++ WMI_TLV_GRP_SAP_OFL,
++};
++
++enum wmi_tlv_cmd_id {
++ WMI_TLV_INIT_CMDID = 0x1,
++ WMI_TLV_START_SCAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SCAN),
++ WMI_TLV_STOP_SCAN_CMDID,
++ WMI_TLV_SCAN_CHAN_LIST_CMDID,
++ WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID,
++ WMI_TLV_SCAN_UPDATE_REQUEST_CMDID,
++ WMI_TLV_SCAN_PROB_REQ_OUI_CMDID,
++ WMI_TLV_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PDEV),
++ WMI_TLV_PDEV_SET_CHANNEL_CMDID,
++ WMI_TLV_PDEV_SET_PARAM_CMDID,
++ WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID,
++ WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID,
++ WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID,
++ WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID,
++ WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID,
++ WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID,
++ WMI_TLV_PDEV_SET_QUIET_MODE_CMDID,
++ WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID,
++ WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID,
++ WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID,
++ WMI_TLV_PDEV_DUMP_CMDID,
++ WMI_TLV_PDEV_SET_LED_CONFIG_CMDID,
++ WMI_TLV_PDEV_GET_TEMPERATURE_CMDID,
++ WMI_TLV_PDEV_SET_LED_FLASHING_CMDID,
++ WMI_TLV_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_VDEV),
++ WMI_TLV_VDEV_DELETE_CMDID,
++ WMI_TLV_VDEV_START_REQUEST_CMDID,
++ WMI_TLV_VDEV_RESTART_REQUEST_CMDID,
++ WMI_TLV_VDEV_UP_CMDID,
++ WMI_TLV_VDEV_STOP_CMDID,
++ WMI_TLV_VDEV_DOWN_CMDID,
++ WMI_TLV_VDEV_SET_PARAM_CMDID,
++ WMI_TLV_VDEV_INSTALL_KEY_CMDID,
++ WMI_TLV_VDEV_WNM_SLEEPMODE_CMDID,
++ WMI_TLV_VDEV_WMM_ADDTS_CMDID,
++ WMI_TLV_VDEV_WMM_DELTS_CMDID,
++ WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID,
++ WMI_TLV_VDEV_SET_GTX_PARAMS_CMDID,
++ WMI_TLV_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID,
++ WMI_TLV_VDEV_PLMREQ_START_CMDID,
++ WMI_TLV_VDEV_PLMREQ_STOP_CMDID,
++ WMI_TLV_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PEER),
++ WMI_TLV_PEER_DELETE_CMDID,
++ WMI_TLV_PEER_FLUSH_TIDS_CMDID,
++ WMI_TLV_PEER_SET_PARAM_CMDID,
++ WMI_TLV_PEER_ASSOC_CMDID,
++ WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID,
++ WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID,
++ WMI_TLV_PEER_MCAST_GROUP_CMDID,
++ WMI_TLV_PEER_INFO_REQ_CMDID,
++ WMI_TLV_PEER_GET_ESTIMATED_LINKSPEED_CMDID,
++ WMI_TLV_BCN_TX_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MGMT),
++ WMI_TLV_PDEV_SEND_BCN_CMDID,
++ WMI_TLV_BCN_TMPL_CMDID,
++ WMI_TLV_BCN_FILTER_RX_CMDID,
++ WMI_TLV_PRB_REQ_FILTER_RX_CMDID,
++ WMI_TLV_MGMT_TX_CMDID,
++ WMI_TLV_PRB_TMPL_CMDID,
++ WMI_TLV_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BA_NEG),
++ WMI_TLV_ADDBA_SEND_CMDID,
++ WMI_TLV_ADDBA_STATUS_CMDID,
++ WMI_TLV_DELBA_SEND_CMDID,
++ WMI_TLV_ADDBA_SET_RESP_CMDID,
++ WMI_TLV_SEND_SINGLEAMSDU_CMDID,
++ WMI_TLV_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_PS),
++ WMI_TLV_STA_POWERSAVE_PARAM_CMDID,
++ WMI_TLV_STA_MIMO_PS_MODE_CMDID,
++ WMI_TLV_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_DFS),
++ WMI_TLV_PDEV_DFS_DISABLE_CMDID,
++ WMI_TLV_DFS_PHYERR_FILTER_ENA_CMDID,
++ WMI_TLV_DFS_PHYERR_FILTER_DIS_CMDID,
++ WMI_TLV_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_TLV_GRP_ROAM),
++ WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD,
++ WMI_TLV_ROAM_SCAN_PERIOD,
++ WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
++ WMI_TLV_ROAM_AP_PROFILE,
++ WMI_TLV_ROAM_CHAN_LIST,
++ WMI_TLV_ROAM_SCAN_CMD,
++ WMI_TLV_ROAM_SYNCH_COMPLETE,
++ WMI_TLV_ROAM_SET_RIC_REQUEST_CMDID,
++ WMI_TLV_ROAM_INVOKE_CMDID,
++ WMI_TLV_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_TLV_GRP_OFL_SCAN),
++ WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE,
++ WMI_TLV_OFL_SCAN_PERIOD,
++ WMI_TLV_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_TLV_GRP_P2P),
++ WMI_TLV_P2P_DEV_SET_DISCOVERABILITY,
++ WMI_TLV_P2P_GO_SET_BEACON_IE,
++ WMI_TLV_P2P_GO_SET_PROBE_RESP_IE,
++ WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID,
++ WMI_TLV_P2P_DISC_OFFLOAD_CONFIG_CMDID,
++ WMI_TLV_P2P_DISC_OFFLOAD_APPIE_CMDID,
++ WMI_TLV_P2P_DISC_OFFLOAD_PATTERN_CMDID,
++ WMI_TLV_P2P_SET_OPPPS_PARAM_CMDID,
++ WMI_TLV_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_AP_PS),
++ WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID,
++ WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RATECTL),
++ WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PROFILE),
++ WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
++ WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
++ WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
++ WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
++ WMI_TLV_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SUSPEND),
++ WMI_TLV_PDEV_RESUME_CMDID,
++ WMI_TLV_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BCN_FILTER),
++ WMI_TLV_RMV_BCN_FILTER_CMDID,
++ WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WOW),
++ WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID,
++ WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
++ WMI_TLV_WOW_ENABLE_CMDID,
++ WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
++ WMI_TLV_WOW_ACER_IOAC_ADD_KEEPALIVE_CMDID,
++ WMI_TLV_WOW_ACER_IOAC_DEL_KEEPALIVE_CMDID,
++ WMI_TLV_WOW_ACER_IOAC_ADD_WAKE_PATTERN_CMDID,
++ WMI_TLV_WOW_ACER_IOAC_DEL_WAKE_PATTERN_CMDID,
++ WMI_TLV_D0_WOW_ENABLE_DISABLE_CMDID,
++ WMI_TLV_EXTWOW_ENABLE_CMDID,
++ WMI_TLV_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID,
++ WMI_TLV_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID,
++ WMI_TLV_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RTT),
++ WMI_TLV_RTT_TSF_CMDID,
++ WMI_TLV_SPECTRAL_SCAN_CONF_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SPECTRAL),
++ WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID,
++ WMI_TLV_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STATS),
++ WMI_TLV_MCC_SCHED_TRAFFIC_STATS_CMDID,
++ WMI_TLV_REQUEST_STATS_EXT_CMDID,
++ WMI_TLV_REQUEST_LINK_STATS_CMDID,
++ WMI_TLV_START_LINK_STATS_CMDID,
++ WMI_TLV_CLEAR_LINK_STATS_CMDID,
++ WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_ARP_NS_OFL),
++ WMI_TLV_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID,
++ WMI_TLV_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID,
++ WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID =
++ WMI_TLV_CMD(WMI_TLV_GRP_NLO_OFL),
++ WMI_TLV_APFIND_CMDID,
++ WMI_TLV_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GTK_OFL),
++ WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CSA_OFL),
++ WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID,
++ WMI_TLV_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CHATTER),
++ WMI_TLV_CHATTER_ADD_COALESCING_FILTER_CMDID,
++ WMI_TLV_CHATTER_DELETE_COALESCING_FILTER_CMDID,
++ WMI_TLV_CHATTER_COALESCING_QUERY_CMDID,
++ WMI_TLV_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TID_ADDBA),
++ WMI_TLV_PEER_TID_DELBA_CMDID,
++ WMI_TLV_STA_DTIM_PS_METHOD_CMDID,
++ WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID,
++ WMI_TLV_STA_KEEPALIVE_CMDID,
++ WMI_TLV_BA_REQ_SSN_CMDID,
++ WMI_TLV_ECHO_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MISC),
++ WMI_TLV_PDEV_UTF_CMDID,
++ WMI_TLV_DBGLOG_CFG_CMDID,
++ WMI_TLV_PDEV_QVIT_CMDID,
++ WMI_TLV_PDEV_FTM_INTG_CMDID,
++ WMI_TLV_VDEV_SET_KEEPALIVE_CMDID,
++ WMI_TLV_VDEV_GET_KEEPALIVE_CMDID,
++ WMI_TLV_FORCE_FW_HANG_CMDID,
++ WMI_TLV_SET_MCASTBCAST_FILTER_CMDID,
++ WMI_TLV_THERMAL_MGMT_CMDID,
++ WMI_TLV_HOST_AUTO_SHUTDOWN_CFG_CMDID,
++ WMI_TLV_TPC_CHAINMASK_CONFIG_CMDID,
++ WMI_TLV_SET_ANTENNA_DIVERSITY_CMDID,
++ WMI_TLV_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GPIO),
++ WMI_TLV_GPIO_OUTPUT_CMDID,
++ WMI_TLV_TXBF_CMDID,
++ WMI_TLV_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID =
++ WMI_TLV_CMD(WMI_TLV_GRP_FWTEST),
++ WMI_TLV_FWTEST_P2P_SET_NOA_PARAM_CMDID,
++ WMI_TLV_UNIT_TEST_CMDID,
++ WMI_TLV_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TDLS),
++ WMI_TLV_TDLS_PEER_UPDATE_CMDID,
++ WMI_TLV_TDLS_SET_OFFCHAN_MODE_CMDID,
++ WMI_TLV_RESMGR_ADAPTIVE_OCS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RESMGR),
++ WMI_TLV_RESMGR_SET_CHAN_TIME_QUOTA_CMDID,
++ WMI_TLV_RESMGR_SET_CHAN_LATENCY_CMDID,
++ WMI_TLV_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_SMPS),
++ WMI_TLV_STA_SMPS_PARAM_CMDID,
++ WMI_TLV_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WLAN_HB),
++ WMI_TLV_HB_SET_TCP_PARAMS_CMDID,
++ WMI_TLV_HB_SET_TCP_PKT_FILTER_CMDID,
++ WMI_TLV_HB_SET_UDP_PARAMS_CMDID,
++ WMI_TLV_HB_SET_UDP_PKT_FILTER_CMDID,
++ WMI_TLV_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RMC),
++ WMI_TLV_RMC_SET_ACTION_PERIOD_CMDID,
++ WMI_TLV_RMC_CONFIG_CMDID,
++ WMI_TLV_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MHF_OFL),
++ WMI_TLV_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID,
++ WMI_TLV_BATCH_SCAN_ENABLE_CMDID =
++ WMI_TLV_CMD(WMI_TLV_GRP_LOCATION_SCAN),
++ WMI_TLV_BATCH_SCAN_DISABLE_CMDID,
++ WMI_TLV_BATCH_SCAN_TRIGGER_RESULT_CMDID,
++ WMI_TLV_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OEM),
++ WMI_TLV_NAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_NAN),
++ WMI_TLV_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_COEX),
++ WMI_TLV_CHAN_AVOID_UPDATE_CMDID,
++ WMI_TLV_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OBSS_OFL),
++ WMI_TLV_OBSS_SCAN_DISABLE_CMDID,
++ WMI_TLV_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_LPI),
++ WMI_TLV_LPI_START_SCAN_CMDID,
++ WMI_TLV_LPI_STOP_SCAN_CMDID,
++ WMI_TLV_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_EXTSCAN),
++ WMI_TLV_EXTSCAN_STOP_CMDID,
++ WMI_TLV_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID,
++ WMI_TLV_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID,
++ WMI_TLV_EXTSCAN_GET_CACHED_RESULTS_CMDID,
++ WMI_TLV_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID,
++ WMI_TLV_EXTSCAN_SET_CAPABILITIES_CMDID,
++ WMI_TLV_EXTSCAN_GET_CAPABILITIES_CMDID,
++ WMI_TLV_SET_DHCP_SERVER_OFFLOAD_CMDID =
++ WMI_TLV_CMD(WMI_TLV_GRP_DHCP_OFL),
++ WMI_TLV_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_IPA),
++ WMI_TLV_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MDNS_OFL),
++ WMI_TLV_MDNS_SET_FQDN_CMDID,
++ WMI_TLV_MDNS_SET_RESPONSE_CMDID,
++ WMI_TLV_MDNS_GET_STATS_CMDID,
++ WMI_TLV_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SAP_OFL),
++};
++
++enum wmi_tlv_event_id {
++ WMI_TLV_SERVICE_READY_EVENTID = 0x1,
++ WMI_TLV_READY_EVENTID,
++ WMI_TLV_SCAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SCAN),
++ WMI_TLV_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PDEV),
++ WMI_TLV_CHAN_INFO_EVENTID,
++ WMI_TLV_PHYERR_EVENTID,
++ WMI_TLV_PDEV_DUMP_EVENTID,
++ WMI_TLV_TX_PAUSE_EVENTID,
++ WMI_TLV_DFS_RADAR_EVENTID,
++ WMI_TLV_PDEV_L1SS_TRACK_EVENTID,
++ WMI_TLV_PDEV_TEMPERATURE_EVENTID,
++ WMI_TLV_VDEV_START_RESP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_VDEV),
++ WMI_TLV_VDEV_STOPPED_EVENTID,
++ WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
++ WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID,
++ WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER),
++ WMI_TLV_PEER_INFO_EVENTID,
++ WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID,
++ WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID,
++ WMI_TLV_PEER_STATE_EVENTID,
++ WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT),
++ WMI_TLV_HOST_SWBA_EVENTID,
++ WMI_TLV_TBTTOFFSET_UPDATE_EVENTID,
++ WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID,
++ WMI_TLV_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID,
++ WMI_TLV_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_BA_NEG),
++ WMI_TLV_TX_ADDBA_COMPLETE_EVENTID,
++ WMI_TLV_BA_RSP_SSN_EVENTID,
++ WMI_TLV_AGGR_STATE_TRIG_EVENTID,
++ WMI_TLV_ROAM_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_ROAM),
++ WMI_TLV_PROFILE_MATCH,
++ WMI_TLV_ROAM_SYNCH_EVENTID,
++ WMI_TLV_P2P_DISC_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_P2P),
++ WMI_TLV_P2P_NOA_EVENTID,
++ WMI_TLV_PDEV_RESUME_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SUSPEND),
++ WMI_TLV_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_WOW),
++ WMI_TLV_D0_WOW_DISABLE_ACK_EVENTID,
++ WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_RTT),
++ WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID,
++ WMI_TLV_RTT_ERROR_REPORT_EVENTID,
++ WMI_TLV_STATS_EXT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_STATS),
++ WMI_TLV_IFACE_LINK_STATS_EVENTID,
++ WMI_TLV_PEER_LINK_STATS_EVENTID,
++ WMI_TLV_RADIO_LINK_STATS_EVENTID,
++ WMI_TLV_NLO_MATCH_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NLO_OFL),
++ WMI_TLV_NLO_SCAN_COMPLETE_EVENTID,
++ WMI_TLV_APFIND_EVENTID,
++ WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GTK_OFL),
++ WMI_TLV_GTK_REKEY_FAIL_EVENTID,
++ WMI_TLV_CSA_HANDLING_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CSA_OFL),
++ WMI_TLV_CHATTER_PC_QUERY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CHATTER),
++ WMI_TLV_ECHO_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MISC),
++ WMI_TLV_PDEV_UTF_EVENTID,
++ WMI_TLV_DEBUG_MESG_EVENTID,
++ WMI_TLV_UPDATE_STATS_EVENTID,
++ WMI_TLV_DEBUG_PRINT_EVENTID,
++ WMI_TLV_DCS_INTERFERENCE_EVENTID,
++ WMI_TLV_PDEV_QVIT_EVENTID,
++ WMI_TLV_WLAN_PROFILE_DATA_EVENTID,
++ WMI_TLV_PDEV_FTM_INTG_EVENTID,
++ WMI_TLV_WLAN_FREQ_AVOID_EVENTID,
++ WMI_TLV_VDEV_GET_KEEPALIVE_EVENTID,
++ WMI_TLV_THERMAL_MGMT_EVENTID,
++ WMI_TLV_DIAG_DATA_CONTAINER_EVENTID,
++ WMI_TLV_HOST_AUTO_SHUTDOWN_EVENTID,
++ WMI_TLV_UPDATE_WHAL_MIB_STATS_EVENTID,
++ WMI_TLV_UPDATE_VDEV_RATE_STATS_EVENTID,
++ WMI_TLV_DIAG_EVENTID,
++ WMI_TLV_GPIO_INPUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GPIO),
++ WMI_TLV_UPLOADH_EVENTID,
++ WMI_TLV_CAPTUREH_EVENTID,
++ WMI_TLV_RFKILL_STATE_CHANGE_EVENTID,
++ WMI_TLV_TDLS_PEER_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_TDLS),
++ WMI_TLV_BATCH_SCAN_ENABLED_EVENTID =
++ WMI_TLV_EV(WMI_TLV_GRP_LOCATION_SCAN),
++ WMI_TLV_BATCH_SCAN_RESULT_EVENTID,
++ WMI_TLV_OEM_CAPABILITY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_OEM),
++ WMI_TLV_OEM_MEASUREMENT_REPORT_EVENTID,
++ WMI_TLV_OEM_ERROR_REPORT_EVENTID,
++ WMI_TLV_NAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NAN),
++ WMI_TLV_LPI_RESULT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_LPI),
++ WMI_TLV_LPI_STATUS_EVENTID,
++ WMI_TLV_LPI_HANDOFF_EVENTID,
++ WMI_TLV_EXTSCAN_START_STOP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_EXTSCAN),
++ WMI_TLV_EXTSCAN_OPERATION_EVENTID,
++ WMI_TLV_EXTSCAN_TABLE_USAGE_EVENTID,
++ WMI_TLV_EXTSCAN_CACHED_RESULTS_EVENTID,
++ WMI_TLV_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID,
++ WMI_TLV_EXTSCAN_HOTLIST_MATCH_EVENTID,
++ WMI_TLV_EXTSCAN_CAPABILITIES_EVENTID,
++ WMI_TLV_MDNS_STATS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MDNS_OFL),
++ WMI_TLV_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SAP_OFL),
++ WMI_TLV_SAP_OFL_DEL_STA_EVENTID,
++};
++
++enum wmi_tlv_pdev_param {
++ WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
++ WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK,
++ WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G,
++ WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G,
++ WMI_TLV_PDEV_PARAM_TXPOWER_SCALE,
++ WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE,
++ WMI_TLV_PDEV_PARAM_BEACON_TX_MODE,
++ WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
++ WMI_TLV_PDEV_PARAM_PROTECTION_MODE,
++ WMI_TLV_PDEV_PARAM_DYNAMIC_BW,
++ WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
++ WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH,
++ WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH,
++ WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING,
++ WMI_TLV_PDEV_PARAM_LTR_ENABLE,
++ WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE,
++ WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK,
++ WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI,
++ WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO,
++ WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
++ WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
++ WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE,
++ WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
++ WMI_TLV_PDEV_PARAM_L1SS_ENABLE,
++ WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE,
++ WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
++ WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_WATERMARK,
++ WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
++ WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
++ WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
++ WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
++ WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
++ WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
++ WMI_TLV_PDEV_PARAM_PMF_QOS,
++ WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE,
++ WMI_TLV_PDEV_PARAM_DCS,
++ WMI_TLV_PDEV_PARAM_ANI_ENABLE,
++ WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD,
++ WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD,
++ WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL,
++ WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL,
++ WMI_TLV_PDEV_PARAM_DYNTXCHAIN,
++ WMI_TLV_PDEV_PARAM_PROXY_STA,
++ WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG,
++ WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP,
++ WMI_TLV_PDEV_PARAM_RFKILL_ENABLE,
++ WMI_TLV_PDEV_PARAM_BURST_DUR,
++ WMI_TLV_PDEV_PARAM_BURST_ENABLE,
++ WMI_TLV_PDEV_PARAM_HW_RFKILL_CONFIG,
++ WMI_TLV_PDEV_PARAM_LOW_POWER_RF_ENABLE,
++ WMI_TLV_PDEV_PARAM_L1SS_TRACK,
++ WMI_TLV_PDEV_PARAM_HYST_EN,
++ WMI_TLV_PDEV_PARAM_POWER_COLLAPSE_ENABLE,
++ WMI_TLV_PDEV_PARAM_LED_SYS_STATE,
++ WMI_TLV_PDEV_PARAM_LED_ENABLE,
++ WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY,
++ WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE,
++ WMI_TLV_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE,
++ WMI_TLV_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD,
++ WMI_TLV_PDEV_PARAM_TXPOWER_REASON_NONE,
++ WMI_TLV_PDEV_PARAM_TXPOWER_REASON_SAR,
++ WMI_TLV_PDEV_PARAM_TXPOWER_REASON_MAX,
++};
++
++enum wmi_tlv_vdev_param {
++ WMI_TLV_VDEV_PARAM_RTS_THRESHOLD = 0x1,
++ WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
++ WMI_TLV_VDEV_PARAM_BEACON_INTERVAL,
++ WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL,
++ WMI_TLV_VDEV_PARAM_MULTICAST_RATE,
++ WMI_TLV_VDEV_PARAM_MGMT_TX_RATE,
++ WMI_TLV_VDEV_PARAM_SLOT_TIME,
++ WMI_TLV_VDEV_PARAM_PREAMBLE,
++ WMI_TLV_VDEV_PARAM_SWBA_TIME,
++ WMI_TLV_VDEV_STATS_UPDATE_PERIOD,
++ WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME,
++ WMI_TLV_VDEV_HOST_SWBA_INTERVAL,
++ WMI_TLV_VDEV_PARAM_DTIM_PERIOD,
++ WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
++ WMI_TLV_VDEV_PARAM_WDS,
++ WMI_TLV_VDEV_PARAM_ATIM_WINDOW,
++ WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX,
++ WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT,
++ WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT,
++ WMI_TLV_VDEV_PARAM_FEATURE_WMM,
++ WMI_TLV_VDEV_PARAM_CHWIDTH,
++ WMI_TLV_VDEV_PARAM_CHEXTOFFSET,
++ WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION,
++ WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT,
++ WMI_TLV_VDEV_PARAM_MGMT_RATE,
++ WMI_TLV_VDEV_PARAM_PROTECTION_MODE,
++ WMI_TLV_VDEV_PARAM_FIXED_RATE,
++ WMI_TLV_VDEV_PARAM_SGI,
++ WMI_TLV_VDEV_PARAM_LDPC,
++ WMI_TLV_VDEV_PARAM_TX_STBC,
++ WMI_TLV_VDEV_PARAM_RX_STBC,
++ WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD,
++ WMI_TLV_VDEV_PARAM_DEF_KEYID,
++ WMI_TLV_VDEV_PARAM_NSS,
++ WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE,
++ WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE,
++ WMI_TLV_VDEV_PARAM_MCAST_INDICATE,
++ WMI_TLV_VDEV_PARAM_DHCP_INDICATE,
++ WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
++ WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
++ WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
++ WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
++ WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS,
++ WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS,
++ WMI_TLV_VDEV_PARAM_TXBF,
++ WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE,
++ WMI_TLV_VDEV_PARAM_DROP_UNENCRY,
++ WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE,
++ WMI_TLV_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_SLOP_STEP,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_INIT_SLOP,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE,
++ WMI_TLV_VDEV_PARAM_TX_PWRLIMIT,
++ WMI_TLV_VDEV_PARAM_SNR_NUM_FOR_CAL,
++ WMI_TLV_VDEV_PARAM_ROAM_FW_OFFLOAD,
++ WMI_TLV_VDEV_PARAM_ENABLE_RMC,
++ WMI_TLV_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS,
++ WMI_TLV_VDEV_PARAM_MAX_RATE,
++ WMI_TLV_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE,
++ WMI_TLV_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR,
++ WMI_TLV_VDEV_PARAM_EBT_RESYNC_TIMEOUT,
++ WMI_TLV_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE,
++ WMI_TLV_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED,
++ WMI_TLV_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED,
++ WMI_TLV_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED,
++ WMI_TLV_VDEV_PARAM_INACTIVITY_CNT,
++ WMI_TLV_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS,
++ WMI_TLV_VDEV_PARAM_DTIM_POLICY,
++ WMI_TLV_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS,
++ WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE,
++};
++
++enum wmi_tlv_tag {
++ WMI_TLV_TAG_LAST_RESERVED = 15,
++
++ WMI_TLV_TAG_FIRST_ARRAY_ENUM,
++ WMI_TLV_TAG_ARRAY_UINT32 = WMI_TLV_TAG_FIRST_ARRAY_ENUM,
++ WMI_TLV_TAG_ARRAY_BYTE,
++ WMI_TLV_TAG_ARRAY_STRUCT,
++ WMI_TLV_TAG_ARRAY_FIXED_STRUCT,
++ WMI_TLV_TAG_LAST_ARRAY_ENUM = 31,
++
++ WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT,
++ WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES,
++ WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ,
++ WMI_TLV_TAG_STRUCT_READY_EVENT,
++ WMI_TLV_TAG_STRUCT_SCAN_EVENT,
++ WMI_TLV_TAG_STRUCT_PDEV_TPC_CONFIG_EVENT,
++ WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT,
++ WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR,
++ WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT,
++ WMI_TLV_TAG_STRUCT_VDEV_STOPPED_EVENT,
++ WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_COMPLETE_EVENT,
++ WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT,
++ WMI_TLV_TAG_STRUCT_MGMT_RX_HDR,
++ WMI_TLV_TAG_STRUCT_TBTT_OFFSET_EVENT,
++ WMI_TLV_TAG_STRUCT_TX_DELBA_COMPLETE_EVENT,
++ WMI_TLV_TAG_STRUCT_TX_ADDBA_COMPLETE_EVENT,
++ WMI_TLV_TAG_STRUCT_ROAM_EVENT,
++ WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO,
++ WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO_SECTION_BITMAP,
++ WMI_TLV_TAG_STRUCT_RTT_EVENT_HEADER,
++ WMI_TLV_TAG_STRUCT_RTT_ERROR_REPORT_EVENT,
++ WMI_TLV_TAG_STRUCT_RTT_MEAS_EVENT,
++ WMI_TLV_TAG_STRUCT_ECHO_EVENT,
++ WMI_TLV_TAG_STRUCT_FTM_INTG_EVENT,
++ WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_EVENT,
++ WMI_TLV_TAG_STRUCT_GPIO_INPUT_EVENT,
++ WMI_TLV_TAG_STRUCT_CSA_EVENT,
++ WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_STATUS_EVENT,
++ WMI_TLV_TAG_STRUCT_IGTK_INFO,
++ WMI_TLV_TAG_STRUCT_DCS_INTERFERENCE_EVENT,
++ WMI_TLV_TAG_STRUCT_ATH_DCS_CW_INT,
++ WMI_TLV_TAG_STRUCT_ATH_DCS_WLAN_INT_STAT,
++ WMI_TLV_TAG_STRUCT_WLAN_PROFILE_CTX_T,
++ WMI_TLV_TAG_STRUCT_WLAN_PROFILE_T,
++ WMI_TLV_TAG_STRUCT_PDEV_QVIT_EVENT,
++ WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT,
++ WMI_TLV_TAG_STRUCT_TIM_INFO,
++ WMI_TLV_TAG_STRUCT_P2P_NOA_INFO,
++ WMI_TLV_TAG_STRUCT_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGES_EVENT,
++ WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGE_DESC,
++ WMI_TLV_TAG_STRUCT_GTK_REKEY_FAIL_EVENT,
++ WMI_TLV_TAG_STRUCT_INIT_CMD,
++ WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG,
++ WMI_TLV_TAG_STRUCT_WLAN_HOST_MEMORY_CHUNK,
++ WMI_TLV_TAG_STRUCT_START_SCAN_CMD,
++ WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD,
++ WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD,
++ WMI_TLV_TAG_STRUCT_CHANNEL,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_WMM_PARAMS,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_QUIET_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD,
++ WMI_TLV_TAG_STRUCT_P2P_NOA_DESCRIPTOR,
++ WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE,
++ WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_UP_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD,
++ WMI_TLV_TAG_STRUCT_VHT_RATE_SET,
++ WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD,
++ WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD,
++ WMI_TLV_TAG_STRUCT_BCN_PRB_INFO,
++ WMI_TLV_TAG_STRUCT_PEER_TID_ADDBA_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_TID_DELBA_CMD,
++ WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD,
++ WMI_TLV_TAG_STRUCT_STA_DTIM_PS_METHOD_CMD,
++ WMI_TLV_TAG_STRUCT_ROAM_SCAN_MODE,
++ WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_THRESHOLD,
++ WMI_TLV_TAG_STRUCT_ROAM_SCAN_PERIOD,
++ WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
++ WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD,
++ WMI_TLV_TAG_STRUCT_ADD_BCN_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_RMV_BCN_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_HOSTWAKEUP_FROM_SLEEP_CMD,
++ WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD,
++ WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM,
++ WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD,
++ WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE,
++ WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE,
++ WMI_TLV_TAG_STRUCT_FTM_INTG_CMD,
++ WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD,
++ WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE,
++ WMI_TLV_TAG_STRUCT_P2P_SET_VENDOR_IE_DATA_CMD,
++ WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_RATE_RETRY_SCHED_CMD,
++ WMI_TLV_TAG_STRUCT_WLAN_PROFILE_TRIGGER_CMD,
++ WMI_TLV_TAG_STRUCT_WLAN_PROFILE_SET_HIST_INTVL_CMD,
++ WMI_TLV_TAG_STRUCT_WLAN_PROFILE_GET_PROF_DATA_CMD,
++ WMI_TLV_TAG_STRUCT_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_DEL_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_ADD_DEL_EVT_CMD,
++ WMI_TLV_TAG_STRUCT_RTT_MEASREQ_HEAD,
++ WMI_TLV_TAG_STRUCT_RTT_MEASREQ_BODY,
++ WMI_TLV_TAG_STRUCT_RTT_TSF_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_CONFIGURE_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD,
++ WMI_TLV_TAG_STRUCT_NLO_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_NLO_CONFIGURED_PARAMETERS,
++ WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_CHANSWITCH_CMD,
++ WMI_TLV_TAG_STRUCT_CHATTER_SET_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_ECHO_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_SET_KEEPALIVE_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_CMD,
++ WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD,
++ WMI_TLV_TAG_STRUCT_GPIO_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_GPIO_OUTPUT_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_ADD_WDS_ENTRY_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_REMOVE_WDS_ENTRY_CMD,
++ WMI_TLV_TAG_STRUCT_BCN_TX_HDR,
++ WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD,
++ WMI_TLV_TAG_STRUCT_MGMT_TX_HDR,
++ WMI_TLV_TAG_STRUCT_ADDBA_CLEAR_RESP_CMD,
++ WMI_TLV_TAG_STRUCT_ADDBA_SEND_CMD,
++ WMI_TLV_TAG_STRUCT_DELBA_SEND_CMD,
++ WMI_TLV_TAG_STRUCT_ADDBA_SETRESPONSE_CMD,
++ WMI_TLV_TAG_STRUCT_SEND_SINGLEAMSDU_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_HT_IE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_VHT_IE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_DSCP_TID_MAP_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_GREEN_AP_PS_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_GET_TPC_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_BASE_MACADDR_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_MCAST_GROUP_CMD,
++ WMI_TLV_TAG_STRUCT_ROAM_AP_PROFILE,
++ WMI_TLV_TAG_STRUCT_AP_PROFILE,
++ WMI_TLV_TAG_STRUCT_SCAN_SCH_PRIORITY_TABLE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_DFS_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_DFS_DISABLE_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_ADD_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_BITMAP_PATTERN_T,
++ WMI_TLV_TAG_STRUCT_WOW_IPV4_SYNC_PATTERN_T,
++ WMI_TLV_TAG_STRUCT_WOW_IPV6_SYNC_PATTERN_T,
++ WMI_TLV_TAG_STRUCT_WOW_MAGIC_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_SCAN_UPDATE_REQUEST_CMD,
++ WMI_TLV_TAG_STRUCT_CHATTER_PKT_COALESCING_FILTER,
++ WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_ADD_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_DELETE_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_QUERY_CMD,
++ WMI_TLV_TAG_STRUCT_TXBF_CMD,
++ WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_NLO_EVENT,
++ WMI_TLV_TAG_STRUCT_CHATTER_QUERY_REPLY_EVENT,
++ WMI_TLV_TAG_STRUCT_UPLOAD_H_HDR,
++ WMI_TLV_TAG_STRUCT_CAPTURE_H_EVENT_HDR,
++ WMI_TLV_TAG_STRUCT_VDEV_WNM_SLEEPMODE_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_WMM_ADDTS_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_WMM_DELTS_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_TDLS_SET_STATE_CMD,
++ WMI_TLV_TAG_STRUCT_TDLS_PEER_UPDATE_CMD,
++ WMI_TLV_TAG_STRUCT_TDLS_PEER_EVENT,
++ WMI_TLV_TAG_STRUCT_TDLS_PEER_CAPABILITIES,
++ WMI_TLV_TAG_STRUCT_VDEV_MCC_SET_TBTT_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_ROAM_CHAN_LIST,
++ WMI_TLV_TAG_STRUCT_VDEV_MCC_BCN_INTVL_CHANGE_EVENT,
++ WMI_TLV_TAG_STRUCT_RESMGR_ADAPTIVE_OCS_CMD,
++ WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_TIME_QUOTA_CMD,
++ WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_LATENCY_CMD,
++ WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD,
++ WMI_TLV_TAG_STRUCT_BA_RSP_SSN_EVENT,
++ WMI_TLV_TAG_STRUCT_STA_SMPS_FORCE_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_SET_MCASTBCAST_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_P2P_SET_OPPPS_CMD,
++ WMI_TLV_TAG_STRUCT_P2P_SET_NOA_CMD,
++ WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM,
++ WMI_TLV_TAG_STRUCT_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM,
++ WMI_TLV_TAG_STRUCT_STA_SMPS_PARAM_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_SET_GTX_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_MCC_SCHED_TRAFFIC_STATS_CMD,
++ WMI_TLV_TAG_STRUCT_MCC_SCHED_STA_TRAFFIC_STATS,
++ WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT,
++ WMI_TLV_TAG_STRUCT_P2P_NOA_EVENT,
++ WMI_TLV_TAG_STRUCT_HB_SET_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_HB_SET_TCP_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_HB_SET_TCP_PKT_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_HB_SET_UDP_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_HB_SET_UDP_PKT_FILTER_CMD,
++ WMI_TLV_TAG_STRUCT_HB_IND_EVENT,
++ WMI_TLV_TAG_STRUCT_TX_PAUSE_EVENT,
++ WMI_TLV_TAG_STRUCT_RFKILL_EVENT,
++ WMI_TLV_TAG_STRUCT_DFS_RADAR_EVENT,
++ WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_ENA_CMD,
++ WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_DIS_CMD,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_SCAN_LIST,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_NETWORK_INFO,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_DISABLE_CMD,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_TRIGGER_RESULT_CMD,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLED_EVENT,
++ WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_EVENT,
++ WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_START_CMD,
++ WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_STOP_CMD,
++ WMI_TLV_TAG_STRUCT_THERMAL_MGMT_CMD,
++ WMI_TLV_TAG_STRUCT_THERMAL_MGMT_EVENT,
++ WMI_TLV_TAG_STRUCT_PEER_INFO_REQ_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_INFO_EVENT,
++ WMI_TLV_TAG_STRUCT_PEER_INFO,
++ WMI_TLV_TAG_STRUCT_PEER_TX_FAIL_CNT_THR_EVENT,
++ WMI_TLV_TAG_STRUCT_RMC_SET_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_RMC_SET_ACTION_PERIOD_CMD,
++ WMI_TLV_TAG_STRUCT_RMC_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_SET_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD,
++ WMI_TLV_TAG_STRUCT_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_NAN_CMD_PARAM,
++ WMI_TLV_TAG_STRUCT_NAN_EVENT_HDR,
++ WMI_TLV_TAG_STRUCT_PDEV_L1SS_TRACK_EVENT,
++ WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT,
++ WMI_TLV_TAG_STRUCT_MODEM_POWER_STATE_CMD_PARAM,
++ WMI_TLV_TAG_STRUCT_PEER_GET_ESTIMATED_LINKSPEED_CMD,
++ WMI_TLV_TAG_STRUCT_PEER_ESTIMATED_LINKSPEED_EVENT,
++ WMI_TLV_TAG_STRUCT_AGGR_STATE_TRIG_EVENT,
++ WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_ROUTING_TABLE_ENTRY,
++ WMI_TLV_TAG_STRUCT_ROAM_SCAN_CMD,
++ WMI_TLV_TAG_STRUCT_REQ_STATS_EXT_CMD,
++ WMI_TLV_TAG_STRUCT_STATS_EXT_EVENT,
++ WMI_TLV_TAG_STRUCT_OBSS_SCAN_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_OBSS_SCAN_DISABLE_CMD,
++ WMI_TLV_TAG_STRUCT_OFFLOAD_PRB_RSP_TX_STATUS_EVENT,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_LED_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_CFG_CMD,
++ WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_EVENT,
++ WMI_TLV_TAG_STRUCT_UPDATE_WHAL_MIB_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_CHAN_AVOID_UPDATE_CMD_PARAM,
++ WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_PKT_PATTERN_T,
++ WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_TMR_PATTERN_T,
++ WMI_TLV_TAG_STRUCT_WOW_IOAC_ADD_KEEPALIVE_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_IOAC_DEL_KEEPALIVE_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_IOAC_KEEPALIVE_T,
++ WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_ADD_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_DEL_PATTERN_CMD,
++ WMI_TLV_TAG_STRUCT_START_LINK_STATS_CMD,
++ WMI_TLV_TAG_STRUCT_CLEAR_LINK_STATS_CMD,
++ WMI_TLV_TAG_STRUCT_REQUEST_LINK_STATS_CMD,
++ WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_PEER_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_CHANNEL_STATS,
++ WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS,
++ WMI_TLV_TAG_STRUCT_RATE_STATS,
++ WMI_TLV_TAG_STRUCT_PEER_LINK_STATS,
++ WMI_TLV_TAG_STRUCT_WMM_AC_STATS,
++ WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS,
++ WMI_TLV_TAG_STRUCT_LPI_MGMT_SNOOPING_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_LPI_START_SCAN_CMD,
++ WMI_TLV_TAG_STRUCT_LPI_STOP_SCAN_CMD,
++ WMI_TLV_TAG_STRUCT_LPI_RESULT_EVENT,
++ WMI_TLV_TAG_STRUCT_PEER_STATE_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CHANNEL_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_START_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_STOP_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CACHED_RESULTS_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_SET_CAPABILITIES_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CAPABILITIES_CMD,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_OPERATION_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_START_STOP_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_TABLE_USAGE_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_DESCRIPTOR_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_RSSI_INFO_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_CACHED_RESULTS_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MATCH_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_CAPABILITIES_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_CACHE_CAPABILITIES_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT,
++ WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT,
++ WMI_TLV_TAG_STRUCT_D0_WOW_ENABLE_DISABLE_CMD,
++ WMI_TLV_TAG_STRUCT_D0_WOW_DISABLE_ACK_EVENT,
++ WMI_TLV_TAG_STRUCT_UNIT_TEST_CMD,
++ WMI_TLV_TAG_STRUCT_ROAM_OFFLOAD_TLV_PARAM,
++ WMI_TLV_TAG_STRUCT_ROAM_11I_OFFLOAD_TLV_PARAM,
++ WMI_TLV_TAG_STRUCT_ROAM_11R_OFFLOAD_TLV_PARAM,
++ WMI_TLV_TAG_STRUCT_ROAM_ESE_OFFLOAD_TLV_PARAM,
++ WMI_TLV_TAG_STRUCT_ROAM_SYNCH_EVENT,
++ WMI_TLV_TAG_STRUCT_ROAM_SYNCH_COMPLETE,
++ WMI_TLV_TAG_STRUCT_EXTWOW_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE1_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE2_PARAMS_CMD,
++ WMI_TLV_TAG_STRUCT_LPI_STATUS_EVENT,
++ WMI_TLV_TAG_STRUCT_LPI_HANDOFF_EVENT,
++ WMI_TLV_TAG_STRUCT_VDEV_RATE_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_VDEV_RATE_HT_INFO,
++ WMI_TLV_TAG_STRUCT_RIC_REQUEST,
++ WMI_TLV_TAG_STRUCT_PDEV_GET_TEMPERATURE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_TEMPERATURE_EVENT,
++ WMI_TLV_TAG_STRUCT_SET_DHCP_SERVER_OFFLOAD_CMD,
++ WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG_CMD,
++ WMI_TLV_TAG_STRUCT_RIC_TSPEC,
++ WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG,
++ WMI_TLV_TAG_STRUCT_IPA_OFFLOAD_CMD,
++ WMI_TLV_TAG_STRUCT_SCAN_PROB_REQ_OUI_CMD,
++ WMI_TLV_TAG_STRUCT_KEY_MATERIAL,
++ WMI_TLV_TAG_STRUCT_TDLS_SET_OFFCHAN_MODE_CMD,
++ WMI_TLV_TAG_STRUCT_SET_LED_FLASHING_CMD,
++ WMI_TLV_TAG_STRUCT_MDNS_OFFLOAD_CMD,
++ WMI_TLV_TAG_STRUCT_MDNS_SET_FQDN_CMD,
++ WMI_TLV_TAG_STRUCT_MDNS_SET_RESP_CMD,
++ WMI_TLV_TAG_STRUCT_MDNS_GET_STATS_CMD,
++ WMI_TLV_TAG_STRUCT_MDNS_STATS_EVENT,
++ WMI_TLV_TAG_STRUCT_ROAM_INVOKE_CMD,
++ WMI_TLV_TAG_STRUCT_PDEV_RESUME_EVENT,
++ WMI_TLV_TAG_STRUCT_PDEV_SET_ANTENNA_DIVERSITY_CMD,
++ WMI_TLV_TAG_STRUCT_SAP_OFL_ENABLE_CMD,
++ WMI_TLV_TAG_STRUCT_SAP_OFL_ADD_STA_EVENT,
++ WMI_TLV_TAG_STRUCT_SAP_OFL_DEL_STA_EVENT,
++ WMI_TLV_TAG_STRUCT_APFIND_CMD_PARAM,
++ WMI_TLV_TAG_STRUCT_APFIND_EVENT_HDR,
++
++ WMI_TLV_TAG_MAX
++};
++
++enum wmi_tlv_service {
++ WMI_TLV_SERVICE_BEACON_OFFLOAD = 0,
++ WMI_TLV_SERVICE_SCAN_OFFLOAD,
++ WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD,
++ WMI_TLV_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_TLV_SERVICE_STA_PWRSAVE,
++ WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_TLV_SERVICE_AP_UAPSD,
++ WMI_TLV_SERVICE_AP_DFS,
++ WMI_TLV_SERVICE_11AC,
++ WMI_TLV_SERVICE_BLOCKACK,
++ WMI_TLV_SERVICE_PHYERR,
++ WMI_TLV_SERVICE_BCN_FILTER,
++ WMI_TLV_SERVICE_RTT,
++ WMI_TLV_SERVICE_WOW,
++ WMI_TLV_SERVICE_RATECTRL_CACHE,
++ WMI_TLV_SERVICE_IRAM_TIDS,
++ WMI_TLV_SERVICE_ARPNS_OFFLOAD,
++ WMI_TLV_SERVICE_NLO,
++ WMI_TLV_SERVICE_GTK_OFFLOAD,
++ WMI_TLV_SERVICE_SCAN_SCH,
++ WMI_TLV_SERVICE_CSA_OFFLOAD,
++ WMI_TLV_SERVICE_CHATTER,
++ WMI_TLV_SERVICE_COEX_FREQAVOID,
++ WMI_TLV_SERVICE_PACKET_POWER_SAVE,
++ WMI_TLV_SERVICE_FORCE_FW_HANG,
++ WMI_TLV_SERVICE_GPIO,
++ WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
++ WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
++ WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
++ WMI_TLV_SERVICE_STA_KEEP_ALIVE,
++ WMI_TLV_SERVICE_TX_ENCAP,
++ WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC,
++ WMI_TLV_SERVICE_EARLY_RX,
++ WMI_TLV_SERVICE_STA_SMPS,
++ WMI_TLV_SERVICE_FWTEST,
++ WMI_TLV_SERVICE_STA_WMMAC,
++ WMI_TLV_SERVICE_TDLS,
++ WMI_TLV_SERVICE_BURST,
++ WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE,
++ WMI_TLV_SERVICE_ADAPTIVE_OCS,
++ WMI_TLV_SERVICE_BA_SSN_SUPPORT,
++ WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
++ WMI_TLV_SERVICE_WLAN_HB,
++ WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT,
++ WMI_TLV_SERVICE_BATCH_SCAN,
++ WMI_TLV_SERVICE_QPOWER,
++ WMI_TLV_SERVICE_PLMREQ,
++ WMI_TLV_SERVICE_THERMAL_MGMT,
++ WMI_TLV_SERVICE_RMC,
++ WMI_TLV_SERVICE_MHF_OFFLOAD,
++ WMI_TLV_SERVICE_COEX_SAR,
++ WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE,
++ WMI_TLV_SERVICE_NAN,
++ WMI_TLV_SERVICE_L1SS_STAT,
++ WMI_TLV_SERVICE_ESTIMATE_LINKSPEED,
++ WMI_TLV_SERVICE_OBSS_SCAN,
++ WMI_TLV_SERVICE_TDLS_OFFCHAN,
++ WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA,
++ WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA,
++ WMI_TLV_SERVICE_IBSS_PWRSAVE,
++ WMI_TLV_SERVICE_LPASS,
++ WMI_TLV_SERVICE_EXTSCAN,
++ WMI_TLV_SERVICE_D0WOW,
++ WMI_TLV_SERVICE_HSOFFLOAD,
++ WMI_TLV_SERVICE_ROAM_HO_OFFLOAD,
++ WMI_TLV_SERVICE_RX_FULL_REORDER,
++ WMI_TLV_SERVICE_DHCP_OFFLOAD,
++ WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
++ WMI_TLV_SERVICE_MDNS_OFFLOAD,
++ WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
++};
++
++#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
++ ((svc_id) < (len) && \
++ __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
++ BIT((svc_id)%(sizeof(u32))))
++
++#define SVCMAP(x, y, len) \
++ do { \
++ if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
++ __set_bit(y, out); \
++ } while (0)
++
++static inline void
++wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len)
++{
++ SVCMAP(WMI_TLV_SERVICE_BEACON_OFFLOAD,
++ WMI_SERVICE_BEACON_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_SCAN_OFFLOAD,
++ WMI_SERVICE_SCAN_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD,
++ WMI_SERVICE_ROAM_SCAN_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_BCN_MISS_OFFLOAD,
++ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_PWRSAVE,
++ WMI_SERVICE_STA_PWRSAVE, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE,
++ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
++ SVCMAP(WMI_TLV_SERVICE_AP_UAPSD,
++ WMI_SERVICE_AP_UAPSD, len);
++ SVCMAP(WMI_TLV_SERVICE_AP_DFS,
++ WMI_SERVICE_AP_DFS, len);
++ SVCMAP(WMI_TLV_SERVICE_11AC,
++ WMI_SERVICE_11AC, len);
++ SVCMAP(WMI_TLV_SERVICE_BLOCKACK,
++ WMI_SERVICE_BLOCKACK, len);
++ SVCMAP(WMI_TLV_SERVICE_PHYERR,
++ WMI_SERVICE_PHYERR, len);
++ SVCMAP(WMI_TLV_SERVICE_BCN_FILTER,
++ WMI_SERVICE_BCN_FILTER, len);
++ SVCMAP(WMI_TLV_SERVICE_RTT,
++ WMI_SERVICE_RTT, len);
++ SVCMAP(WMI_TLV_SERVICE_WOW,
++ WMI_SERVICE_WOW, len);
++ SVCMAP(WMI_TLV_SERVICE_RATECTRL_CACHE,
++ WMI_SERVICE_RATECTRL_CACHE, len);
++ SVCMAP(WMI_TLV_SERVICE_IRAM_TIDS,
++ WMI_SERVICE_IRAM_TIDS, len);
++ SVCMAP(WMI_TLV_SERVICE_ARPNS_OFFLOAD,
++ WMI_SERVICE_ARPNS_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_NLO,
++ WMI_SERVICE_NLO, len);
++ SVCMAP(WMI_TLV_SERVICE_GTK_OFFLOAD,
++ WMI_SERVICE_GTK_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_SCAN_SCH,
++ WMI_SERVICE_SCAN_SCH, len);
++ SVCMAP(WMI_TLV_SERVICE_CSA_OFFLOAD,
++ WMI_SERVICE_CSA_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_CHATTER,
++ WMI_SERVICE_CHATTER, len);
++ SVCMAP(WMI_TLV_SERVICE_COEX_FREQAVOID,
++ WMI_SERVICE_COEX_FREQAVOID, len);
++ SVCMAP(WMI_TLV_SERVICE_PACKET_POWER_SAVE,
++ WMI_SERVICE_PACKET_POWER_SAVE, len);
++ SVCMAP(WMI_TLV_SERVICE_FORCE_FW_HANG,
++ WMI_SERVICE_FORCE_FW_HANG, len);
++ SVCMAP(WMI_TLV_SERVICE_GPIO,
++ WMI_SERVICE_GPIO, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
++ WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
++ WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
++ WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_KEEP_ALIVE,
++ WMI_SERVICE_STA_KEEP_ALIVE, len);
++ SVCMAP(WMI_TLV_SERVICE_TX_ENCAP,
++ WMI_SERVICE_TX_ENCAP, len);
++ SVCMAP(WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC,
++ WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, len);
++ SVCMAP(WMI_TLV_SERVICE_EARLY_RX,
++ WMI_SERVICE_EARLY_RX, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_SMPS,
++ WMI_SERVICE_STA_SMPS, len);
++ SVCMAP(WMI_TLV_SERVICE_FWTEST,
++ WMI_SERVICE_FWTEST, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_WMMAC,
++ WMI_SERVICE_STA_WMMAC, len);
++ SVCMAP(WMI_TLV_SERVICE_TDLS,
++ WMI_SERVICE_TDLS, len);
++ SVCMAP(WMI_TLV_SERVICE_BURST,
++ WMI_SERVICE_BURST, len);
++ SVCMAP(WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE,
++ WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, len);
++ SVCMAP(WMI_TLV_SERVICE_ADAPTIVE_OCS,
++ WMI_SERVICE_ADAPTIVE_OCS, len);
++ SVCMAP(WMI_TLV_SERVICE_BA_SSN_SUPPORT,
++ WMI_SERVICE_BA_SSN_SUPPORT, len);
++ SVCMAP(WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
++ WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE, len);
++ SVCMAP(WMI_TLV_SERVICE_WLAN_HB,
++ WMI_SERVICE_WLAN_HB, len);
++ SVCMAP(WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT,
++ WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, len);
++ SVCMAP(WMI_TLV_SERVICE_BATCH_SCAN,
++ WMI_SERVICE_BATCH_SCAN, len);
++ SVCMAP(WMI_TLV_SERVICE_QPOWER,
++ WMI_SERVICE_QPOWER, len);
++ SVCMAP(WMI_TLV_SERVICE_PLMREQ,
++ WMI_SERVICE_PLMREQ, len);
++ SVCMAP(WMI_TLV_SERVICE_THERMAL_MGMT,
++ WMI_SERVICE_THERMAL_MGMT, len);
++ SVCMAP(WMI_TLV_SERVICE_RMC,
++ WMI_SERVICE_RMC, len);
++ SVCMAP(WMI_TLV_SERVICE_MHF_OFFLOAD,
++ WMI_SERVICE_MHF_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_COEX_SAR,
++ WMI_SERVICE_COEX_SAR, len);
++ SVCMAP(WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE,
++ WMI_SERVICE_BCN_TXRATE_OVERRIDE, len);
++ SVCMAP(WMI_TLV_SERVICE_NAN,
++ WMI_SERVICE_NAN, len);
++ SVCMAP(WMI_TLV_SERVICE_L1SS_STAT,
++ WMI_SERVICE_L1SS_STAT, len);
++ SVCMAP(WMI_TLV_SERVICE_ESTIMATE_LINKSPEED,
++ WMI_SERVICE_ESTIMATE_LINKSPEED, len);
++ SVCMAP(WMI_TLV_SERVICE_OBSS_SCAN,
++ WMI_SERVICE_OBSS_SCAN, len);
++ SVCMAP(WMI_TLV_SERVICE_TDLS_OFFCHAN,
++ WMI_SERVICE_TDLS_OFFCHAN, len);
++ SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA,
++ WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, len);
++ SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA,
++ WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, len);
++ SVCMAP(WMI_TLV_SERVICE_IBSS_PWRSAVE,
++ WMI_SERVICE_IBSS_PWRSAVE, len);
++ SVCMAP(WMI_TLV_SERVICE_LPASS,
++ WMI_SERVICE_LPASS, len);
++ SVCMAP(WMI_TLV_SERVICE_EXTSCAN,
++ WMI_SERVICE_EXTSCAN, len);
++ SVCMAP(WMI_TLV_SERVICE_D0WOW,
++ WMI_SERVICE_D0WOW, len);
++ SVCMAP(WMI_TLV_SERVICE_HSOFFLOAD,
++ WMI_SERVICE_HSOFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_ROAM_HO_OFFLOAD,
++ WMI_SERVICE_ROAM_HO_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_RX_FULL_REORDER,
++ WMI_SERVICE_RX_FULL_REORDER, len);
++ SVCMAP(WMI_TLV_SERVICE_DHCP_OFFLOAD,
++ WMI_SERVICE_DHCP_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT,
++ WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, len);
++ SVCMAP(WMI_TLV_SERVICE_MDNS_OFFLOAD,
++ WMI_SERVICE_MDNS_OFFLOAD, len);
++ SVCMAP(WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD,
++ WMI_SERVICE_SAP_AUTH_OFFLOAD, len);
++}
++
++#undef SVCMAP
++
++struct wmi_tlv {
++ __le16 len;
++ __le16 tag;
++ u8 value[0];
++} __packed;
++
++#define WMI_TLV_MGMT_RX_NUM_RSSI 4
++
++struct wmi_tlv_mgmt_rx_ev {
++ __le32 channel;
++ __le32 snr;
++ __le32 rate;
++ __le32 phy_mode;
++ __le32 buf_len;
++ __le32 status;
++ __le32 rssi[WMI_TLV_MGMT_RX_NUM_RSSI];
++} __packed;
++
++struct wmi_tlv_abi_version {
++ __le32 abi_ver0;
++ __le32 abi_ver1;
++ __le32 abi_ver_ns0;
++ __le32 abi_ver_ns1;
++ __le32 abi_ver_ns2;
++ __le32 abi_ver_ns3;
++} __packed;
++
++enum wmi_tlv_hw_bd_id {
++ WMI_TLV_HW_BD_LEGACY = 0,
++ WMI_TLV_HW_BD_QCA6174 = 1,
++ WMI_TLV_HW_BD_QCA2582 = 2,
++};
++
++struct wmi_tlv_hw_bd_info {
++ u8 rev;
++ u8 project_id;
++ u8 custom_id;
++ u8 reference_design_id;
++} __packed;
++
++struct wmi_tlv_svc_rdy_ev {
++ __le32 fw_build_vers;
++ struct wmi_tlv_abi_version abi;
++ __le32 phy_capability;
++ __le32 max_frag_entry;
++ __le32 num_rf_chains;
++ __le32 ht_cap_info;
++ __le32 vht_cap_info;
++ __le32 vht_supp_mcs;
++ __le32 hw_min_tx_power;
++ __le32 hw_max_tx_power;
++ __le32 sys_cap_info;
++ __le32 min_pkt_size_enable;
++ __le32 max_bcn_ie_size;
++ __le32 num_mem_reqs;
++ __le32 max_num_scan_chans;
++ __le32 hw_bd_id; /* 0 means hw_bd_info is invalid */
++ struct wmi_tlv_hw_bd_info hw_bd_info[5];
++} __packed;
++
++struct wmi_tlv_rdy_ev {
++ struct wmi_tlv_abi_version abi;
++ struct wmi_mac_addr mac_addr;
++ __le32 status;
++} __packed;
++
++struct wmi_tlv_resource_config {
++ __le32 num_vdevs;
++ __le32 num_peers;
++ __le32 num_offload_peers;
++ __le32 num_offload_reorder_bufs;
++ __le32 num_peer_keys;
++ __le32 num_tids;
++ __le32 ast_skid_limit;
++ __le32 tx_chain_mask;
++ __le32 rx_chain_mask;
++ __le32 rx_timeout_pri[4];
++ __le32 rx_decap_mode;
++ __le32 scan_max_pending_reqs;
++ __le32 bmiss_offload_max_vdev;
++ __le32 roam_offload_max_vdev;
++ __le32 roam_offload_max_ap_profiles;
++ __le32 num_mcast_groups;
++ __le32 num_mcast_table_elems;
++ __le32 mcast2ucast_mode;
++ __le32 tx_dbg_log_size;
++ __le32 num_wds_entries;
++ __le32 dma_burst_size;
++ __le32 mac_aggr_delim;
++ __le32 rx_skip_defrag_timeout_dup_detection_check;
++ __le32 vow_config;
++ __le32 gtk_offload_max_vdev;
++ __le32 num_msdu_desc;
++ __le32 max_frag_entries;
++ __le32 num_tdls_vdevs;
++ __le32 num_tdls_conn_table_entries;
++ __le32 beacon_tx_offload_max_vdev;
++ __le32 num_multicast_filter_entries;
++ __le32 num_wow_filters;
++ __le32 num_keep_alive_pattern;
++ __le32 keep_alive_pattern_size;
++ __le32 max_tdls_concurrent_sleep_sta;
++ __le32 max_tdls_concurrent_buffer_sta;
++} __packed;
++
++struct wmi_tlv_init_cmd {
++ struct wmi_tlv_abi_version abi;
++ __le32 num_host_mem_chunks;
++} __packed;
++
++struct wmi_tlv_pdev_set_param_cmd {
++ __le32 pdev_id; /* not used yet */
++ __le32 param_id;
++ __le32 param_value;
++} __packed;
++
++struct wmi_tlv_pdev_set_rd_cmd {
++ __le32 pdev_id; /* not used yet */
++ __le32 regd;
++ __le32 regd_2ghz;
++ __le32 regd_5ghz;
++ __le32 conform_limit_2ghz;
++ __le32 conform_limit_5ghz;
++} __packed;
++
++struct wmi_tlv_scan_chan_list_cmd {
++ __le32 num_scan_chans;
++} __packed;
++
++struct wmi_tlv_start_scan_cmd {
++ struct wmi_start_scan_common common;
++ __le32 burst_duration_ms;
++ __le32 num_channels;
++ __le32 num_bssids;
++ __le32 num_ssids;
++ __le32 ie_len;
++ __le32 num_probes;
++} __packed;
++
++struct wmi_tlv_vdev_start_cmd {
++ __le32 vdev_id;
++ __le32 requestor_id;
++ __le32 bcn_intval;
++ __le32 dtim_period;
++ __le32 flags;
++ struct wmi_ssid ssid;
++ __le32 bcn_tx_rate;
++ __le32 bcn_tx_power;
++ __le32 num_noa_descr;
++ __le32 disable_hw_ack;
++} __packed;
++
++enum {
++ WMI_TLV_PEER_TYPE_DEFAULT = 0, /* generic / non-BSS / self-peer */
++ WMI_TLV_PEER_TYPE_BSS = 1,
++ WMI_TLV_PEER_TYPE_TDLS = 2,
++ WMI_TLV_PEER_TYPE_HOST_MAX = 127,
++ WMI_TLV_PEER_TYPE_ROAMOFFLOAD_TMP = 128,
++};
++
++struct wmi_tlv_peer_create_cmd {
++ __le32 vdev_id;
++ struct wmi_mac_addr peer_addr;
++ __le32 peer_type;
++} __packed;
++
++struct wmi_tlv_peer_assoc_cmd {
++ struct wmi_mac_addr mac_addr;
++ __le32 vdev_id;
++ __le32 new_assoc;
++ __le32 assoc_id;
++ __le32 flags;
++ __le32 caps;
++ __le32 listen_intval;
++ __le32 ht_caps;
++ __le32 max_mpdu;
++ __le32 mpdu_density;
++ __le32 rate_caps;
++ __le32 nss;
++ __le32 vht_caps;
++ __le32 phy_mode;
++ __le32 ht_info[2];
++ __le32 num_legacy_rates;
++ __le32 num_ht_rates;
++} __packed;
++
++struct wmi_tlv_pdev_suspend {
++ __le32 pdev_id; /* not used yet */
++ __le32 opt;
++} __packed;
++
++struct wmi_tlv_pdev_set_wmm_cmd {
++ __le32 pdev_id; /* not used yet */
++ __le32 dg_type; /* no idea.. */
++} __packed;
++
++struct wmi_tlv_vdev_wmm_params {
++ __le32 dummy;
++ struct wmi_wmm_params params;
++} __packed;
++
++struct wmi_tlv_vdev_set_wmm_cmd {
++ __le32 vdev_id;
++ struct wmi_tlv_vdev_wmm_params vdev_wmm_params[4];
++} __packed;
++
++struct wmi_tlv_phyerr_ev {
++ __le32 num_phyerrs;
++ __le32 tsf_l32;
++ __le32 tsf_u32;
++ __le32 buf_len;
++} __packed;
++
++enum wmi_tlv_dbglog_param {
++ WMI_TLV_DBGLOG_PARAM_LOG_LEVEL = 1,
++ WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE,
++ WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE,
++ WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE_BITMAP,
++ WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE_BITMAP,
++};
++
++enum wmi_tlv_dbglog_log_level {
++ WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE = 0,
++ WMI_TLV_DBGLOG_LOG_LEVEL_INFO,
++ WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_1,
++ WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_2,
++ WMI_TLV_DBGLOG_LOG_LEVEL_WARN,
++ WMI_TLV_DBGLOG_LOG_LEVEL_ERR,
++};
++
++#define WMI_TLV_DBGLOG_BITMAP_MAX_IDS 512
++#define WMI_TLV_DBGLOG_BITMAP_MAX_WORDS (WMI_TLV_DBGLOG_BITMAP_MAX_IDS / \
++ sizeof(__le32))
++#define WMI_TLV_DBGLOG_ALL_MODULES 0xffff
++#define WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(module_id, log_level) \
++ (((module_id << 16) & 0xffff0000) | \
++ ((log_level << 0) & 0x000000ff))
++
++struct wmi_tlv_dbglog_cmd {
++ __le32 param;
++ __le32 value;
++} __packed;
++
++struct wmi_tlv_resume_cmd {
++ __le32 reserved;
++} __packed;
++
++struct wmi_tlv_req_stats_cmd {
++ __le32 stats_id; /* wmi_stats_id */
++ __le32 vdev_id;
++ struct wmi_mac_addr peer_macaddr;
++} __packed;
++
++struct wmi_tlv_vdev_stats {
++ __le32 vdev_id;
++ __le32 beacon_snr;
++ __le32 data_snr;
++ __le32 num_tx_frames[4]; /* per-AC */
++ __le32 num_rx_frames;
++ __le32 num_tx_frames_retries[4];
++ __le32 num_tx_frames_failures[4];
++ __le32 num_rts_fail;
++ __le32 num_rts_success;
++ __le32 num_rx_err;
++ __le32 num_rx_discard;
++ __le32 num_tx_not_acked;
++ __le32 tx_rate_history[10];
++ __le32 beacon_rssi_history[10];
++} __packed;
++
++struct wmi_tlv_pktlog_enable {
++ __le32 reserved;
++ __le32 filter;
++} __packed;
++
++struct wmi_tlv_pktlog_disable {
++ __le32 reserved;
++} __packed;
++
++enum wmi_tlv_bcn_tx_status {
++ WMI_TLV_BCN_TX_STATUS_OK,
++ WMI_TLV_BCN_TX_STATUS_XRETRY,
++ WMI_TLV_BCN_TX_STATUS_DROP,
++ WMI_TLV_BCN_TX_STATUS_FILTERED,
++};
++
++struct wmi_tlv_bcn_tx_status_ev {
++ __le32 vdev_id;
++ __le32 tx_status;
++} __packed;
++
++struct wmi_tlv_bcn_prb_info {
++ __le32 caps;
++ __le32 erp;
++ u8 ies[0];
++} __packed;
++
++struct wmi_tlv_bcn_tmpl_cmd {
++ __le32 vdev_id;
++ __le32 tim_ie_offset;
++ __le32 buf_len;
++} __packed;
++
++struct wmi_tlv_prb_tmpl_cmd {
++ __le32 vdev_id;
++ __le32 buf_len;
++} __packed;
++
++struct wmi_tlv_p2p_go_bcn_ie {
++ __le32 vdev_id;
++ __le32 ie_len;
++} __packed;
++
++enum wmi_tlv_diag_item_type {
++ WMI_TLV_DIAG_ITEM_TYPE_FW_EVENT,
++ WMI_TLV_DIAG_ITEM_TYPE_FW_LOG,
++ WMI_TLV_DIAG_ITEM_TYPE_FW_DEBUG_MSG,
++};
++
++struct wmi_tlv_diag_item {
++ u8 type;
++ u8 reserved;
++ __le16 len;
++ __le32 timestamp;
++ __le32 code;
++ u8 payload[0];
++} __packed;
++
++struct wmi_tlv_diag_data_ev {
++ __le32 num_items;
++} __packed;
++
++struct wmi_tlv_sta_keepalive_cmd {
++ __le32 vdev_id;
++ __le32 enabled;
++ __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */
++ __le32 interval; /* in seconds */
++} __packed;
++
++struct wmi_tlv_stats_ev {
++ __le32 stats_id; /* WMI_STAT_ */
++ __le32 num_pdev_stats;
++ __le32 num_vdev_stats;
++ __le32 num_peer_stats;
++ __le32 num_bcnflt_stats;
++ __le32 num_chan_stats;
++} __packed;
++
++void ath10k_wmi_tlv_attach(struct ath10k *ar);
++
++#endif
+--- a/backport-include/linux/etherdevice.h
++++ b/backport-include/linux/etherdevice.h
+@@ -148,6 +148,29 @@ static inline bool ether_addr_equal_unal
+ return memcmp(addr1, addr2, ETH_ALEN) == 0;
+ #endif
+ }
++
++/**
++ * ether_addr_copy - Copy an Ethernet address
++ * @dst: Pointer to a six-byte array Ethernet address destination
++ * @src: Pointer to a six-byte array Ethernet address source
++ *
++ * Please note: dst & src must both be aligned to u16.
++ */
++#define ether_addr_copy LINUX_BACKPORT(ether_addr_copy)
++static inline void ether_addr_copy(u8 *dst, const u8 *src)
++{
++#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
++ *(u32 *)dst = *(const u32 *)src;
++ *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
++#else
++ u16 *a = (u16 *)dst;
++ const u16 *b = (const u16 *)src;
++
++ a[0] = b[0];
++ a[1] = b[1];
++ a[2] = b[2];
++#endif
++}
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) */
+
+ #endif /* _BACKPORT_LINUX_ETHERDEVICE_H */
+--- a/drivers/net/wireless/ath/spectral_common.h
++++ b/drivers/net/wireless/ath/spectral_common.h
+@@ -20,6 +20,11 @@
+ #define SPECTRAL_HT20_NUM_BINS 56
+ #define SPECTRAL_HT20_40_NUM_BINS 128
+
++/* TODO: could possibly be 512, but no samples this large
++ * could be acquired so far.
++ */
++#define SPECTRAL_ATH10K_MAX_NUM_BINS 256
++
+ /* FFT sample format given to userspace via debugfs.
+ *
+ * Please keep the type/length at the front position and change
+@@ -31,6 +36,7 @@
+ enum ath_fft_sample_type {
+ ATH_FFT_SAMPLE_HT20 = 1,
+ ATH_FFT_SAMPLE_HT20_40,
++ ATH_FFT_SAMPLE_ATH10K,
+ };
+
+ struct fft_sample_tlv {
+@@ -85,4 +91,23 @@ struct fft_sample_ht20_40 {
+ u8 data[SPECTRAL_HT20_40_NUM_BINS];
+ } __packed;
+
++struct fft_sample_ath10k {
++ struct fft_sample_tlv tlv;
++ u8 chan_width_mhz;
++ __be16 freq1;
++ __be16 freq2;
++ __be16 noise;
++ __be16 max_magnitude;
++ __be16 total_gain_db;
++ __be16 base_pwr_db;
++ __be64 tsf;
++ s8 max_index;
++ u8 rssi;
++ u8 relpwr_db;
++ u8 avgpwr_db;
++ u8 max_exp;
++
++ u8 data[0];
++} __packed;
++
+ #endif /* SPECTRAL_COMMON_H */
+--- a/compat/backport-3.13.c
++++ b/compat/backport-3.13.c
+@@ -12,6 +12,10 @@
+ #include <linux/version.h>
+ #include <linux/kernel.h>
+ #include <net/genetlink.h>
++#include <linux/delay.h>
++#include <linux/pci.h>
++#include <linux/device.h>
++#include <linux/hwmon.h>
+
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
+ #ifdef CPTCFG_REGULATOR
+@@ -200,3 +204,103 @@ bool __net_get_random_once(void *buf, in
+ }
+ EXPORT_SYMBOL_GPL(__net_get_random_once);
+ #endif /* __BACKPORT_NET_GET_RANDOM_ONCE */
++
++#ifdef CPTCFG_PCI
++#define pci_bus_read_dev_vendor_id LINUX_BACKPORT(pci_bus_read_dev_vendor_id)
++static bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
++ int crs_timeout)
++{
++ int delay = 1;
++
++ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
++ return false;
++
++ /* some broken boards return 0 or ~0 if a slot is empty: */
++ if (*l == 0xffffffff || *l == 0x00000000 ||
++ *l == 0x0000ffff || *l == 0xffff0000)
++ return false;
++
++ /*
++ * Configuration Request Retry Status. Some root ports return the
++ * actual device ID instead of the synthetic ID (0xFFFF) required
++ * by the PCIe spec. Ignore the device ID and only check for
++ * (vendor id == 1).
++ */
++ while ((*l & 0xffff) == 0x0001) {
++ if (!crs_timeout)
++ return false;
++
++ msleep(delay);
++ delay *= 2;
++ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
++ return false;
++ /* Card hasn't responded in 60 seconds? Must be stuck. */
++ if (delay > crs_timeout) {
++ printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
++ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
++ PCI_FUNC(devfn));
++ return false;
++ }
++ }
++
++ return true;
++}
++
++bool pci_device_is_present(struct pci_dev *pdev)
++{
++ u32 v;
++
++ return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
++}
++EXPORT_SYMBOL_GPL(pci_device_is_present);
++#endif /* CPTCFG_PCI */
++
++#ifdef CPTCFG_HWMON
++struct device*
++hwmon_device_register_with_groups(struct device *dev, const char *name,
++ void *drvdata,
++ const struct attribute_group **groups)
++{
++ struct device *hwdev;
++
++ hwdev = hwmon_device_register(dev);
++ hwdev->groups = groups;
++ dev_set_drvdata(hwdev, drvdata);
++ return hwdev;
++}
++
++static void devm_hwmon_release(struct device *dev, void *res)
++{
++ struct device *hwdev = *(struct device **)res;
++
++ hwmon_device_unregister(hwdev);
++}
++
++struct device *
++devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
++ void *drvdata,
++ const struct attribute_group **groups)
++{
++ struct device **ptr, *hwdev;
++
++ if (!dev)
++ return ERR_PTR(-EINVAL);
++
++ ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
++ if (!ptr)
++ return ERR_PTR(-ENOMEM);
++
++ hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups);
++ if (IS_ERR(hwdev))
++ goto error;
++
++ *ptr = hwdev;
++ devres_add(dev, ptr);
++ return hwdev;
++
++error:
++ devres_free(ptr);
++ return hwdev;
++}
++EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
++#endif
+--- /dev/null
++++ b/backport-include/linux/hwmon.h
+@@ -0,0 +1,34 @@
++#ifndef __BACKPORT_LINUX_HWMON_H
++#define __BACKPORT_LINUX_HWMON_H
++#include_next <linux/hwmon.h>
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
++/*
++ * Backports
++ *
++ * commit bab2243ce1897865e31ea6d59b0478391f51812b
++ * Author: Guenter Roeck <linux@roeck-us.net>
++ * Date: Sat Jul 6 13:57:23 2013 -0700
++ *
++ * hwmon: Introduce hwmon_device_register_with_groups
++ *
++ * hwmon_device_register_with_groups() lets callers register a hwmon device
++ * together with all sysfs attributes in a single call.
++ *
++ * When using hwmon_device_register_with_groups(), hwmon attributes are attached
++ * to the hwmon device directly and no longer with its parent device.
++ *
++ * Signed-off-by: Guenter Roeck <linux@roeck-us.net>
++ */
++struct device *
++hwmon_device_register_with_groups(struct device *dev, const char *name,
++ void *drvdata,
++ const struct attribute_group **groups);
++struct device *
++devm_hwmon_device_register_with_groups(struct device *dev, const char *name,
++ void *drvdata,
++ const struct attribute_group **groups);
++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) */
++
++#endif /* __BACKPORT_LINUX_HWMON_H */
diff --git a/package/kernel/mac80211/patches/920-ath10k_allow_fallback_to_board_bin_on_empty_otp_stream.patch b/package/kernel/mac80211/patches/920-ath10k_allow_fallback_to_board_bin_on_empty_otp_stream.patch
index 6a5c766..6a3d2a4 100644
--- a/package/kernel/mac80211/patches/920-ath10k_allow_fallback_to_board_bin_on_empty_otp_stream.patch
+++ b/package/kernel/mac80211/patches/920-ath10k_allow_fallback_to_board_bin_on_empty_otp_stream.patch
@@ -1,14 +1,14 @@
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
-@@ -277,7 +277,10 @@ static int ath10k_download_and_run_otp(s
+@@ -387,7 +387,10 @@ static int ath10k_download_and_run_otp(s
- ath10k_dbg(ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
-- if (result != 0) {
+- if (!skip_otp && result != 0) {
+ if (result == 2) {
-+ ath10k_warn("otp stream is empty, using board.bin contents");
++ ath10k_warn(ar, "otp stream is empty, using board.bin contents");
+ return 0;
-+ } else if (result != 0) {
- ath10k_err("otp calibration failed: %d", result);
++ } else if (!skip_otp && result != 0) {
+ ath10k_err(ar, "otp calibration failed: %d", result);
return -EINVAL;
}
diff --git a/package/kernel/mac80211/patches/921-ath10k_init_devices_synchronously.patch b/package/kernel/mac80211/patches/921-ath10k_init_devices_synchronously.patch
new file mode 100644
index 0000000..c664faa
--- /dev/null
+++ b/package/kernel/mac80211/patches/921-ath10k_init_devices_synchronously.patch
@@ -0,0 +1,33 @@
+From: Sven Eckelmann <sven@open-mesh.com>
+Date: Tue, 18 Nov 2014 12:29:28 +0100
+Subject: [PATCH] ath10k: Don't initialize devices asynchronously
+
+OpenWrt requires all PHYs to be initialized to create the configuration files
+during bootup. ath10k violates this because it delays the creation of the PHY
+to a not well defined point in the future.
+
+Forcing the work to be done immediately works around this problem but may also
+delay the boot when firmware images cannot be found.
+
+Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
+---
+
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -1321,6 +1321,16 @@ int ath10k_core_register(struct ath10k *
+ ar->chip_id = chip_id;
+ queue_work(ar->workqueue, &ar->register_work);
+
++ /* OpenWrt requires all PHYs to be initialized to create the
++ * configuration files during bootup. ath10k violates this
++ * because it delays the creation of the PHY to a not well defined
++ * point in the future.
++ *
++ * Forcing the work to be done immediately works around this problem
++ * but may also delay the boot when firmware images cannot be found.
++ */
++ flush_workqueue(ar->workqueue);
++
+ return 0;
+ }
+ EXPORT_SYMBOL(ath10k_core_register);
diff --git a/package/kernel/mac80211/patches/930-ath10k_add_tpt_led_trigger.patch b/package/kernel/mac80211/patches/930-ath10k_add_tpt_led_trigger.patch
new file mode 100644
index 0000000..54174b1
--- /dev/null
+++ b/package/kernel/mac80211/patches/930-ath10k_add_tpt_led_trigger.patch
@@ -0,0 +1,37 @@
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -5405,6 +5405,21 @@ struct ath10k_vif *ath10k_get_arvif(stru
+ return arvif_iter.arvif;
+ }
+
++#ifdef CPTCFG_MAC80211_LEDS
++static const struct ieee80211_tpt_blink ath10k_tpt_blink[] = {
++ { .throughput = 0 * 1024, .blink_time = 334 },
++ { .throughput = 1 * 1024, .blink_time = 260 },
++ { .throughput = 2 * 1024, .blink_time = 220 },
++ { .throughput = 5 * 1024, .blink_time = 190 },
++ { .throughput = 10 * 1024, .blink_time = 170 },
++ { .throughput = 25 * 1024, .blink_time = 150 },
++ { .throughput = 54 * 1024, .blink_time = 130 },
++ { .throughput = 120 * 1024, .blink_time = 110 },
++ { .throughput = 265 * 1024, .blink_time = 80 },
++ { .throughput = 586 * 1024, .blink_time = 50 },
++};
++#endif
++
+ int ath10k_mac_register(struct ath10k *ar)
+ {
+ struct ieee80211_supported_band *band;
+@@ -5567,6 +5582,12 @@ int ath10k_mac_register(struct ath10k *a
+ goto err_free;
+ }
+
++#if CPTCFG_MAC80211_LEDS
++ ieee80211_create_tpt_led_trigger(ar->hw,
++ IEEE80211_TPT_LEDTRIG_FL_RADIO, ath10k_tpt_blink,
++ ARRAY_SIZE(ath10k_tpt_blink));
++#endif
++
+ ret = ieee80211_register_hw(ar->hw);
+ if (ret) {
+ ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
diff --git a/package/kernel/mac80211/patches/950-ath10k_AP_IBSS.patch b/package/kernel/mac80211/patches/950-ath10k_AP_IBSS.patch
new file mode 100644
index 0000000..0011b5d
--- /dev/null
+++ b/package/kernel/mac80211/patches/950-ath10k_AP_IBSS.patch
@@ -0,0 +1,32 @@
+--- a/drivers/net/wireless/ath/ath10k/mac.c
++++ b/drivers/net/wireless/ath/ath10k/mac.c
+@@ -5253,6 +5253,10 @@ static const struct ieee80211_iface_limi
+ .max = 7,
+ .types = BIT(NL80211_IFTYPE_AP)
+ },
++ {
++ .max = 1,
++ .types = BIT(NL80211_IFTYPE_ADHOC)
++ },
+ };
+
+ static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
+@@ -5260,6 +5264,10 @@ static const struct ieee80211_iface_limi
+ .max = 8,
+ .types = BIT(NL80211_IFTYPE_AP)
+ },
++ {
++ .max = 1,
++ .types = BIT(NL80211_IFTYPE_ADHOC)
++ },
+ };
+
+ static const struct ieee80211_iface_combination ath10k_if_comb[] = {
+@@ -5555,6 +5563,7 @@ int ath10k_mac_register(struct ath10k *a
+ ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
+ ar->hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(ath10k_10x_if_comb);
++ ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
+ break;
+ case ATH10K_FW_WMI_OP_VERSION_UNSET:
+ case ATH10K_FW_WMI_OP_VERSION_MAX: