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