1 | // SPDX-License-Identifier: ISC |
2 | /* |
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 | |
13 | static 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 | |
29 | static 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 | |
49 | void 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(lock: &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 | stats: &arsta->tid_stats[tid], |
72 | mpdu_count: ranges[i].mpdu_count); |
73 | |
74 | out: |
75 | spin_unlock_bh(lock: &ar->data_lock); |
76 | rcu_read_unlock(); |
77 | } |
78 | |
79 | void 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(fc: hdr->frame_control)) |
96 | non_data_frm = true; |
97 | |
98 | if (ieee80211_is_data_qos(fc: 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(hw: ar->hw, addr: hdr->addr2, NULL); |
107 | if (!sta) |
108 | goto exit; |
109 | |
110 | arsta = (struct ath10k_sta *)sta->drv_priv; |
111 | |
112 | spin_lock_bh(lock: &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, stats: &arsta->tid_stats[tid], |
122 | msdu_count: num_msdus); |
123 | spin_unlock_bh(lock: &ar->data_lock); |
124 | |
125 | exit: |
126 | rcu_read_unlock(); |
127 | } |
128 | |
129 | static 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(hw: ar->hw, addr: 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 | |
148 | static 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(hw: ar->hw, addr: 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 | |
167 | void 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 | else |
173 | ath10k_sta_update_stats_rx_duration(ar, stats); |
174 | } |
175 | |
176 | static 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, size: sizeof(buf) - len, fmt: "aggregation mode: %s\n" , |
188 | (arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ? |
189 | "auto" : "manual" ); |
190 | mutex_unlock(lock: &ar->conf_mutex); |
191 | |
192 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
193 | } |
194 | |
195 | static 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(s: user_buf, count, base: 0, res: &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, vdev_id: arsta->arvif->vdev_id, mac: sta->addr); |
219 | if (ret) { |
220 | ath10k_warn(ar, fmt: "failed to clear addba session ret: %d\n" , ret); |
221 | goto out; |
222 | } |
223 | |
224 | arsta->aggr_mode = aggr_mode; |
225 | out: |
226 | mutex_unlock(lock: &ar->conf_mutex); |
227 | return ret; |
228 | } |
229 | |
230 | static 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 | |
238 | static 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(to: buf, available: sizeof(buf) - 1, ppos, |
250 | from: 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, vdev_id: arsta->arvif->vdev_id, mac: sta->addr, |
270 | tid, buf_size); |
271 | if (ret) { |
272 | ath10k_warn(ar, fmt: "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; |
277 | out: |
278 | mutex_unlock(lock: &ar->conf_mutex); |
279 | return ret; |
280 | } |
281 | |
282 | static 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 | |
289 | static 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(to: buf, available: sizeof(buf) - 1, ppos, |
301 | from: 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, vdev_id: arsta->arvif->vdev_id, mac: sta->addr, |
321 | tid, status); |
322 | if (ret) { |
323 | ath10k_warn(ar, fmt: "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; |
327 | out: |
328 | mutex_unlock(lock: &ar->conf_mutex); |
329 | return ret; |
330 | } |
331 | |
332 | static 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 | |
339 | static 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(to: buf, available: sizeof(buf) - 1, ppos, |
351 | from: 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, vdev_id: arsta->arvif->vdev_id, mac: sta->addr, |
371 | tid, initiator, reason); |
372 | if (ret) { |
373 | ath10k_warn(ar, fmt: "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; |
378 | out: |
379 | mutex_unlock(lock: &ar->conf_mutex); |
380 | return ret; |
381 | } |
382 | |
383 | static 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 | |
390 | static 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, size: sizeof(buf) - len, |
403 | fmt: "Write 1 to once trigger the debug logs\n" ); |
404 | mutex_unlock(lock: &ar->conf_mutex); |
405 | |
406 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
407 | } |
408 | |
409 | static ssize_t |
410 | ath10k_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(s: user_buf, count, base: 0, res: &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, vdev_id: arsta->arvif->vdev_id, peer_addr: sta->addr, |
434 | param_id: ar->wmi.peer_param->debug, param_value: peer_debug_trigger); |
435 | if (ret) { |
436 | ath10k_warn(ar, fmt: "failed to set param to trigger peer tid logs for station ret: %d\n" , |
437 | ret); |
438 | goto out; |
439 | } |
440 | out: |
441 | mutex_unlock(lock: &ar->conf_mutex); |
442 | return count; |
443 | } |
444 | |
445 | static 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 | |
453 | static 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(lock: &ar->data_lock); |
464 | |
465 | len = scnprintf(buf, size: sizeof(buf) - len, fmt: "%d\n" , |
466 | arsta->peer_ps_state); |
467 | |
468 | spin_unlock_bh(lock: &ar->data_lock); |
469 | |
470 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
471 | } |
472 | |
473 | static 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 | |
480 | static 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 | |
498 | static 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 | |
522 | static 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 | |
563 | static 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(size: buf_len, GFP_KERNEL); |
577 | if (!buf) |
578 | return -ENOMEM; |
579 | |
580 | mutex_lock(&ar->conf_mutex); |
581 | |
582 | spin_lock_bh(lock: &ar->data_lock); |
583 | |
584 | len += scnprintf(buf: buf + len, size: buf_len - len, |
585 | fmt: "\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n" ); |
586 | len += scnprintf(buf: buf + len, size: buf_len - len, |
587 | fmt: "\t\t------------------------------------------\n" ); |
588 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "MSDUs from FW\t\t\t" ); |
589 | PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t" ); |
590 | |
591 | len += scnprintf(buf: buf + len, size: buf_len - len, fmt: "MSDUs unchained\t\t\t" ); |
592 | PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t" ); |
593 | |
594 | len += scnprintf(buf: buf + len, size: buf_len - len, |
595 | fmt: "MSDUs locally dropped:chained\t" ); |
596 | PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t" ); |
597 | |
598 | len += scnprintf(buf: buf + len, size: buf_len - len, |
599 | fmt: "MSDUs locally dropped:filtered\t" ); |
600 | PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t" ); |
601 | |
602 | len += scnprintf(buf: buf + len, size: buf_len - len, |
603 | fmt: "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: buf + len, size: buf_len - len, |
608 | fmt: "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: buf + len, size: buf_len - len, fmt: "\n" ); |
613 | for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) { |
614 | len += scnprintf(buf: buf + len, size: buf_len - len, |
615 | fmt: "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: buf + len, size: buf_len - len, fmt: "\n" ); |
621 | for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) { |
622 | len += scnprintf(buf: buf + len, size: buf_len - len, |
623 | fmt: "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(lock: &ar->data_lock); |
629 | |
630 | ret = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
631 | |
632 | kfree(objp: buf); |
633 | |
634 | mutex_unlock(lock: &ar->conf_mutex); |
635 | |
636 | return ret; |
637 | } |
638 | |
639 | static 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 | |
646 | static 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, fmt: "failed to get tx stats" ); |
669 | mutex_unlock(lock: &ar->conf_mutex); |
670 | kfree(objp: buf); |
671 | return 0; |
672 | } |
673 | |
674 | spin_lock_bh(lock: &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: buf + len, size: size - len, fmt: "%s_%s\n" , |
679 | str_name[k], |
680 | str[j]); |
681 | len += scnprintf(buf: buf + len, size: size - len, |
682 | fmt: " VHT MCS %s\n" , |
683 | str[j]); |
684 | for (i = 0; i < ATH10K_VHT_MCS_NUM; i++) |
685 | len += scnprintf(buf: buf + len, size: size - len, |
686 | fmt: " %llu " , |
687 | stats->vht[j][i]); |
688 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
689 | len += scnprintf(buf: buf + len, size: size - len, fmt: " HT MCS %s\n" , |
690 | str[j]); |
691 | for (i = 0; i < ATH10K_HT_MCS_NUM; i++) |
692 | len += scnprintf(buf: buf + len, size: size - len, |
693 | fmt: " %llu " , stats->ht[j][i]); |
694 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
695 | len += scnprintf(buf: buf + len, size: size - len, |
696 | fmt: " BW %s (20,5,10,40,80,160 MHz)\n" , str[j]); |
697 | len += scnprintf(buf: buf + len, size: size - len, |
698 | fmt: " %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: buf + len, size: size - len, |
703 | fmt: " NSS %s (1x1,2x2,3x3,4x4)\n" , str[j]); |
704 | len += scnprintf(buf: buf + len, size: size - len, |
705 | fmt: " %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: buf + len, size: size - len, |
709 | fmt: " GI %s (LGI,SGI)\n" , |
710 | str[j]); |
711 | len += scnprintf(buf: buf + len, size: size - len, fmt: " %llu %llu\n" , |
712 | stats->gi[j][0], stats->gi[j][1]); |
713 | len += scnprintf(buf: buf + len, size: size - len, |
714 | fmt: " legacy rate %s (1,2 ... Mbps)\n " , |
715 | str[j]); |
716 | for (i = 0; i < ATH10K_LEGACY_NUM; i++) |
717 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%llu " , |
718 | stats->legacy[j][i]); |
719 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
720 | len += scnprintf(buf: buf + len, size: size - len, |
721 | fmt: " Rate table %s (1,2 ... Mbps)\n " , |
722 | str[j]); |
723 | for (i = 0; i < ATH10K_RATE_TABLE_NUM; i++) { |
724 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%llu " , |
725 | stats->rate_table[j][i]); |
726 | if (!((i + 1) % 8)) |
727 | len += |
728 | scnprintf(buf: buf + len, size: size - len, fmt: "\n " ); |
729 | } |
730 | } |
731 | } |
732 | |
733 | len += scnprintf(buf: buf + len, size: size - len, |
734 | fmt: "\nTX duration\n %llu usecs\n" , |
735 | arsta->tx_stats->tx_duration); |
736 | len += scnprintf(buf: buf + len, size: size - len, |
737 | fmt: "BA fails\n %llu\n" , arsta->tx_stats->ba_fails); |
738 | len += scnprintf(buf: buf + len, size: size - len, |
739 | fmt: "ack fails\n %llu\n" , arsta->tx_stats->ack_fails); |
740 | spin_unlock_bh(lock: &ar->data_lock); |
741 | |
742 | if (len > size) |
743 | len = size; |
744 | retval = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
745 | kfree(objp: buf); |
746 | |
747 | mutex_unlock(lock: &ar->conf_mutex); |
748 | return retval; |
749 | } |
750 | |
751 | static 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 | |
758 | void 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(name: "aggr_mode" , mode: 0644, parent: dir, data: sta, fops: &fops_aggr_mode); |
764 | debugfs_create_file(name: "addba" , mode: 0200, parent: dir, data: sta, fops: &fops_addba); |
765 | debugfs_create_file(name: "addba_resp" , mode: 0200, parent: dir, data: sta, fops: &fops_addba_resp); |
766 | debugfs_create_file(name: "delba" , mode: 0200, parent: dir, data: sta, fops: &fops_delba); |
767 | debugfs_create_file(name: "peer_debug_trigger" , mode: 0600, parent: dir, data: sta, |
768 | fops: &fops_peer_debug_trigger); |
769 | debugfs_create_file(name: "dump_tid_stats" , mode: 0400, parent: dir, data: sta, |
770 | fops: &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(name: "tx_stats" , mode: 0400, parent: dir, data: sta, |
775 | fops: &fops_tx_stats); |
776 | debugfs_create_file(name: "peer_ps_state" , mode: 0400, parent: dir, data: sta, |
777 | fops: &fops_peer_ps_state); |
778 | } |
779 | |