Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e69fbf31 authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'wireless-drivers-next-for-davem-2018-12-20' of...

Merge tag 'wireless-drivers-next-for-davem-2018-12-20' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next



Kalle Valo says:

====================
wireless-drivers-next patches for 4.21

Last set of patches for 4.21. mt76 is still in very active development
and having some refactoring as well as new features. But also other
drivers got few new features and fixes.

Major changes:

ath10k

* add amsdu support for QCA6174 monitor mode

* report tx rate using the new ieee80211_tx_rate_update() API

* wcn3990 support is not experimental anymore

iwlwifi

* support for FW version 43 for 9000 and 22000 series

brcmfmac

* add support for CYW43012 SDIO chipset

* add the raw 4354 PCIe device ID for unprogrammed Cypress boards

mwifiex

* add NL80211_STA_INFO_RX_BITRATE support

mt76

* use the same firmware for mt76x2e and mt76x2u

* mt76x0e survey support

* more unification between mt76x2 and mt76x0

* mt76x0e AP mode support

* mt76x0e DFS support

* rework and fix tx status handling for mt76x0 and mt76x2
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents aa9d6e0f fbbae71d
Loading
Loading
Loading
Loading
+49 −25
Original line number Diff line number Diff line
@@ -21,10 +21,22 @@ can be provided per device.

SNOC based devices (i.e. wcn3990) uses compatible string "qcom,wcn3990-wifi".

Optional properties:
- reg: Address and length of the register set for the device.
- reg-names: Must include the list of following reg names,
	     "membase"
- interrupts: reference to the list of 17 interrupt numbers for "qcom,ipq4019-wifi"
	      compatible target.
	      reference to the list of 12 interrupt numbers for "qcom,wcn3990-wifi"
	      compatible target.
	      Must contain interrupt-names property per entry for
	      "qcom,ath10k", "qcom,ipq4019-wifi" compatible targets.

- interrupt-names: Must include the entries for MSI interrupt
		   names ("msi0" to "msi15") and legacy interrupt
		   name ("legacy") for "qcom,ath10k", "qcom,ipq4019-wifi"
		   compatible targets.

Optional properties:
- resets: Must contain an entry for each entry in reset-names.
          See ../reset/reseti.txt for details.
- reset-names: Must include the list of following reset names,
@@ -37,12 +49,9 @@ Optional properties:
- clocks: List of clock specifiers, must contain an entry for each required
          entry in clock-names.
- clock-names: Should contain the clock names "wifi_wcss_cmd", "wifi_wcss_ref",
               "wifi_wcss_rtc".
- interrupts: List of interrupt lines. Must contain an entry
	      for each entry in the interrupt-names property.
- interrupt-names: Must include the entries for MSI interrupt
		   names ("msi0" to "msi15") and legacy interrupt
		   name ("legacy"),
	       "wifi_wcss_rtc" for "qcom,ipq4019-wifi" compatible target and
	       "cxo_ref_clk_pin" for "qcom,wcn3990-wifi"
	       compatible target.
- qcom,msi_addr: MSI interrupt address.
- qcom,msi_base: Base value to add before writing MSI data into
		MSI address register.
@@ -55,14 +64,25 @@ Optional properties:
- qcom,ath10k-pre-calibration-data : pre calibration data as an array,
				     the length can vary between hw versions.
- <supply-name>-supply: handle to the regulator device tree node
			   optional "supply-name" is "vdd-0.8-cx-mx".
			   optional "supply-name" are "vdd-0.8-cx-mx",
			   "vdd-1.8-xo", "vdd-1.3-rfa" and "vdd-3.3-ch0".
- memory-region:
	Usage: optional
	Value type: <phandle>
	Definition: reference to the reserved-memory for the msa region
		    used by the wifi firmware running in Q6.
- iommus:
	Usage: optional
	Value type: <prop-encoded-array>
	Definition: A list of phandle and IOMMU specifier pairs.
- ext-fem-name:
	Usage: Optional
	Value type: string
	Definition: Name of external front end module used. Some valid FEM names
		    for example: "microsemi-lx5586", "sky85703-11"
		    and "sky85803" etc.

Example (to supply the calibration data alone):
Example (to supply PCI based wifi block details):

In this example, the node is defined as child node of the PCI controller.

@@ -74,10 +94,10 @@ pci {
		#address-cells = <3>;
		device_type = "pci";

		ath10k@0,0 {
		wifi@0,0 {
			reg = <0 0 0 0 0>;
			device_type = "pci";
			qcom,ath10k-calibration-data = [ 01 02 03 ... ];
			ext-fem-name = "microsemi-lx5586";
		};
	};
};
@@ -138,21 +158,25 @@ wifi@18000000 {
		compatible = "qcom,wcn3990-wifi";
		reg = <0x18800000 0x800000>;
		reg-names = "membase";
		clocks = <&clock_gcc clk_aggre2_noc_clk>;
		clock-names = "smmu_aggre2_noc_clk"
		clocks = <&clock_gcc clk_rf_clk2_pin>;
		clock-names = "cxo_ref_clk_pin";
		interrupts =
			   <0 130 0 /* CE0 */ >,
			   <0 131 0 /* CE1 */ >,
			   <0 132 0 /* CE2 */ >,
			   <0 133 0 /* CE3 */ >,
			   <0 134 0 /* CE4 */ >,
			   <0 135 0 /* CE5 */ >,
			   <0 136 0 /* CE6 */ >,
			   <0 137 0 /* CE7 */ >,
			   <0 138 0 /* CE8 */ >,
			   <0 139 0 /* CE9 */ >,
			   <0 140 0 /* CE10 */ >,
			   <0 141 0 /* CE11 */ >;
			<GIC_SPI 414 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 415 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 420 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 423 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>,
			<GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH>;
		vdd-0.8-cx-mx-supply = <&pm8998_l5>;
		vdd-1.8-xo-supply = <&vreg_l7a_1p8>;
		vdd-1.3-rfa-supply = <&vreg_l17a_1p3>;
		vdd-3.3-ch0-supply = <&vreg_l25a_3p3>;
		memory-region = <&wifi_msa_mem>;
		iommus = <&apps_smmu 0x0040 0x1>;
};
+1 −2
Original line number Diff line number Diff line
@@ -47,8 +47,7 @@ config ATH10K_SNOC
	select QCOM_QMI_HELPERS
	---help---
	  This module adds support for integrated WCN3990 chip connected
	  to system NOC(SNOC). Currently work in progress and will not
	  fully work.
	  to system NOC(SNOC).

config ATH10K_DEBUG
	bool "Atheros ath10k debugging"
+1 −0
Original line number Diff line number Diff line
@@ -494,6 +494,7 @@ struct ath10k_sta {
	u32 smps;
	u16 peer_id;
	struct rate_info txrate;
	struct ieee80211_tx_info tx_info;

	struct work_struct update_wk;
	u64 rx_duration;
+1 −1
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid
	spin_lock_bh(&ar->data_lock);

	peer = ath10k_peer_find_by_id(ar, peer_id);
	if (!peer)
	if (!peer || !peer->sta)
		goto out;

	arsta = (struct ath10k_sta *)peer->sta->drv_priv;
+287 −13
Original line number Diff line number Diff line
@@ -469,6 +469,166 @@ static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
	return msdu;
}

static inline void ath10k_htt_append_frag_list(struct sk_buff *skb_head,
					       struct sk_buff *frag_list,
					       unsigned int frag_len)
{
	skb_shinfo(skb_head)->frag_list = frag_list;
	skb_head->data_len = frag_len;
	skb_head->len += skb_head->data_len;
}

static int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt,
					     struct sk_buff *msdu,
					     struct htt_rx_in_ord_msdu_desc **msdu_desc)
{
	struct ath10k *ar = htt->ar;
	u32 paddr;
	struct sk_buff *frag_buf;
	struct sk_buff *prev_frag_buf;
	u8 last_frag;
	struct htt_rx_in_ord_msdu_desc *ind_desc = *msdu_desc;
	struct htt_rx_desc *rxd;
	int amsdu_len = __le16_to_cpu(ind_desc->msdu_len);

	rxd = (void *)msdu->data;
	trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));

	skb_put(msdu, sizeof(struct htt_rx_desc));
	skb_pull(msdu, sizeof(struct htt_rx_desc));
	skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE));
	amsdu_len -= msdu->len;

	last_frag = ind_desc->reserved;
	if (last_frag) {
		if (amsdu_len) {
			ath10k_warn(ar, "invalid amsdu len %u, left %d",
				    __le16_to_cpu(ind_desc->msdu_len),
				    amsdu_len);
		}
		return 0;
	}

	ind_desc++;
	paddr = __le32_to_cpu(ind_desc->msdu_paddr);
	frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
	if (!frag_buf) {
		ath10k_warn(ar, "failed to pop frag-1 paddr: 0x%x", paddr);
		return -ENOENT;
	}

	skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
	ath10k_htt_append_frag_list(msdu, frag_buf, amsdu_len);

	amsdu_len -= frag_buf->len;
	prev_frag_buf = frag_buf;
	last_frag = ind_desc->reserved;
	while (!last_frag) {
		ind_desc++;
		paddr = __le32_to_cpu(ind_desc->msdu_paddr);
		frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
		if (!frag_buf) {
			ath10k_warn(ar, "failed to pop frag-n paddr: 0x%x",
				    paddr);
			prev_frag_buf->next = NULL;
			return -ENOENT;
		}

		skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
		last_frag = ind_desc->reserved;
		amsdu_len -= frag_buf->len;

		prev_frag_buf->next = frag_buf;
		prev_frag_buf = frag_buf;
	}

	if (amsdu_len) {
		ath10k_warn(ar, "invalid amsdu len %u, left %d",
			    __le16_to_cpu(ind_desc->msdu_len), amsdu_len);
	}

	*msdu_desc = ind_desc;

	prev_frag_buf->next = NULL;
	return 0;
}

static int
ath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt,
				  struct sk_buff *msdu,
				  struct htt_rx_in_ord_msdu_desc_ext **msdu_desc)
{
	struct ath10k *ar = htt->ar;
	u64 paddr;
	struct sk_buff *frag_buf;
	struct sk_buff *prev_frag_buf;
	u8 last_frag;
	struct htt_rx_in_ord_msdu_desc_ext *ind_desc = *msdu_desc;
	struct htt_rx_desc *rxd;
	int amsdu_len = __le16_to_cpu(ind_desc->msdu_len);

	rxd = (void *)msdu->data;
	trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd));

	skb_put(msdu, sizeof(struct htt_rx_desc));
	skb_pull(msdu, sizeof(struct htt_rx_desc));
	skb_put(msdu, min(amsdu_len, HTT_RX_MSDU_SIZE));
	amsdu_len -= msdu->len;

	last_frag = ind_desc->reserved;
	if (last_frag) {
		if (amsdu_len) {
			ath10k_warn(ar, "invalid amsdu len %u, left %d",
				    __le16_to_cpu(ind_desc->msdu_len),
				    amsdu_len);
		}
		return 0;
	}

	ind_desc++;
	paddr = __le64_to_cpu(ind_desc->msdu_paddr);
	frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
	if (!frag_buf) {
		ath10k_warn(ar, "failed to pop frag-1 paddr: 0x%llx", paddr);
		return -ENOENT;
	}

	skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
	ath10k_htt_append_frag_list(msdu, frag_buf, amsdu_len);

	amsdu_len -= frag_buf->len;
	prev_frag_buf = frag_buf;
	last_frag = ind_desc->reserved;
	while (!last_frag) {
		ind_desc++;
		paddr = __le64_to_cpu(ind_desc->msdu_paddr);
		frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
		if (!frag_buf) {
			ath10k_warn(ar, "failed to pop frag-n paddr: 0x%llx",
				    paddr);
			prev_frag_buf->next = NULL;
			return -ENOENT;
		}

		skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
		last_frag = ind_desc->reserved;
		amsdu_len -= frag_buf->len;

		prev_frag_buf->next = frag_buf;
		prev_frag_buf = frag_buf;
	}

	if (amsdu_len) {
		ath10k_warn(ar, "invalid amsdu len %u, left %d",
			    __le16_to_cpu(ind_desc->msdu_len), amsdu_len);
	}

	*msdu_desc = ind_desc;

	prev_frag_buf->next = NULL;
	return 0;
}

static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
					  struct htt_rx_in_ord_ind *ev,
					  struct sk_buff_head *list)
@@ -477,7 +637,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
	struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs32;
	struct htt_rx_desc *rxd;
	struct sk_buff *msdu;
	int msdu_count;
	int msdu_count, ret;
	bool is_offload;
	u32 paddr;

@@ -495,6 +655,18 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
			return -ENOENT;
		}

		if (!is_offload && ar->monitor_arvif) {
			ret = ath10k_htt_rx_handle_amsdu_mon_32(htt, msdu,
								&msdu_desc);
			if (ret) {
				__skb_queue_purge(list);
				return ret;
			}
			__skb_queue_tail(list, msdu);
			msdu_desc++;
			continue;
		}

		__skb_queue_tail(list, msdu);

		if (!is_offload) {
@@ -527,7 +699,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
	struct htt_rx_in_ord_msdu_desc_ext *msdu_desc = ev->msdu_descs64;
	struct htt_rx_desc *rxd;
	struct sk_buff *msdu;
	int msdu_count;
	int msdu_count, ret;
	bool is_offload;
	u64 paddr;

@@ -544,6 +716,18 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
			return -ENOENT;
		}

		if (!is_offload && ar->monitor_arvif) {
			ret = ath10k_htt_rx_handle_amsdu_mon_64(htt, msdu,
								&msdu_desc);
			if (ret) {
				__skb_queue_purge(list);
				return ret;
			}
			__skb_queue_tail(list, msdu);
			msdu_desc++;
			continue;
		}

		__skb_queue_tail(list, msdu);

		if (!is_offload) {
@@ -1159,7 +1343,8 @@ 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)
					bool is_decrypted,
					const u8 first_hdr[64])
{
	struct ieee80211_hdr *hdr;
	struct htt_rx_desc *rxd;
@@ -1167,6 +1352,9 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
	size_t crypto_len;
	bool is_first;
	bool is_last;
	bool msdu_limit_err;
	int bytes_aligned = ar->hw_params.decap_align_bytes;
	u8 *qos;

	rxd = (void *)msdu->data - sizeof(*rxd);
	is_first = !!(rxd->msdu_end.common.info0 &
@@ -1184,16 +1372,45 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
	 * [FCS] <-- at end, needs to be trimmed
	 */

	/* Some hardwares(QCA99x0 variants) limit number of msdus in a-msdu when
	 * deaggregate, so that unwanted MSDU-deaggregation is avoided for
	 * error packets. If limit exceeds, hw sends all remaining MSDUs as
	 * a single last MSDU with this msdu limit error set.
	 */
	msdu_limit_err = ath10k_rx_desc_msdu_limit_error(&ar->hw_params, rxd);

	/* If MSDU limit error happens, then don't warn on, the partial raw MSDU
	 * without first MSDU is expected in that case, and handled later here.
	 */
	/* This probably shouldn't happen but warn just in case */
	if (WARN_ON_ONCE(!is_first))
	if (WARN_ON_ONCE(!is_first && !msdu_limit_err))
		return;

	/* This probably shouldn't happen but warn just in case */
	if (WARN_ON_ONCE(!(is_first && is_last)))
	if (WARN_ON_ONCE(!(is_first && is_last) && !msdu_limit_err))
		return;

	skb_trim(msdu, msdu->len - FCS_LEN);

	/* Push original 80211 header */
	if (unlikely(msdu_limit_err)) {
		hdr = (struct ieee80211_hdr *)first_hdr;
		hdr_len = ieee80211_hdrlen(hdr->frame_control);
		crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);

		if (ieee80211_is_data_qos(hdr->frame_control)) {
			qos = ieee80211_get_qos_ctl(hdr);
			qos[0] |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
		}

		if (crypto_len)
			memcpy(skb_push(msdu, crypto_len),
			       (void *)hdr + round_up(hdr_len, bytes_aligned),
			       crypto_len);

		memcpy(skb_push(msdu, hdr_len), hdr, hdr_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 is
	 * necessary for software based decryption.
@@ -1467,7 +1684,7 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
	switch (decap) {
	case RX_MSDU_DECAP_RAW:
		ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
					    is_decrypted);
					    is_decrypted, first_hdr);
		break;
	case RX_MSDU_DECAP_NATIVE_WIFI:
		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
@@ -2627,7 +2844,7 @@ void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
		dev_kfree_skb_any(skb);
}

static inline int ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate)
static inline s8 ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate)
{
	static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12,
					  18, 24, 36, 48, 54};
@@ -2646,7 +2863,7 @@ static void
ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
				    struct ath10k_sta *arsta,
				    struct ath10k_per_peer_tx_stats *pstats,
				    u8 legacy_rate_idx)
				    s8 legacy_rate_idx)
{
	struct rate_info *txrate = &arsta->txrate;
	struct ath10k_htt_tx_stats *tx_stats;
@@ -2766,8 +2983,10 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
				struct ath10k_per_peer_tx_stats *peer_stats)
{
	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
	struct ieee80211_chanctx_conf *conf = NULL;
	u8 rate = 0, sgi;
	s8 rate_idx = 0;
	bool skip_auto_rate;
	struct rate_info txrate;

	lockdep_assert_held(&ar->data_lock);
@@ -2777,6 +2996,13 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
	txrate.nss = ATH10K_HW_NSS(peer_stats->ratecode);
	txrate.mcs = ATH10K_HW_MCS_RATE(peer_stats->ratecode);
	sgi = ATH10K_HW_GI(peer_stats->flags);
	skip_auto_rate = ATH10K_FW_SKIPPED_RATE_CTRL(peer_stats->flags);

	/* Firmware's rate control skips broadcast/management frames,
	 * if host has configure fixed rates and in some other special cases.
	 */
	if (skip_auto_rate)
		return;

	if (txrate.flags == WMI_RATE_PREAMBLE_VHT && txrate.mcs > 9) {
		ath10k_warn(ar, "Invalid VHT mcs %hhd peer stats",  txrate.mcs);
@@ -2791,7 +3017,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
	}

	memset(&arsta->txrate, 0, sizeof(arsta->txrate));

	memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status));
	if (txrate.flags == WMI_RATE_PREAMBLE_CCK ||
	    txrate.flags == WMI_RATE_PREAMBLE_OFDM) {
		rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode);
@@ -2810,11 +3036,59 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar,
		arsta->txrate.mcs = txrate.mcs;
	}

	switch (txrate.flags) {
	case WMI_RATE_PREAMBLE_OFDM:
		if (arsta->arvif && arsta->arvif->vif)
			conf = rcu_dereference(arsta->arvif->vif->chanctx_conf);
		if (conf && conf->def.chan->band == NL80211_BAND_5GHZ)
			arsta->tx_info.status.rates[0].idx = rate_idx - 4;
		break;
	case WMI_RATE_PREAMBLE_CCK:
		arsta->tx_info.status.rates[0].idx = rate_idx;
		if (sgi)
		arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
			arsta->tx_info.status.rates[0].flags |=
				(IEEE80211_TX_RC_USE_SHORT_PREAMBLE |
				 IEEE80211_TX_RC_SHORT_GI);
		break;
	case WMI_RATE_PREAMBLE_HT:
		arsta->tx_info.status.rates[0].idx =
				txrate.mcs + ((txrate.nss - 1) * 8);
		if (sgi)
			arsta->tx_info.status.rates[0].flags |=
					IEEE80211_TX_RC_SHORT_GI;
		arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_MCS;
		break;
	case WMI_RATE_PREAMBLE_VHT:
		ieee80211_rate_set_vht(&arsta->tx_info.status.rates[0],
				       txrate.mcs, txrate.nss);
		if (sgi)
			arsta->tx_info.status.rates[0].flags |=
						IEEE80211_TX_RC_SHORT_GI;
		arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_VHT_MCS;
		break;
	}

	arsta->txrate.nss = txrate.nss;
	arsta->txrate.bw = ath10k_bw_to_mac80211_bw(txrate.bw);
	if (sgi)
		arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;

	switch (arsta->txrate.bw) {
	case RATE_INFO_BW_40:
		arsta->tx_info.status.rates[0].flags |=
				IEEE80211_TX_RC_40_MHZ_WIDTH;
		break;
	case RATE_INFO_BW_80:
		arsta->tx_info.status.rates[0].flags |=
				IEEE80211_TX_RC_80_MHZ_WIDTH;
		break;
	}

	if (peer_stats->succ_pkts) {
		arsta->tx_info.flags = IEEE80211_TX_STAT_ACK;
		arsta->tx_info.status.rates[0].count = 1;
		ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info);
	}

	if (ath10k_debug_is_extd_tx_stats_enabled(ar))
		ath10k_accumulate_per_peer_tx_stats(ar, arsta, peer_stats,
@@ -2847,7 +3121,7 @@ static void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
	rcu_read_lock();
	spin_lock_bh(&ar->data_lock);
	peer = ath10k_peer_find_by_id(ar, peer_id);
	if (!peer) {
	if (!peer || !peer->sta) {
		ath10k_warn(ar, "Invalid peer id %d peer stats buffer\n",
			    peer_id);
		goto out;
@@ -2900,7 +3174,7 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
	rcu_read_lock();
	spin_lock_bh(&ar->data_lock);
	peer = ath10k_peer_find_by_id(ar, peer_id);
	if (!peer) {
	if (!peer || !peer->sta) {
		ath10k_warn(ar, "Invalid peer id %d in peer stats buffer\n",
			    peer_id);
		goto out;
Loading