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

Commit fbbae71d authored by Kalle Valo's avatar Kalle Valo
Browse files
ath.git patches for 4.21. 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
parents 19de2fa8 84758d4d
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