brintos

brintos / linux-shallow public Read only

0
0
Text · 21.0 KiB · 0f6de86 Raw
779 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.4 * Copyright (c) 2018, The Linux Foundation. All rights reserved.5 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.6 */7 8#include "core.h"9#include "wmi-ops.h"10#include "txrx.h"11#include "debug.h"12 13static void ath10k_rx_stats_update_amsdu_subfrm(struct ath10k *ar,14						struct ath10k_sta_tid_stats *stats,15						u32 msdu_count)16{17	if (msdu_count == 1)18		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_1]++;19	else if (msdu_count == 2)20		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_2]++;21	else if (msdu_count == 3)22		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_3]++;23	else if (msdu_count == 4)24		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_4]++;25	else if (msdu_count > 4)26		stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MORE]++;27}28 29static void ath10k_rx_stats_update_ampdu_subfrm(struct ath10k *ar,30						struct ath10k_sta_tid_stats *stats,31						u32 mpdu_count)32{33	if (mpdu_count <= 10)34		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_10]++;35	else if (mpdu_count <= 20)36		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_20]++;37	else if (mpdu_count <= 30)38		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_30]++;39	else if (mpdu_count <= 40)40		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_40]++;41	else if (mpdu_count <= 50)42		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_50]++;43	else if (mpdu_count <= 60)44		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_60]++;45	else if (mpdu_count > 60)46		stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MORE]++;47}48 49void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid,50					  struct htt_rx_indication_mpdu_range *ranges,51					  int num_ranges)52{53	struct ath10k_sta *arsta;54	struct ath10k_peer *peer;55	int i;56 57	if (tid > IEEE80211_NUM_TIDS || !(ar->sta_tid_stats_mask & BIT(tid)))58		return;59 60	rcu_read_lock();61	spin_lock_bh(&ar->data_lock);62 63	peer = ath10k_peer_find_by_id(ar, peer_id);64	if (!peer || !peer->sta)65		goto out;66 67	arsta = (struct ath10k_sta *)peer->sta->drv_priv;68 69	for (i = 0; i < num_ranges; i++)70		ath10k_rx_stats_update_ampdu_subfrm(ar,71						    &arsta->tid_stats[tid],72						    ranges[i].mpdu_count);73 74out:75	spin_unlock_bh(&ar->data_lock);76	rcu_read_unlock();77}78 79void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,80				    unsigned long num_msdus,81				    enum ath10k_pkt_rx_err err,82				    unsigned long unchain_cnt,83				    unsigned long drop_cnt,84				    unsigned long drop_cnt_filter,85				    unsigned long queued_msdus)86{87	struct ieee80211_sta *sta;88	struct ath10k_sta *arsta;89	struct ieee80211_hdr *hdr;90	struct ath10k_sta_tid_stats *stats;91	u8 tid = IEEE80211_NUM_TIDS;92	bool non_data_frm = false;93 94	hdr = (struct ieee80211_hdr *)first_hdr;95	if (!ieee80211_is_data(hdr->frame_control))96		non_data_frm = true;97 98	if (ieee80211_is_data_qos(hdr->frame_control))99		tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;100 101	if (!(ar->sta_tid_stats_mask & BIT(tid)) || non_data_frm)102		return;103 104	rcu_read_lock();105 106	sta = ieee80211_find_sta_by_ifaddr(ar->hw, hdr->addr2, NULL);107	if (!sta)108		goto exit;109 110	arsta = (struct ath10k_sta *)sta->drv_priv;111 112	spin_lock_bh(&ar->data_lock);113	stats = &arsta->tid_stats[tid];114	stats->rx_pkt_from_fw += num_msdus;115	stats->rx_pkt_unchained += unchain_cnt;116	stats->rx_pkt_drop_chained += drop_cnt;117	stats->rx_pkt_drop_filter += drop_cnt_filter;118	if (err != ATH10K_PKT_RX_ERR_MAX)119		stats->rx_pkt_err[err] += queued_msdus;120	stats->rx_pkt_queued_for_mac += queued_msdus;121	ath10k_rx_stats_update_amsdu_subfrm(ar, &arsta->tid_stats[tid],122					    num_msdus);123	spin_unlock_bh(&ar->data_lock);124 125exit:126	rcu_read_unlock();127}128 129static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar,130						     struct ath10k_fw_stats *stats)131{132	struct ath10k_fw_extd_stats_peer *peer;133	struct ieee80211_sta *sta;134	struct ath10k_sta *arsta;135 136	rcu_read_lock();137	list_for_each_entry(peer, &stats->peers_extd, list) {138		sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr,139						   NULL);140		if (!sta)141			continue;142		arsta = (struct ath10k_sta *)sta->drv_priv;143		arsta->rx_duration += (u64)peer->rx_duration;144	}145	rcu_read_unlock();146}147 148static void ath10k_sta_update_stats_rx_duration(struct ath10k *ar,149						struct ath10k_fw_stats *stats)150{151	struct ath10k_fw_stats_peer *peer;152	struct ieee80211_sta *sta;153	struct ath10k_sta *arsta;154 155	rcu_read_lock();156	list_for_each_entry(peer, &stats->peers, list) {157		sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr,158						   NULL);159		if (!sta)160			continue;161		arsta = (struct ath10k_sta *)sta->drv_priv;162		arsta->rx_duration += (u64)peer->rx_duration;163	}164	rcu_read_unlock();165}166 167void ath10k_sta_update_rx_duration(struct ath10k *ar,168				   struct ath10k_fw_stats *stats)169{170	if (stats->extended)171		ath10k_sta_update_extd_stats_rx_duration(ar, stats);172	else173		ath10k_sta_update_stats_rx_duration(ar, stats);174}175 176static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file,177					     char __user *user_buf,178					     size_t count, loff_t *ppos)179{180	struct ieee80211_sta *sta = file->private_data;181	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;182	struct ath10k *ar = arsta->arvif->ar;183	char buf[32];184	int len = 0;185 186	mutex_lock(&ar->conf_mutex);187	len = scnprintf(buf, sizeof(buf) - len, "aggregation mode: %s\n",188			(arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ?189			"auto" : "manual");190	mutex_unlock(&ar->conf_mutex);191 192	return simple_read_from_buffer(user_buf, count, ppos, buf, len);193}194 195static ssize_t ath10k_dbg_sta_write_aggr_mode(struct file *file,196					      const char __user *user_buf,197					      size_t count, loff_t *ppos)198{199	struct ieee80211_sta *sta = file->private_data;200	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;201	struct ath10k *ar = arsta->arvif->ar;202	u32 aggr_mode;203	int ret;204 205	if (kstrtouint_from_user(user_buf, count, 0, &aggr_mode))206		return -EINVAL;207 208	if (aggr_mode >= ATH10K_DBG_AGGR_MODE_MAX)209		return -EINVAL;210 211	mutex_lock(&ar->conf_mutex);212	if ((ar->state != ATH10K_STATE_ON) ||213	    (aggr_mode == arsta->aggr_mode)) {214		ret = count;215		goto out;216	}217 218	ret = ath10k_wmi_addba_clear_resp(ar, arsta->arvif->vdev_id, sta->addr);219	if (ret) {220		ath10k_warn(ar, "failed to clear addba session ret: %d\n", ret);221		goto out;222	}223 224	arsta->aggr_mode = aggr_mode;225out:226	mutex_unlock(&ar->conf_mutex);227	return ret;228}229 230static const struct file_operations fops_aggr_mode = {231	.read = ath10k_dbg_sta_read_aggr_mode,232	.write = ath10k_dbg_sta_write_aggr_mode,233	.open = simple_open,234	.owner = THIS_MODULE,235	.llseek = default_llseek,236};237 238static ssize_t ath10k_dbg_sta_write_addba(struct file *file,239					  const char __user *user_buf,240					  size_t count, loff_t *ppos)241{242	struct ieee80211_sta *sta = file->private_data;243	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;244	struct ath10k *ar = arsta->arvif->ar;245	u32 tid, buf_size;246	int ret;247	char buf[64] = {0};248 249	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,250				     user_buf, count);251	if (ret <= 0)252		return ret;253 254	ret = sscanf(buf, "%u %u", &tid, &buf_size);255	if (ret != 2)256		return -EINVAL;257 258	/* Valid TID values are 0 through 15 */259	if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)260		return -EINVAL;261 262	mutex_lock(&ar->conf_mutex);263	if ((ar->state != ATH10K_STATE_ON) ||264	    (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {265		ret = count;266		goto out;267	}268 269	ret = ath10k_wmi_addba_send(ar, arsta->arvif->vdev_id, sta->addr,270				    tid, buf_size);271	if (ret) {272		ath10k_warn(ar, "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n",273			    arsta->arvif->vdev_id, sta->addr, tid, buf_size);274	}275 276	ret = count;277out:278	mutex_unlock(&ar->conf_mutex);279	return ret;280}281 282static const struct file_operations fops_addba = {283	.write = ath10k_dbg_sta_write_addba,284	.open = simple_open,285	.owner = THIS_MODULE,286	.llseek = default_llseek,287};288 289static ssize_t ath10k_dbg_sta_write_addba_resp(struct file *file,290					       const char __user *user_buf,291					       size_t count, loff_t *ppos)292{293	struct ieee80211_sta *sta = file->private_data;294	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;295	struct ath10k *ar = arsta->arvif->ar;296	u32 tid, status;297	int ret;298	char buf[64] = {0};299 300	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,301				     user_buf, count);302	if (ret <= 0)303		return ret;304 305	ret = sscanf(buf, "%u %u", &tid, &status);306	if (ret != 2)307		return -EINVAL;308 309	/* Valid TID values are 0 through 15 */310	if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)311		return -EINVAL;312 313	mutex_lock(&ar->conf_mutex);314	if ((ar->state != ATH10K_STATE_ON) ||315	    (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {316		ret = count;317		goto out;318	}319 320	ret = ath10k_wmi_addba_set_resp(ar, arsta->arvif->vdev_id, sta->addr,321					tid, status);322	if (ret) {323		ath10k_warn(ar, "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n",324			    arsta->arvif->vdev_id, sta->addr, tid, status);325	}326	ret = count;327out:328	mutex_unlock(&ar->conf_mutex);329	return ret;330}331 332static const struct file_operations fops_addba_resp = {333	.write = ath10k_dbg_sta_write_addba_resp,334	.open = simple_open,335	.owner = THIS_MODULE,336	.llseek = default_llseek,337};338 339static ssize_t ath10k_dbg_sta_write_delba(struct file *file,340					  const char __user *user_buf,341					  size_t count, loff_t *ppos)342{343	struct ieee80211_sta *sta = file->private_data;344	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;345	struct ath10k *ar = arsta->arvif->ar;346	u32 tid, initiator, reason;347	int ret;348	char buf[64] = {0};349 350	ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,351				     user_buf, count);352	if (ret <= 0)353		return ret;354 355	ret = sscanf(buf, "%u %u %u", &tid, &initiator, &reason);356	if (ret != 3)357		return -EINVAL;358 359	/* Valid TID values are 0 through 15 */360	if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)361		return -EINVAL;362 363	mutex_lock(&ar->conf_mutex);364	if ((ar->state != ATH10K_STATE_ON) ||365	    (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {366		ret = count;367		goto out;368	}369 370	ret = ath10k_wmi_delba_send(ar, arsta->arvif->vdev_id, sta->addr,371				    tid, initiator, reason);372	if (ret) {373		ath10k_warn(ar, "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n",374			    arsta->arvif->vdev_id, sta->addr, tid, initiator,375			    reason);376	}377	ret = count;378out:379	mutex_unlock(&ar->conf_mutex);380	return ret;381}382 383static const struct file_operations fops_delba = {384	.write = ath10k_dbg_sta_write_delba,385	.open = simple_open,386	.owner = THIS_MODULE,387	.llseek = default_llseek,388};389 390static ssize_t ath10k_dbg_sta_read_peer_debug_trigger(struct file *file,391						      char __user *user_buf,392						      size_t count,393						      loff_t *ppos)394{395	struct ieee80211_sta *sta = file->private_data;396	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;397	struct ath10k *ar = arsta->arvif->ar;398	char buf[8];399	int len = 0;400 401	mutex_lock(&ar->conf_mutex);402	len = scnprintf(buf, sizeof(buf) - len,403			"Write 1 to once trigger the debug logs\n");404	mutex_unlock(&ar->conf_mutex);405 406	return simple_read_from_buffer(user_buf, count, ppos, buf, len);407}408 409static ssize_t410ath10k_dbg_sta_write_peer_debug_trigger(struct file *file,411					const char __user *user_buf,412					size_t count, loff_t *ppos)413{414	struct ieee80211_sta *sta = file->private_data;415	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;416	struct ath10k *ar = arsta->arvif->ar;417	u8 peer_debug_trigger;418	int ret;419 420	if (kstrtou8_from_user(user_buf, count, 0, &peer_debug_trigger))421		return -EINVAL;422 423	if (peer_debug_trigger != 1)424		return -EINVAL;425 426	mutex_lock(&ar->conf_mutex);427 428	if (ar->state != ATH10K_STATE_ON) {429		ret = -ENETDOWN;430		goto out;431	}432 433	ret = ath10k_wmi_peer_set_param(ar, arsta->arvif->vdev_id, sta->addr,434					ar->wmi.peer_param->debug, peer_debug_trigger);435	if (ret) {436		ath10k_warn(ar, "failed to set param to trigger peer tid logs for station ret: %d\n",437			    ret);438		goto out;439	}440out:441	mutex_unlock(&ar->conf_mutex);442	return ret ?: count;443}444 445static const struct file_operations fops_peer_debug_trigger = {446	.open = simple_open,447	.read = ath10k_dbg_sta_read_peer_debug_trigger,448	.write = ath10k_dbg_sta_write_peer_debug_trigger,449	.owner = THIS_MODULE,450	.llseek = default_llseek,451};452 453static ssize_t ath10k_dbg_sta_read_peer_ps_state(struct file *file,454						 char __user *user_buf,455						 size_t count, loff_t *ppos)456{457	struct ieee80211_sta *sta = file->private_data;458	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;459	struct ath10k *ar = arsta->arvif->ar;460	char buf[20];461	int len = 0;462 463	spin_lock_bh(&ar->data_lock);464 465	len = scnprintf(buf, sizeof(buf) - len, "%d\n",466			arsta->peer_ps_state);467 468	spin_unlock_bh(&ar->data_lock);469 470	return simple_read_from_buffer(user_buf, count, ppos, buf, len);471}472 473static const struct file_operations fops_peer_ps_state = {474	.open = simple_open,475	.read = ath10k_dbg_sta_read_peer_ps_state,476	.owner = THIS_MODULE,477	.llseek = default_llseek,478};479 480static char *get_err_str(enum ath10k_pkt_rx_err i)481{482	switch (i) {483	case ATH10K_PKT_RX_ERR_FCS:484		return "fcs_err";485	case ATH10K_PKT_RX_ERR_TKIP:486		return "tkip_err";487	case ATH10K_PKT_RX_ERR_CRYPT:488		return "crypt_err";489	case ATH10K_PKT_RX_ERR_PEER_IDX_INVAL:490		return "peer_idx_inval";491	case ATH10K_PKT_RX_ERR_MAX:492		return "unknown";493	}494 495	return "unknown";496}497 498static char *get_num_ampdu_subfrm_str(enum ath10k_ampdu_subfrm_num i)499{500	switch (i) {501	case ATH10K_AMPDU_SUBFRM_NUM_10:502		return "up to 10";503	case ATH10K_AMPDU_SUBFRM_NUM_20:504		return "11-20";505	case ATH10K_AMPDU_SUBFRM_NUM_30:506		return "21-30";507	case ATH10K_AMPDU_SUBFRM_NUM_40:508		return "31-40";509	case ATH10K_AMPDU_SUBFRM_NUM_50:510		return "41-50";511	case ATH10K_AMPDU_SUBFRM_NUM_60:512		return "51-60";513	case ATH10K_AMPDU_SUBFRM_NUM_MORE:514		return ">60";515	case ATH10K_AMPDU_SUBFRM_NUM_MAX:516		return "0";517	}518 519	return "0";520}521 522static char *get_num_amsdu_subfrm_str(enum ath10k_amsdu_subfrm_num i)523{524	switch (i) {525	case ATH10K_AMSDU_SUBFRM_NUM_1:526		return "1";527	case ATH10K_AMSDU_SUBFRM_NUM_2:528		return "2";529	case ATH10K_AMSDU_SUBFRM_NUM_3:530		return "3";531	case ATH10K_AMSDU_SUBFRM_NUM_4:532		return "4";533	case ATH10K_AMSDU_SUBFRM_NUM_MORE:534		return ">4";535	case ATH10K_AMSDU_SUBFRM_NUM_MAX:536		return "0";537	}538 539	return "0";540}541 542#define PRINT_TID_STATS(_field, _tabs) \543	do { \544		int k = 0; \545		for (j = 0; j <= IEEE80211_NUM_TIDS; j++) { \546			if (ar->sta_tid_stats_mask & BIT(j))  { \547				len += scnprintf(buf + len, buf_len - len, \548						 "[%02d] %-10lu  ", \549						 j, stats[j]._field); \550				k++; \551				if (k % 8 == 0)  { \552					len += scnprintf(buf + len, \553							 buf_len - len, "\n"); \554					len += scnprintf(buf + len, \555							 buf_len - len, \556							 _tabs); \557				} \558			} \559		} \560		len += scnprintf(buf + len, buf_len - len, "\n"); \561	} while (0)562 563static ssize_t ath10k_dbg_sta_read_tid_stats(struct file *file,564					     char __user *user_buf,565					     size_t count, loff_t *ppos)566{567	struct ieee80211_sta *sta = file->private_data;568	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;569	struct ath10k *ar = arsta->arvif->ar;570	struct ath10k_sta_tid_stats *stats = arsta->tid_stats;571	size_t len = 0, buf_len = 1048 * IEEE80211_NUM_TIDS;572	char *buf;573	int i, j;574	ssize_t ret;575 576	buf = kzalloc(buf_len, GFP_KERNEL);577	if (!buf)578		return -ENOMEM;579 580	mutex_lock(&ar->conf_mutex);581 582	spin_lock_bh(&ar->data_lock);583 584	len += scnprintf(buf + len, buf_len - len,585			 "\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n");586	len += scnprintf(buf + len, buf_len - len,587			 "\t\t------------------------------------------\n");588	len += scnprintf(buf + len, buf_len - len, "MSDUs from FW\t\t\t");589	PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t");590 591	len += scnprintf(buf + len, buf_len - len, "MSDUs unchained\t\t\t");592	PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t");593 594	len += scnprintf(buf + len, buf_len - len,595			 "MSDUs locally dropped:chained\t");596	PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t");597 598	len += scnprintf(buf + len, buf_len - len,599			 "MSDUs locally dropped:filtered\t");600	PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t");601 602	len += scnprintf(buf + len, buf_len - len,603			 "MSDUs queued for mac80211\t");604	PRINT_TID_STATS(rx_pkt_queued_for_mac, "\t\t\t\t");605 606	for (i = 0; i < ATH10K_PKT_RX_ERR_MAX; i++) {607		len += scnprintf(buf + len, buf_len - len,608				 "MSDUs with error:%s\t", get_err_str(i));609		PRINT_TID_STATS(rx_pkt_err[i], "\t\t\t\t");610	}611 612	len += scnprintf(buf + len, buf_len - len, "\n");613	for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) {614		len += scnprintf(buf + len, buf_len - len,615				 "A-MPDU num subframes %s\t",616				 get_num_ampdu_subfrm_str(i));617		PRINT_TID_STATS(rx_pkt_ampdu[i], "\t\t\t\t");618	}619 620	len += scnprintf(buf + len, buf_len - len, "\n");621	for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) {622		len += scnprintf(buf + len, buf_len - len,623				 "A-MSDU num subframes %s\t\t",624				 get_num_amsdu_subfrm_str(i));625		PRINT_TID_STATS(rx_pkt_amsdu[i], "\t\t\t\t");626	}627 628	spin_unlock_bh(&ar->data_lock);629 630	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);631 632	kfree(buf);633 634	mutex_unlock(&ar->conf_mutex);635 636	return ret;637}638 639static const struct file_operations fops_tid_stats_dump = {640	.open = simple_open,641	.read = ath10k_dbg_sta_read_tid_stats,642	.owner = THIS_MODULE,643	.llseek = default_llseek,644};645 646static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file,647					    char __user *user_buf,648					    size_t count, loff_t *ppos)649{650	struct ieee80211_sta *sta = file->private_data;651	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;652	struct ath10k *ar = arsta->arvif->ar;653	struct ath10k_htt_data_stats *stats;654	const char *str_name[ATH10K_STATS_TYPE_MAX] = {"succ", "fail",655						       "retry", "ampdu"};656	const char *str[ATH10K_COUNTER_TYPE_MAX] = {"bytes", "packets"};657	int len = 0, i, j, k, retval = 0;658	const int size = 16 * 4096;659	char *buf;660 661	buf = kzalloc(size, GFP_KERNEL);662	if (!buf)663		return -ENOMEM;664 665	mutex_lock(&ar->conf_mutex);666 667	if (!arsta->tx_stats) {668		ath10k_warn(ar, "failed to get tx stats");669		mutex_unlock(&ar->conf_mutex);670		kfree(buf);671		return 0;672	}673 674	spin_lock_bh(&ar->data_lock);675	for (k = 0; k < ATH10K_STATS_TYPE_MAX; k++) {676		for (j = 0; j < ATH10K_COUNTER_TYPE_MAX; j++) {677			stats = &arsta->tx_stats->stats[k];678			len += scnprintf(buf + len, size - len, "%s_%s\n",679					 str_name[k],680					 str[j]);681			len += scnprintf(buf + len, size - len,682					 " VHT MCS %s\n",683					 str[j]);684			for (i = 0; i < ATH10K_VHT_MCS_NUM; i++)685				len += scnprintf(buf + len, size - len,686						 "  %llu ",687						 stats->vht[j][i]);688			len += scnprintf(buf + len, size - len, "\n");689			len += scnprintf(buf + len, size - len, " HT MCS %s\n",690					 str[j]);691			for (i = 0; i < ATH10K_HT_MCS_NUM; i++)692				len += scnprintf(buf + len, size - len,693						 "  %llu ", stats->ht[j][i]);694			len += scnprintf(buf + len, size - len, "\n");695			len += scnprintf(buf + len, size - len,696					" BW %s (20,5,10,40,80,160 MHz)\n", str[j]);697			len += scnprintf(buf + len, size - len,698					 "  %llu %llu %llu %llu %llu %llu\n",699					 stats->bw[j][0], stats->bw[j][1],700					 stats->bw[j][2], stats->bw[j][3],701					 stats->bw[j][4], stats->bw[j][5]);702			len += scnprintf(buf + len, size - len,703					 " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]);704			len += scnprintf(buf + len, size - len,705					 "  %llu %llu %llu %llu\n",706					 stats->nss[j][0], stats->nss[j][1],707					 stats->nss[j][2], stats->nss[j][3]);708			len += scnprintf(buf + len, size - len,709					 " GI %s (LGI,SGI)\n",710					 str[j]);711			len += scnprintf(buf + len, size - len, "  %llu %llu\n",712					 stats->gi[j][0], stats->gi[j][1]);713			len += scnprintf(buf + len, size - len,714					 " legacy rate %s (1,2 ... Mbps)\n  ",715					 str[j]);716			for (i = 0; i < ATH10K_LEGACY_NUM; i++)717				len += scnprintf(buf + len, size - len, "%llu ",718						 stats->legacy[j][i]);719			len += scnprintf(buf + len, size - len, "\n");720			len += scnprintf(buf + len, size - len,721					 " Rate table %s (1,2 ... Mbps)\n  ",722					 str[j]);723			for (i = 0; i < ATH10K_RATE_TABLE_NUM; i++) {724				len += scnprintf(buf + len, size - len, "%llu ",725						 stats->rate_table[j][i]);726				if (!((i + 1) % 8))727					len +=728					scnprintf(buf + len, size - len, "\n  ");729			}730		}731	}732 733	len += scnprintf(buf + len, size - len,734			 "\nTX duration\n %llu usecs\n",735			 arsta->tx_stats->tx_duration);736	len += scnprintf(buf + len, size - len,737			"BA fails\n %llu\n", arsta->tx_stats->ba_fails);738	len += scnprintf(buf + len, size - len,739			"ack fails\n %llu\n", arsta->tx_stats->ack_fails);740	spin_unlock_bh(&ar->data_lock);741 742	if (len > size)743		len = size;744	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);745	kfree(buf);746 747	mutex_unlock(&ar->conf_mutex);748	return retval;749}750 751static const struct file_operations fops_tx_stats = {752	.read = ath10k_dbg_sta_dump_tx_stats,753	.open = simple_open,754	.owner = THIS_MODULE,755	.llseek = default_llseek,756};757 758void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,759			    struct ieee80211_sta *sta, struct dentry *dir)760{761	struct ath10k *ar = hw->priv;762 763	debugfs_create_file("aggr_mode", 0644, dir, sta, &fops_aggr_mode);764	debugfs_create_file("addba", 0200, dir, sta, &fops_addba);765	debugfs_create_file("addba_resp", 0200, dir, sta, &fops_addba_resp);766	debugfs_create_file("delba", 0200, dir, sta, &fops_delba);767	debugfs_create_file("peer_debug_trigger", 0600, dir, sta,768			    &fops_peer_debug_trigger);769	debugfs_create_file("dump_tid_stats", 0400, dir, sta,770			    &fops_tid_stats_dump);771 772	if (ath10k_peer_stats_enabled(ar) &&773	    ath10k_debug_is_extd_tx_stats_enabled(ar))774		debugfs_create_file("tx_stats", 0400, dir, sta,775				    &fops_tx_stats);776	debugfs_create_file("peer_ps_state", 0400, dir, sta,777			    &fops_peer_ps_state);778}779