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

Commit abd5f008 authored by Kalle Valo's avatar Kalle Valo
Browse files
ath.git patches for 4.17. Major changes:

wil6210

* support multiple virtual interfaces
parents 996bfed1 6767b302
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -2439,7 +2440,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar)

	ret = ath10k_hif_power_up(ar);
	if (ret) {
		ath10k_err(ar, "could not start pci hif (%d)\n", ret);
		ath10k_err(ar, "could not power on hif bus (%d)\n", ret);
		return ret;
	}

+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -324,6 +325,27 @@ struct ath10k_tpc_stats {
	struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG];
};

struct ath10k_tpc_table_final {
	u32 pream_idx[WMI_TPC_FINAL_RATE_MAX];
	u8 rate_code[WMI_TPC_FINAL_RATE_MAX];
	char tpc_value[WMI_TPC_FINAL_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE];
};

struct ath10k_tpc_stats_final {
	u32 reg_domain;
	u32 chan_freq;
	u32 phy_mode;
	u32 twice_antenna_reduction;
	u32 twice_max_rd_power;
	s32 twice_antenna_gain;
	u32 power_limit;
	u32 num_tx_chain;
	u32 ctl;
	u32 rate_max;
	u8 flag[WMI_TPC_FLAG];
	struct ath10k_tpc_table_final tpc_table_final[WMI_TPC_FLAG];
};

struct ath10k_dfs_stats {
	u32 phy_errors;
	u32 pulses_total;
@@ -354,6 +376,45 @@ struct ath10k_txq {
	unsigned long num_push_allowed;
};

enum ath10k_pkt_rx_err {
	ATH10K_PKT_RX_ERR_FCS,
	ATH10K_PKT_RX_ERR_TKIP,
	ATH10K_PKT_RX_ERR_CRYPT,
	ATH10K_PKT_RX_ERR_PEER_IDX_INVAL,
	ATH10K_PKT_RX_ERR_MAX,
};

enum ath10k_ampdu_subfrm_num {
	ATH10K_AMPDU_SUBFRM_NUM_10,
	ATH10K_AMPDU_SUBFRM_NUM_20,
	ATH10K_AMPDU_SUBFRM_NUM_30,
	ATH10K_AMPDU_SUBFRM_NUM_40,
	ATH10K_AMPDU_SUBFRM_NUM_50,
	ATH10K_AMPDU_SUBFRM_NUM_60,
	ATH10K_AMPDU_SUBFRM_NUM_MORE,
	ATH10K_AMPDU_SUBFRM_NUM_MAX,
};

enum ath10k_amsdu_subfrm_num {
	ATH10K_AMSDU_SUBFRM_NUM_1,
	ATH10K_AMSDU_SUBFRM_NUM_2,
	ATH10K_AMSDU_SUBFRM_NUM_3,
	ATH10K_AMSDU_SUBFRM_NUM_4,
	ATH10K_AMSDU_SUBFRM_NUM_MORE,
	ATH10K_AMSDU_SUBFRM_NUM_MAX,
};

struct ath10k_sta_tid_stats {
	unsigned long int rx_pkt_from_fw;
	unsigned long int rx_pkt_unchained;
	unsigned long int rx_pkt_drop_chained;
	unsigned long int rx_pkt_drop_filter;
	unsigned long int rx_pkt_err[ATH10K_PKT_RX_ERR_MAX];
	unsigned long int rx_pkt_queued_for_mac;
	unsigned long int rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MAX];
	unsigned long int rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MAX];
};

struct ath10k_sta {
	struct ath10k_vif *arvif;

@@ -371,6 +432,9 @@ struct ath10k_sta {
#ifdef CONFIG_MAC80211_DEBUGFS
	/* protected by conf_mutex */
	bool aggr_mode;

	/* Protected with ar->data_lock */
	struct ath10k_sta_tid_stats tid_stats[IEEE80211_NUM_TIDS + 1];
#endif
};

@@ -487,6 +551,7 @@ struct ath10k_debug {

	/* used for tpc-dump storage, protected by data-lock */
	struct ath10k_tpc_stats *tpc_stats;
	struct ath10k_tpc_stats_final *tpc_stats_final;

	struct completion tpc_complete;

@@ -1019,6 +1084,8 @@ struct ath10k {

	void *ce_priv;

	u32 sta_tid_stats_mask;

	/* must be last */
	u8 drv_priv[0] __aligned(sizeof(void *));
};
+154 −0
Original line number Diff line number Diff line
@@ -1480,6 +1480,19 @@ void ath10k_debug_tpc_stats_process(struct ath10k *ar,
	spin_unlock_bh(&ar->data_lock);
}

void
ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
				     struct ath10k_tpc_stats_final *tpc_stats)
{
	spin_lock_bh(&ar->data_lock);

	kfree(ar->debug.tpc_stats_final);
	ar->debug.tpc_stats_final = tpc_stats;
	complete(&ar->debug.tpc_complete);

	spin_unlock_bh(&ar->data_lock);
}

static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats,
				   unsigned int j, char *buf, size_t *len)
{
@@ -2143,6 +2156,137 @@ static const struct file_operations fops_fw_checksums = {
	.llseek = default_llseek,
};

static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file,
					      char __user *user_buf,
					      size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	char buf[32];
	size_t len;

	len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask);
	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t ath10k_sta_tid_stats_mask_write(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;
	u32 mask;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EFAULT;

	buf[len] = '\0';
	if (kstrtoint(buf, 0, &mask))
		return -EINVAL;

	ar->sta_tid_stats_mask = mask;

	return len;
}

static const struct file_operations fops_sta_tid_stats_mask = {
	.read = ath10k_sta_tid_stats_mask_read,
	.write = ath10k_sta_tid_stats_mask_write,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar)
{
	int ret;
	unsigned long time_left;

	lockdep_assert_held(&ar->conf_mutex);

	reinit_completion(&ar->debug.tpc_complete);

	ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM);
	if (ret) {
		ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret);
		return ret;
	}

	time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,
						1 * HZ);
	if (time_left == 0)
		return -ETIMEDOUT;

	return 0;
}

static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file)
{
	struct ath10k *ar = inode->i_private;
	void *buf;
	int ret;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH10K_STATE_ON) {
		ret = -ENETDOWN;
		goto err_unlock;
	}

	buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);
	if (!buf) {
		ret = -ENOMEM;
		goto err_unlock;
	}

	ret = ath10k_debug_tpc_stats_final_request(ar);
	if (ret) {
		ath10k_warn(ar, "failed to request tpc stats final: %d\n",
			    ret);
		goto err_free;
	}

	ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);
	file->private_data = buf;

	mutex_unlock(&ar->conf_mutex);
	return 0;

err_free:
	vfree(buf);

err_unlock:
	mutex_unlock(&ar->conf_mutex);
	return ret;
}

static int ath10k_tpc_stats_final_release(struct inode *inode,
					  struct file *file)
{
	vfree(file->private_data);

	return 0;
}

static ssize_t ath10k_tpc_stats_final_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_tpc_stats_final = {
	.open = ath10k_tpc_stats_final_open,
	.release = ath10k_tpc_stats_final_release,
	.read = ath10k_tpc_stats_final_read,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

int ath10k_debug_create(struct ath10k *ar)
{
	ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);
@@ -2258,6 +2402,16 @@ int ath10k_debug_register(struct ath10k *ar)
	debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar,
			    &fops_fw_checksums);

	if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS))
		debugfs_create_file("sta_tid_stats_mask", 0600,
				    ar->debug.debugfs_phy,
				    ar, &fops_sta_tid_stats_mask);

	if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map))
		debugfs_create_file("tpc_stats_final", 0400,
				    ar->debug.debugfs_phy, ar,
				    &fops_tpc_stats_final);

	return 0;
}

+41 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -101,6 +102,9 @@ void ath10k_debug_unregister(struct ath10k *ar);
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
void ath10k_debug_tpc_stats_process(struct ath10k *ar,
				    struct ath10k_tpc_stats *tpc_stats);
void
ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
				     struct ath10k_tpc_stats_final *tpc_stats);
void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);

#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
@@ -164,6 +168,13 @@ static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar,
	kfree(tpc_stats);
}

static inline void
ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
				     struct ath10k_tpc_stats_final *tpc_stats)
{
	kfree(tpc_stats);
}

static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
					   int len)
{
@@ -191,12 +202,42 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			    struct ieee80211_sta *sta, struct dentry *dir);
void ath10k_sta_update_rx_duration(struct ath10k *ar,
				   struct ath10k_fw_stats *stats);
void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
				    unsigned long int num_msdus,
				    enum ath10k_pkt_rx_err err,
				    unsigned long int unchain_cnt,
				    unsigned long int drop_cnt,
				    unsigned long int drop_cnt_filter,
				    unsigned long int queued_msdus);
void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar,
					  u16 peer_id, u8 tid,
					  struct htt_rx_indication_mpdu_range *ranges,
					  int num_ranges);
#else
static inline
void ath10k_sta_update_rx_duration(struct ath10k *ar,
				   struct ath10k_fw_stats *stats)
{
}

static inline
void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
				    unsigned long int num_msdus,
				    enum ath10k_pkt_rx_err err,
				    unsigned long int unchain_cnt,
				    unsigned long int drop_cnt,
				    unsigned long int drop_cnt_filter,
				    unsigned long int queued_msdus)
{
}

static inline
void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar,
					  u16 peer_id, u8 tid,
					  struct htt_rx_indication_mpdu_range *ranges,
					  int num_ranges)
{
}
#endif /* CONFIG_MAC80211_DEBUGFS */

#ifdef CONFIG_ATH10K_DEBUG
+286 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
@@ -16,8 +17,125 @@

#include "core.h"
#include "wmi-ops.h"
#include "txrx.h"
#include "debug.h"

static void ath10k_rx_stats_update_amsdu_subfrm(struct ath10k *ar,
						struct ath10k_sta_tid_stats *stats,
						u32 msdu_count)
{
	if (msdu_count == 1)
		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_1]++;
	else if (msdu_count == 2)
		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_2]++;
	else if (msdu_count == 3)
		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_3]++;
	else if (msdu_count == 4)
		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_4]++;
	else if (msdu_count > 4)
		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MORE]++;
}

static void ath10k_rx_stats_update_ampdu_subfrm(struct ath10k *ar,
						struct ath10k_sta_tid_stats *stats,
						u32 mpdu_count)
{
	if (mpdu_count <= 10)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_10]++;
	else if (mpdu_count <= 20)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_20]++;
	else if (mpdu_count <= 30)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_30]++;
	else if (mpdu_count <= 40)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_40]++;
	else if (mpdu_count <= 50)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_50]++;
	else if (mpdu_count <= 60)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_60]++;
	else if (mpdu_count > 60)
		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MORE]++;
}

void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid,
					  struct htt_rx_indication_mpdu_range *ranges,
					  int num_ranges)
{
	struct ath10k_sta *arsta;
	struct ath10k_peer *peer;
	int i;

	if (tid > IEEE80211_NUM_TIDS || !(ar->sta_tid_stats_mask & BIT(tid)))
		return;

	rcu_read_lock();
	spin_lock_bh(&ar->data_lock);

	peer = ath10k_peer_find_by_id(ar, peer_id);
	if (!peer)
		goto out;

	arsta = (struct ath10k_sta *)peer->sta->drv_priv;

	for (i = 0; i < num_ranges; i++)
		ath10k_rx_stats_update_ampdu_subfrm(ar,
						    &arsta->tid_stats[tid],
						    ranges[i].mpdu_count);

out:
	spin_unlock_bh(&ar->data_lock);
	rcu_read_unlock();
}

void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
				    unsigned long int num_msdus,
				    enum ath10k_pkt_rx_err err,
				    unsigned long int unchain_cnt,
				    unsigned long int drop_cnt,
				    unsigned long int drop_cnt_filter,
				    unsigned long int queued_msdus)
{
	struct ieee80211_sta *sta;
	struct ath10k_sta *arsta;
	struct ieee80211_hdr *hdr;
	struct ath10k_sta_tid_stats *stats;
	u8 tid = IEEE80211_NUM_TIDS;
	bool non_data_frm = false;

	hdr = (struct ieee80211_hdr *)first_hdr;
	if (!ieee80211_is_data(hdr->frame_control))
		non_data_frm = true;

	if (ieee80211_is_data_qos(hdr->frame_control))
		tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;

	if (!(ar->sta_tid_stats_mask & BIT(tid)) || non_data_frm)
		return;

	rcu_read_lock();

	sta = ieee80211_find_sta_by_ifaddr(ar->hw, hdr->addr2, NULL);
	if (!sta)
		goto exit;

	arsta = (struct ath10k_sta *)sta->drv_priv;

	spin_lock_bh(&ar->data_lock);
	stats = &arsta->tid_stats[tid];
	stats->rx_pkt_from_fw += num_msdus;
	stats->rx_pkt_unchained += unchain_cnt;
	stats->rx_pkt_drop_chained += drop_cnt;
	stats->rx_pkt_drop_filter += drop_cnt_filter;
	if (err != ATH10K_PKT_RX_ERR_MAX)
		stats->rx_pkt_err[err] += queued_msdus;
	stats->rx_pkt_queued_for_mac += queued_msdus;
	ath10k_rx_stats_update_amsdu_subfrm(ar, &arsta->tid_stats[tid],
					    num_msdus);
	spin_unlock_bh(&ar->data_lock);

exit:
	rcu_read_unlock();
}

static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar,
						     struct ath10k_fw_stats *stats)
{
@@ -342,6 +460,172 @@ static const struct file_operations fops_peer_debug_trigger = {
	.llseek = default_llseek,
};

static char *get_err_str(enum ath10k_pkt_rx_err i)
{
	switch (i) {
	case ATH10K_PKT_RX_ERR_FCS:
		return "fcs_err";
	case ATH10K_PKT_RX_ERR_TKIP:
		return "tkip_err";
	case ATH10K_PKT_RX_ERR_CRYPT:
		return "crypt_err";
	case ATH10K_PKT_RX_ERR_PEER_IDX_INVAL:
		return "peer_idx_inval";
	case ATH10K_PKT_RX_ERR_MAX:
		return "unknown";
	}

	return "unknown";
}

static char *get_num_ampdu_subfrm_str(enum ath10k_ampdu_subfrm_num i)
{
	switch (i) {
	case ATH10K_AMPDU_SUBFRM_NUM_10:
		return "upto 10";
	case ATH10K_AMPDU_SUBFRM_NUM_20:
		return "11-20";
	case ATH10K_AMPDU_SUBFRM_NUM_30:
		return "21-30";
	case ATH10K_AMPDU_SUBFRM_NUM_40:
		return "31-40";
	case ATH10K_AMPDU_SUBFRM_NUM_50:
		return "41-50";
	case ATH10K_AMPDU_SUBFRM_NUM_60:
		return "51-60";
	case ATH10K_AMPDU_SUBFRM_NUM_MORE:
		return ">60";
	case ATH10K_AMPDU_SUBFRM_NUM_MAX:
		return "0";
	}

	return "0";
}

static char *get_num_amsdu_subfrm_str(enum ath10k_amsdu_subfrm_num i)
{
	switch (i) {
	case ATH10K_AMSDU_SUBFRM_NUM_1:
		return "1";
	case ATH10K_AMSDU_SUBFRM_NUM_2:
		return "2";
	case ATH10K_AMSDU_SUBFRM_NUM_3:
		return "3";
	case ATH10K_AMSDU_SUBFRM_NUM_4:
		return "4";
	case ATH10K_AMSDU_SUBFRM_NUM_MORE:
		return ">4";
	case ATH10K_AMSDU_SUBFRM_NUM_MAX:
		return "0";
	}

	return "0";
}

#define PRINT_TID_STATS(_field, _tabs) \
	do { \
		int k = 0; \
		for (j = 0; j <= IEEE80211_NUM_TIDS; j++) { \
			if (ar->sta_tid_stats_mask & BIT(j))  { \
				len += scnprintf(buf + len, buf_len - len, \
						 "[%02d] %-10lu  ", \
						 j, stats[j]._field); \
				k++; \
				if (k % 8 == 0)  { \
					len += scnprintf(buf + len, \
							 buf_len - len, "\n"); \
					len += scnprintf(buf + len, \
							 buf_len - len, \
							 _tabs); \
				} \
			} \
		} \
		len += scnprintf(buf + len, buf_len - len, "\n"); \
	} while (0)

static ssize_t ath10k_dbg_sta_read_tid_stats(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;
	struct ath10k_sta_tid_stats *stats = arsta->tid_stats;
	size_t len = 0, buf_len = 1048 * IEEE80211_NUM_TIDS;
	char *buf;
	int i, j;
	ssize_t ret;

	buf = kzalloc(buf_len, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	mutex_lock(&ar->conf_mutex);

	spin_lock_bh(&ar->data_lock);

	len += scnprintf(buf + len, buf_len - len,
			 "\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n");
	len += scnprintf(buf + len, buf_len - len,
			 "\t\t------------------------------------------\n");
	len += scnprintf(buf + len, buf_len - len, "MSDUs from FW\t\t\t");
	PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t");

	len += scnprintf(buf + len, buf_len - len, "MSDUs unchained\t\t\t");
	PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t");

	len += scnprintf(buf + len, buf_len - len,
			 "MSDUs locally dropped:chained\t");
	PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t");

	len += scnprintf(buf + len, buf_len - len,
			 "MSDUs locally dropped:filtered\t");
	PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t");

	len += scnprintf(buf + len, buf_len - len,
			 "MSDUs queued for mac80211\t");
	PRINT_TID_STATS(rx_pkt_queued_for_mac, "\t\t\t\t");

	for (i = 0; i < ATH10K_PKT_RX_ERR_MAX; i++) {
		len += scnprintf(buf + len, buf_len - len,
				 "MSDUs with error:%s\t", get_err_str(i));
		PRINT_TID_STATS(rx_pkt_err[i], "\t\t\t\t");
	}

	len += scnprintf(buf + len, buf_len - len, "\n");
	for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) {
		len += scnprintf(buf + len, buf_len - len,
				 "A-MPDU num subframes %s\t",
				 get_num_ampdu_subfrm_str(i));
		PRINT_TID_STATS(rx_pkt_ampdu[i], "\t\t\t\t");
	}

	len += scnprintf(buf + len, buf_len - len, "\n");
	for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) {
		len += scnprintf(buf + len, buf_len - len,
				 "A-MSDU num subframes %s\t\t",
				 get_num_amsdu_subfrm_str(i));
		PRINT_TID_STATS(rx_pkt_amsdu[i], "\t\t\t\t");
	}

	spin_unlock_bh(&ar->data_lock);

	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);

	kfree(buf);

	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static const struct file_operations fops_tid_stats_dump = {
	.open = simple_open,
	.read = ath10k_dbg_sta_read_tid_stats,
	.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)
{
@@ -351,4 +635,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
	debugfs_create_file("delba", 0200, dir, sta, &fops_delba);
	debugfs_create_file("peer_debug_trigger", 0600, dir, sta,
			    &fops_peer_debug_trigger);
	debugfs_create_file("dump_tid_stats", 0400, dir, sta,
			    &fops_tid_stats_dump);
}
Loading