1 | // SPDX-License-Identifier: BSD-3-Clause-Clear |
2 | /* |
3 | * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. |
4 | * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. |
5 | */ |
6 | |
7 | #include <linux/vmalloc.h> |
8 | |
9 | #include "debugfs_sta.h" |
10 | #include "core.h" |
11 | #include "peer.h" |
12 | #include "debug.h" |
13 | #include "dp_tx.h" |
14 | #include "debugfs_htt_stats.h" |
15 | |
16 | void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta, |
17 | struct ath11k_per_peer_tx_stats *peer_stats, |
18 | u8 legacy_rate_idx) |
19 | { |
20 | struct rate_info *txrate = &arsta->txrate; |
21 | struct ath11k_htt_tx_stats *tx_stats; |
22 | int gi, mcs, bw, nss; |
23 | |
24 | if (!arsta->tx_stats) |
25 | return; |
26 | |
27 | tx_stats = arsta->tx_stats; |
28 | gi = FIELD_GET(RATE_INFO_FLAGS_SHORT_GI, arsta->txrate.flags); |
29 | mcs = txrate->mcs; |
30 | bw = ath11k_mac_mac80211_bw_to_ath11k_bw(bw: txrate->bw); |
31 | nss = txrate->nss - 1; |
32 | |
33 | #define STATS_OP_FMT(name) tx_stats->stats[ATH11K_STATS_TYPE_##name] |
34 | |
35 | if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { |
36 | STATS_OP_FMT(SUCC).he[0][mcs] += peer_stats->succ_bytes; |
37 | STATS_OP_FMT(SUCC).he[1][mcs] += peer_stats->succ_pkts; |
38 | STATS_OP_FMT(FAIL).he[0][mcs] += peer_stats->failed_bytes; |
39 | STATS_OP_FMT(FAIL).he[1][mcs] += peer_stats->failed_pkts; |
40 | STATS_OP_FMT(RETRY).he[0][mcs] += peer_stats->retry_bytes; |
41 | STATS_OP_FMT(RETRY).he[1][mcs] += peer_stats->retry_pkts; |
42 | } else if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) { |
43 | STATS_OP_FMT(SUCC).vht[0][mcs] += peer_stats->succ_bytes; |
44 | STATS_OP_FMT(SUCC).vht[1][mcs] += peer_stats->succ_pkts; |
45 | STATS_OP_FMT(FAIL).vht[0][mcs] += peer_stats->failed_bytes; |
46 | STATS_OP_FMT(FAIL).vht[1][mcs] += peer_stats->failed_pkts; |
47 | STATS_OP_FMT(RETRY).vht[0][mcs] += peer_stats->retry_bytes; |
48 | STATS_OP_FMT(RETRY).vht[1][mcs] += peer_stats->retry_pkts; |
49 | } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { |
50 | STATS_OP_FMT(SUCC).ht[0][mcs] += peer_stats->succ_bytes; |
51 | STATS_OP_FMT(SUCC).ht[1][mcs] += peer_stats->succ_pkts; |
52 | STATS_OP_FMT(FAIL).ht[0][mcs] += peer_stats->failed_bytes; |
53 | STATS_OP_FMT(FAIL).ht[1][mcs] += peer_stats->failed_pkts; |
54 | STATS_OP_FMT(RETRY).ht[0][mcs] += peer_stats->retry_bytes; |
55 | STATS_OP_FMT(RETRY).ht[1][mcs] += peer_stats->retry_pkts; |
56 | } else { |
57 | mcs = legacy_rate_idx; |
58 | |
59 | STATS_OP_FMT(SUCC).legacy[0][mcs] += peer_stats->succ_bytes; |
60 | STATS_OP_FMT(SUCC).legacy[1][mcs] += peer_stats->succ_pkts; |
61 | STATS_OP_FMT(FAIL).legacy[0][mcs] += peer_stats->failed_bytes; |
62 | STATS_OP_FMT(FAIL).legacy[1][mcs] += peer_stats->failed_pkts; |
63 | STATS_OP_FMT(RETRY).legacy[0][mcs] += peer_stats->retry_bytes; |
64 | STATS_OP_FMT(RETRY).legacy[1][mcs] += peer_stats->retry_pkts; |
65 | } |
66 | |
67 | if (peer_stats->is_ampdu) { |
68 | tx_stats->ba_fails += peer_stats->ba_fails; |
69 | |
70 | if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { |
71 | STATS_OP_FMT(AMPDU).he[0][mcs] += |
72 | peer_stats->succ_bytes + peer_stats->retry_bytes; |
73 | STATS_OP_FMT(AMPDU).he[1][mcs] += |
74 | peer_stats->succ_pkts + peer_stats->retry_pkts; |
75 | } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { |
76 | STATS_OP_FMT(AMPDU).ht[0][mcs] += |
77 | peer_stats->succ_bytes + peer_stats->retry_bytes; |
78 | STATS_OP_FMT(AMPDU).ht[1][mcs] += |
79 | peer_stats->succ_pkts + peer_stats->retry_pkts; |
80 | } else { |
81 | STATS_OP_FMT(AMPDU).vht[0][mcs] += |
82 | peer_stats->succ_bytes + peer_stats->retry_bytes; |
83 | STATS_OP_FMT(AMPDU).vht[1][mcs] += |
84 | peer_stats->succ_pkts + peer_stats->retry_pkts; |
85 | } |
86 | STATS_OP_FMT(AMPDU).bw[0][bw] += |
87 | peer_stats->succ_bytes + peer_stats->retry_bytes; |
88 | STATS_OP_FMT(AMPDU).nss[0][nss] += |
89 | peer_stats->succ_bytes + peer_stats->retry_bytes; |
90 | STATS_OP_FMT(AMPDU).gi[0][gi] += |
91 | peer_stats->succ_bytes + peer_stats->retry_bytes; |
92 | STATS_OP_FMT(AMPDU).bw[1][bw] += |
93 | peer_stats->succ_pkts + peer_stats->retry_pkts; |
94 | STATS_OP_FMT(AMPDU).nss[1][nss] += |
95 | peer_stats->succ_pkts + peer_stats->retry_pkts; |
96 | STATS_OP_FMT(AMPDU).gi[1][gi] += |
97 | peer_stats->succ_pkts + peer_stats->retry_pkts; |
98 | } else { |
99 | tx_stats->ack_fails += peer_stats->ba_fails; |
100 | } |
101 | |
102 | STATS_OP_FMT(SUCC).bw[0][bw] += peer_stats->succ_bytes; |
103 | STATS_OP_FMT(SUCC).nss[0][nss] += peer_stats->succ_bytes; |
104 | STATS_OP_FMT(SUCC).gi[0][gi] += peer_stats->succ_bytes; |
105 | |
106 | STATS_OP_FMT(SUCC).bw[1][bw] += peer_stats->succ_pkts; |
107 | STATS_OP_FMT(SUCC).nss[1][nss] += peer_stats->succ_pkts; |
108 | STATS_OP_FMT(SUCC).gi[1][gi] += peer_stats->succ_pkts; |
109 | |
110 | STATS_OP_FMT(FAIL).bw[0][bw] += peer_stats->failed_bytes; |
111 | STATS_OP_FMT(FAIL).nss[0][nss] += peer_stats->failed_bytes; |
112 | STATS_OP_FMT(FAIL).gi[0][gi] += peer_stats->failed_bytes; |
113 | |
114 | STATS_OP_FMT(FAIL).bw[1][bw] += peer_stats->failed_pkts; |
115 | STATS_OP_FMT(FAIL).nss[1][nss] += peer_stats->failed_pkts; |
116 | STATS_OP_FMT(FAIL).gi[1][gi] += peer_stats->failed_pkts; |
117 | |
118 | STATS_OP_FMT(RETRY).bw[0][bw] += peer_stats->retry_bytes; |
119 | STATS_OP_FMT(RETRY).nss[0][nss] += peer_stats->retry_bytes; |
120 | STATS_OP_FMT(RETRY).gi[0][gi] += peer_stats->retry_bytes; |
121 | |
122 | STATS_OP_FMT(RETRY).bw[1][bw] += peer_stats->retry_pkts; |
123 | STATS_OP_FMT(RETRY).nss[1][nss] += peer_stats->retry_pkts; |
124 | STATS_OP_FMT(RETRY).gi[1][gi] += peer_stats->retry_pkts; |
125 | |
126 | tx_stats->tx_duration += peer_stats->duration; |
127 | } |
128 | |
129 | void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, |
130 | struct hal_tx_status *ts) |
131 | { |
132 | ath11k_dp_tx_update_txcompl(ar, ts); |
133 | } |
134 | |
135 | static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, |
136 | char __user *user_buf, |
137 | size_t count, loff_t *ppos) |
138 | { |
139 | struct ieee80211_sta *sta = file->private_data; |
140 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
141 | struct ath11k *ar = arsta->arvif->ar; |
142 | struct ath11k_htt_data_stats *stats; |
143 | static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"succ" , "fail" , |
144 | "retry" , "ampdu" }; |
145 | static const char *str[ATH11K_COUNTER_TYPE_MAX] = {"bytes" , "packets" }; |
146 | int len = 0, i, j, k, retval = 0; |
147 | const int size = 2 * 4096; |
148 | char *buf; |
149 | |
150 | if (!arsta->tx_stats) |
151 | return -ENOENT; |
152 | |
153 | buf = kzalloc(size, GFP_KERNEL); |
154 | if (!buf) |
155 | return -ENOMEM; |
156 | |
157 | mutex_lock(&ar->conf_mutex); |
158 | |
159 | spin_lock_bh(lock: &ar->data_lock); |
160 | for (k = 0; k < ATH11K_STATS_TYPE_MAX; k++) { |
161 | for (j = 0; j < ATH11K_COUNTER_TYPE_MAX; j++) { |
162 | stats = &arsta->tx_stats->stats[k]; |
163 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%s_%s\n" , |
164 | str_name[k], |
165 | str[j]); |
166 | len += scnprintf(buf: buf + len, size: size - len, |
167 | fmt: " HE MCS %s\n" , |
168 | str[j]); |
169 | for (i = 0; i < ATH11K_HE_MCS_NUM; i++) |
170 | len += scnprintf(buf: buf + len, size: size - len, |
171 | fmt: " %llu " , |
172 | stats->he[j][i]); |
173 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
174 | len += scnprintf(buf: buf + len, size: size - len, |
175 | fmt: " VHT MCS %s\n" , |
176 | str[j]); |
177 | for (i = 0; i < ATH11K_VHT_MCS_NUM; i++) |
178 | len += scnprintf(buf: buf + len, size: size - len, |
179 | fmt: " %llu " , |
180 | stats->vht[j][i]); |
181 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
182 | len += scnprintf(buf: buf + len, size: size - len, fmt: " HT MCS %s\n" , |
183 | str[j]); |
184 | for (i = 0; i < ATH11K_HT_MCS_NUM; i++) |
185 | len += scnprintf(buf: buf + len, size: size - len, |
186 | fmt: " %llu " , stats->ht[j][i]); |
187 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
188 | len += scnprintf(buf: buf + len, size: size - len, |
189 | fmt: " BW %s (20,40,80,160 MHz)\n" , str[j]); |
190 | len += scnprintf(buf: buf + len, size: size - len, |
191 | fmt: " %llu %llu %llu %llu\n" , |
192 | stats->bw[j][0], stats->bw[j][1], |
193 | stats->bw[j][2], stats->bw[j][3]); |
194 | len += scnprintf(buf: buf + len, size: size - len, |
195 | fmt: " NSS %s (1x1,2x2,3x3,4x4)\n" , str[j]); |
196 | len += scnprintf(buf: buf + len, size: size - len, |
197 | fmt: " %llu %llu %llu %llu\n" , |
198 | stats->nss[j][0], stats->nss[j][1], |
199 | stats->nss[j][2], stats->nss[j][3]); |
200 | len += scnprintf(buf: buf + len, size: size - len, |
201 | fmt: " GI %s (0.4us,0.8us,1.6us,3.2us)\n" , |
202 | str[j]); |
203 | len += scnprintf(buf: buf + len, size: size - len, |
204 | fmt: " %llu %llu %llu %llu\n" , |
205 | stats->gi[j][0], stats->gi[j][1], |
206 | stats->gi[j][2], stats->gi[j][3]); |
207 | len += scnprintf(buf: buf + len, size: size - len, |
208 | fmt: " legacy rate %s (1,2 ... Mbps)\n " , |
209 | str[j]); |
210 | for (i = 0; i < ATH11K_LEGACY_NUM; i++) |
211 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%llu " , |
212 | stats->legacy[j][i]); |
213 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
214 | } |
215 | } |
216 | |
217 | len += scnprintf(buf: buf + len, size: size - len, |
218 | fmt: "\nTX duration\n %llu usecs\n" , |
219 | arsta->tx_stats->tx_duration); |
220 | len += scnprintf(buf: buf + len, size: size - len, |
221 | fmt: "BA fails\n %llu\n" , arsta->tx_stats->ba_fails); |
222 | len += scnprintf(buf: buf + len, size: size - len, |
223 | fmt: "ack fails\n %llu\n" , arsta->tx_stats->ack_fails); |
224 | spin_unlock_bh(lock: &ar->data_lock); |
225 | |
226 | if (len > size) |
227 | len = size; |
228 | retval = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
229 | kfree(objp: buf); |
230 | |
231 | mutex_unlock(lock: &ar->conf_mutex); |
232 | return retval; |
233 | } |
234 | |
235 | static const struct file_operations fops_tx_stats = { |
236 | .read = ath11k_dbg_sta_dump_tx_stats, |
237 | .open = simple_open, |
238 | .owner = THIS_MODULE, |
239 | .llseek = default_llseek, |
240 | }; |
241 | |
242 | static ssize_t ath11k_dbg_sta_dump_rx_stats(struct file *file, |
243 | char __user *user_buf, |
244 | size_t count, loff_t *ppos) |
245 | { |
246 | struct ieee80211_sta *sta = file->private_data; |
247 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
248 | struct ath11k *ar = arsta->arvif->ar; |
249 | struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; |
250 | int len = 0, i, retval = 0; |
251 | const int size = 4096; |
252 | char *buf; |
253 | |
254 | if (!rx_stats) |
255 | return -ENOENT; |
256 | |
257 | buf = kzalloc(size, GFP_KERNEL); |
258 | if (!buf) |
259 | return -ENOMEM; |
260 | |
261 | mutex_lock(&ar->conf_mutex); |
262 | spin_lock_bh(lock: &ar->ab->base_lock); |
263 | |
264 | len += scnprintf(buf: buf + len, size: size - len, fmt: "RX peer stats:\n" ); |
265 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs: %llu\n" , |
266 | rx_stats->num_msdu); |
267 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs with TCP L4: %llu\n" , |
268 | rx_stats->tcp_msdu_count); |
269 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs with UDP L4: %llu\n" , |
270 | rx_stats->udp_msdu_count); |
271 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs part of AMPDU: %llu\n" , |
272 | rx_stats->ampdu_msdu_count); |
273 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs not part of AMPDU: %llu\n" , |
274 | rx_stats->non_ampdu_msdu_count); |
275 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs using STBC: %llu\n" , |
276 | rx_stats->stbc_count); |
277 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MSDUs beamformed: %llu\n" , |
278 | rx_stats->beamformed_count); |
279 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MPDUs with FCS ok: %llu\n" , |
280 | rx_stats->num_mpdu_fcs_ok); |
281 | len += scnprintf(buf: buf + len, size: size - len, fmt: "Num of MPDUs with FCS error: %llu\n" , |
282 | rx_stats->num_mpdu_fcs_err); |
283 | len += scnprintf(buf: buf + len, size: size - len, |
284 | fmt: "GI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n" , |
285 | rx_stats->gi_count[0], rx_stats->gi_count[1], |
286 | rx_stats->gi_count[2], rx_stats->gi_count[3]); |
287 | len += scnprintf(buf: buf + len, size: size - len, |
288 | fmt: "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n" , |
289 | rx_stats->bw_count[0], rx_stats->bw_count[1], |
290 | rx_stats->bw_count[2], rx_stats->bw_count[3]); |
291 | len += scnprintf(buf: buf + len, size: size - len, fmt: "BCC %llu LDPC %llu\n" , |
292 | rx_stats->coding_count[0], rx_stats->coding_count[1]); |
293 | len += scnprintf(buf: buf + len, size: size - len, |
294 | fmt: "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu\n" , |
295 | rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], |
296 | rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], |
297 | rx_stats->pream_cnt[4]); |
298 | len += scnprintf(buf: buf + len, size: size - len, |
299 | fmt: "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n" , |
300 | rx_stats->reception_type[0], rx_stats->reception_type[1], |
301 | rx_stats->reception_type[2], rx_stats->reception_type[3]); |
302 | len += scnprintf(buf: buf + len, size: size - len, fmt: "TID(0-15) Legacy TID(16):" ); |
303 | for (i = 0; i <= IEEE80211_NUM_TIDS; i++) |
304 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%llu " , rx_stats->tid_count[i]); |
305 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\nMCS(0-11) Legacy MCS(12):" ); |
306 | for (i = 0; i < HAL_RX_MAX_MCS + 1; i++) |
307 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%llu " , rx_stats->mcs_count[i]); |
308 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\nNSS(1-8):" ); |
309 | for (i = 0; i < HAL_RX_MAX_NSS; i++) |
310 | len += scnprintf(buf: buf + len, size: size - len, fmt: "%llu " , rx_stats->nss_count[i]); |
311 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\nRX Duration:%llu " , |
312 | rx_stats->rx_duration); |
313 | len += scnprintf(buf: buf + len, size: size - len, |
314 | fmt: "\nDCM: %llu\nRU: 26 %llu 52: %llu 106: %llu 242: %llu 484: %llu 996: %llu\n" , |
315 | rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], |
316 | rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], |
317 | rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], |
318 | rx_stats->ru_alloc_cnt[5]); |
319 | |
320 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
321 | |
322 | spin_unlock_bh(lock: &ar->ab->base_lock); |
323 | |
324 | if (len > size) |
325 | len = size; |
326 | retval = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
327 | kfree(objp: buf); |
328 | |
329 | mutex_unlock(lock: &ar->conf_mutex); |
330 | return retval; |
331 | } |
332 | |
333 | static const struct file_operations fops_rx_stats = { |
334 | .read = ath11k_dbg_sta_dump_rx_stats, |
335 | .open = simple_open, |
336 | .owner = THIS_MODULE, |
337 | .llseek = default_llseek, |
338 | }; |
339 | |
340 | static int |
341 | ath11k_dbg_sta_open_htt_peer_stats(struct inode *inode, struct file *file) |
342 | { |
343 | struct ieee80211_sta *sta = inode->i_private; |
344 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
345 | struct ath11k *ar = arsta->arvif->ar; |
346 | struct debug_htt_stats_req *stats_req; |
347 | int type = ar->debug.htt_stats.type; |
348 | int ret; |
349 | |
350 | if ((type != ATH11K_DBG_HTT_EXT_STATS_PEER_INFO && |
351 | type != ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS) || |
352 | type == ATH11K_DBG_HTT_EXT_STATS_RESET) |
353 | return -EPERM; |
354 | |
355 | stats_req = vzalloc(size: sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); |
356 | if (!stats_req) |
357 | return -ENOMEM; |
358 | |
359 | mutex_lock(&ar->conf_mutex); |
360 | ar->debug.htt_stats.stats_req = stats_req; |
361 | stats_req->type = type; |
362 | memcpy(stats_req->peer_addr, sta->addr, ETH_ALEN); |
363 | ret = ath11k_debugfs_htt_stats_req(ar); |
364 | mutex_unlock(lock: &ar->conf_mutex); |
365 | if (ret < 0) |
366 | goto out; |
367 | |
368 | file->private_data = stats_req; |
369 | return 0; |
370 | out: |
371 | vfree(addr: stats_req); |
372 | ar->debug.htt_stats.stats_req = NULL; |
373 | return ret; |
374 | } |
375 | |
376 | static int |
377 | ath11k_dbg_sta_release_htt_peer_stats(struct inode *inode, struct file *file) |
378 | { |
379 | struct ieee80211_sta *sta = inode->i_private; |
380 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
381 | struct ath11k *ar = arsta->arvif->ar; |
382 | |
383 | mutex_lock(&ar->conf_mutex); |
384 | vfree(addr: file->private_data); |
385 | ar->debug.htt_stats.stats_req = NULL; |
386 | mutex_unlock(lock: &ar->conf_mutex); |
387 | |
388 | return 0; |
389 | } |
390 | |
391 | static ssize_t ath11k_dbg_sta_read_htt_peer_stats(struct file *file, |
392 | char __user *user_buf, |
393 | size_t count, loff_t *ppos) |
394 | { |
395 | struct debug_htt_stats_req *stats_req = file->private_data; |
396 | char *buf; |
397 | u32 length = 0; |
398 | |
399 | buf = stats_req->buf; |
400 | length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); |
401 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: length); |
402 | } |
403 | |
404 | static const struct file_operations fops_htt_peer_stats = { |
405 | .open = ath11k_dbg_sta_open_htt_peer_stats, |
406 | .release = ath11k_dbg_sta_release_htt_peer_stats, |
407 | .read = ath11k_dbg_sta_read_htt_peer_stats, |
408 | .owner = THIS_MODULE, |
409 | .llseek = default_llseek, |
410 | }; |
411 | |
412 | static ssize_t ath11k_dbg_sta_write_peer_pktlog(struct file *file, |
413 | const char __user *buf, |
414 | size_t count, loff_t *ppos) |
415 | { |
416 | struct ieee80211_sta *sta = file->private_data; |
417 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
418 | struct ath11k *ar = arsta->arvif->ar; |
419 | int ret, enable; |
420 | |
421 | mutex_lock(&ar->conf_mutex); |
422 | |
423 | if (ar->state != ATH11K_STATE_ON) { |
424 | ret = -ENETDOWN; |
425 | goto out; |
426 | } |
427 | |
428 | ret = kstrtoint_from_user(s: buf, count, base: 0, res: &enable); |
429 | if (ret) |
430 | goto out; |
431 | |
432 | ar->debug.pktlog_peer_valid = enable; |
433 | memcpy(ar->debug.pktlog_peer_addr, sta->addr, ETH_ALEN); |
434 | |
435 | /* Send peer based pktlog enable/disable */ |
436 | ret = ath11k_wmi_pdev_peer_pktlog_filter(ar, addr: sta->addr, enable); |
437 | if (ret) { |
438 | ath11k_warn(ab: ar->ab, fmt: "failed to set peer pktlog filter %pM: %d\n" , |
439 | sta->addr, ret); |
440 | goto out; |
441 | } |
442 | |
443 | ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "peer pktlog filter set to %d\n" , |
444 | enable); |
445 | ret = count; |
446 | |
447 | out: |
448 | mutex_unlock(lock: &ar->conf_mutex); |
449 | return ret; |
450 | } |
451 | |
452 | static ssize_t ath11k_dbg_sta_read_peer_pktlog(struct file *file, |
453 | char __user *ubuf, |
454 | size_t count, loff_t *ppos) |
455 | { |
456 | struct ieee80211_sta *sta = file->private_data; |
457 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
458 | struct ath11k *ar = arsta->arvif->ar; |
459 | char buf[32] = {0}; |
460 | int len; |
461 | |
462 | mutex_lock(&ar->conf_mutex); |
463 | len = scnprintf(buf, size: sizeof(buf), fmt: "%08x %pM\n" , |
464 | ar->debug.pktlog_peer_valid, |
465 | ar->debug.pktlog_peer_addr); |
466 | mutex_unlock(lock: &ar->conf_mutex); |
467 | |
468 | return simple_read_from_buffer(to: ubuf, count, ppos, from: buf, available: len); |
469 | } |
470 | |
471 | static const struct file_operations fops_peer_pktlog = { |
472 | .write = ath11k_dbg_sta_write_peer_pktlog, |
473 | .read = ath11k_dbg_sta_read_peer_pktlog, |
474 | .open = simple_open, |
475 | .owner = THIS_MODULE, |
476 | .llseek = default_llseek, |
477 | }; |
478 | |
479 | static ssize_t ath11k_dbg_sta_write_delba(struct file *file, |
480 | const char __user *user_buf, |
481 | size_t count, loff_t *ppos) |
482 | { |
483 | struct ieee80211_sta *sta = file->private_data; |
484 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
485 | struct ath11k *ar = arsta->arvif->ar; |
486 | u32 tid, initiator, reason; |
487 | int ret; |
488 | char buf[64] = {0}; |
489 | |
490 | ret = simple_write_to_buffer(to: buf, available: sizeof(buf) - 1, ppos, |
491 | from: user_buf, count); |
492 | if (ret <= 0) |
493 | return ret; |
494 | |
495 | ret = sscanf(buf, "%u %u %u" , &tid, &initiator, &reason); |
496 | if (ret != 3) |
497 | return -EINVAL; |
498 | |
499 | /* Valid TID values are 0 through 15 */ |
500 | if (tid > HAL_DESC_REO_NON_QOS_TID - 1) |
501 | return -EINVAL; |
502 | |
503 | mutex_lock(&ar->conf_mutex); |
504 | if (ar->state != ATH11K_STATE_ON || |
505 | arsta->aggr_mode != ATH11K_DBG_AGGR_MODE_MANUAL) { |
506 | ret = count; |
507 | goto out; |
508 | } |
509 | |
510 | ret = ath11k_wmi_delba_send(ar, vdev_id: arsta->arvif->vdev_id, mac: sta->addr, |
511 | tid, initiator, reason); |
512 | if (ret) { |
513 | ath11k_warn(ab: ar->ab, fmt: "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n" , |
514 | arsta->arvif->vdev_id, sta->addr, tid, initiator, |
515 | reason); |
516 | } |
517 | ret = count; |
518 | out: |
519 | mutex_unlock(lock: &ar->conf_mutex); |
520 | return ret; |
521 | } |
522 | |
523 | static const struct file_operations fops_delba = { |
524 | .write = ath11k_dbg_sta_write_delba, |
525 | .open = simple_open, |
526 | .owner = THIS_MODULE, |
527 | .llseek = default_llseek, |
528 | }; |
529 | |
530 | static ssize_t ath11k_dbg_sta_write_addba_resp(struct file *file, |
531 | const char __user *user_buf, |
532 | size_t count, loff_t *ppos) |
533 | { |
534 | struct ieee80211_sta *sta = file->private_data; |
535 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
536 | struct ath11k *ar = arsta->arvif->ar; |
537 | u32 tid, status; |
538 | int ret; |
539 | char buf[64] = {0}; |
540 | |
541 | ret = simple_write_to_buffer(to: buf, available: sizeof(buf) - 1, ppos, |
542 | from: user_buf, count); |
543 | if (ret <= 0) |
544 | return ret; |
545 | |
546 | ret = sscanf(buf, "%u %u" , &tid, &status); |
547 | if (ret != 2) |
548 | return -EINVAL; |
549 | |
550 | /* Valid TID values are 0 through 15 */ |
551 | if (tid > HAL_DESC_REO_NON_QOS_TID - 1) |
552 | return -EINVAL; |
553 | |
554 | mutex_lock(&ar->conf_mutex); |
555 | if (ar->state != ATH11K_STATE_ON || |
556 | arsta->aggr_mode != ATH11K_DBG_AGGR_MODE_MANUAL) { |
557 | ret = count; |
558 | goto out; |
559 | } |
560 | |
561 | ret = ath11k_wmi_addba_set_resp(ar, vdev_id: arsta->arvif->vdev_id, mac: sta->addr, |
562 | tid, status); |
563 | if (ret) { |
564 | ath11k_warn(ab: ar->ab, fmt: "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n" , |
565 | arsta->arvif->vdev_id, sta->addr, tid, status); |
566 | } |
567 | ret = count; |
568 | out: |
569 | mutex_unlock(lock: &ar->conf_mutex); |
570 | return ret; |
571 | } |
572 | |
573 | static const struct file_operations fops_addba_resp = { |
574 | .write = ath11k_dbg_sta_write_addba_resp, |
575 | .open = simple_open, |
576 | .owner = THIS_MODULE, |
577 | .llseek = default_llseek, |
578 | }; |
579 | |
580 | static ssize_t ath11k_dbg_sta_write_addba(struct file *file, |
581 | const char __user *user_buf, |
582 | size_t count, loff_t *ppos) |
583 | { |
584 | struct ieee80211_sta *sta = file->private_data; |
585 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
586 | struct ath11k *ar = arsta->arvif->ar; |
587 | u32 tid, buf_size; |
588 | int ret; |
589 | char buf[64] = {0}; |
590 | |
591 | ret = simple_write_to_buffer(to: buf, available: sizeof(buf) - 1, ppos, |
592 | from: user_buf, count); |
593 | if (ret <= 0) |
594 | return ret; |
595 | |
596 | ret = sscanf(buf, "%u %u" , &tid, &buf_size); |
597 | if (ret != 2) |
598 | return -EINVAL; |
599 | |
600 | /* Valid TID values are 0 through 15 */ |
601 | if (tid > HAL_DESC_REO_NON_QOS_TID - 1) |
602 | return -EINVAL; |
603 | |
604 | mutex_lock(&ar->conf_mutex); |
605 | if (ar->state != ATH11K_STATE_ON || |
606 | arsta->aggr_mode != ATH11K_DBG_AGGR_MODE_MANUAL) { |
607 | ret = count; |
608 | goto out; |
609 | } |
610 | |
611 | ret = ath11k_wmi_addba_send(ar, vdev_id: arsta->arvif->vdev_id, mac: sta->addr, |
612 | tid, buf_size); |
613 | if (ret) { |
614 | ath11k_warn(ab: ar->ab, fmt: "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n" , |
615 | arsta->arvif->vdev_id, sta->addr, tid, buf_size); |
616 | } |
617 | |
618 | ret = count; |
619 | out: |
620 | mutex_unlock(lock: &ar->conf_mutex); |
621 | return ret; |
622 | } |
623 | |
624 | static const struct file_operations fops_addba = { |
625 | .write = ath11k_dbg_sta_write_addba, |
626 | .open = simple_open, |
627 | .owner = THIS_MODULE, |
628 | .llseek = default_llseek, |
629 | }; |
630 | |
631 | static ssize_t ath11k_dbg_sta_read_aggr_mode(struct file *file, |
632 | char __user *user_buf, |
633 | size_t count, loff_t *ppos) |
634 | { |
635 | struct ieee80211_sta *sta = file->private_data; |
636 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
637 | struct ath11k *ar = arsta->arvif->ar; |
638 | char buf[64]; |
639 | int len = 0; |
640 | |
641 | mutex_lock(&ar->conf_mutex); |
642 | len = scnprintf(buf, size: sizeof(buf) - len, |
643 | fmt: "aggregation mode: %s\n\n%s\n%s\n" , |
644 | (arsta->aggr_mode == ATH11K_DBG_AGGR_MODE_AUTO) ? |
645 | "auto" : "manual" , "auto = 0" , "manual = 1" ); |
646 | mutex_unlock(lock: &ar->conf_mutex); |
647 | |
648 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
649 | } |
650 | |
651 | static ssize_t ath11k_dbg_sta_write_aggr_mode(struct file *file, |
652 | const char __user *user_buf, |
653 | size_t count, loff_t *ppos) |
654 | { |
655 | struct ieee80211_sta *sta = file->private_data; |
656 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
657 | struct ath11k *ar = arsta->arvif->ar; |
658 | u32 aggr_mode; |
659 | int ret; |
660 | |
661 | if (kstrtouint_from_user(s: user_buf, count, base: 0, res: &aggr_mode)) |
662 | return -EINVAL; |
663 | |
664 | if (aggr_mode >= ATH11K_DBG_AGGR_MODE_MAX) |
665 | return -EINVAL; |
666 | |
667 | mutex_lock(&ar->conf_mutex); |
668 | if (ar->state != ATH11K_STATE_ON || |
669 | aggr_mode == arsta->aggr_mode) { |
670 | ret = count; |
671 | goto out; |
672 | } |
673 | |
674 | ret = ath11k_wmi_addba_clear_resp(ar, vdev_id: arsta->arvif->vdev_id, mac: sta->addr); |
675 | if (ret) { |
676 | ath11k_warn(ab: ar->ab, fmt: "failed to clear addba session ret: %d\n" , |
677 | ret); |
678 | goto out; |
679 | } |
680 | |
681 | arsta->aggr_mode = aggr_mode; |
682 | out: |
683 | mutex_unlock(lock: &ar->conf_mutex); |
684 | return ret; |
685 | } |
686 | |
687 | static const struct file_operations fops_aggr_mode = { |
688 | .read = ath11k_dbg_sta_read_aggr_mode, |
689 | .write = ath11k_dbg_sta_write_aggr_mode, |
690 | .open = simple_open, |
691 | .owner = THIS_MODULE, |
692 | .llseek = default_llseek, |
693 | }; |
694 | |
695 | static ssize_t |
696 | ath11k_write_htt_peer_stats_reset(struct file *file, |
697 | const char __user *user_buf, |
698 | size_t count, loff_t *ppos) |
699 | { |
700 | struct ieee80211_sta *sta = file->private_data; |
701 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
702 | struct ath11k *ar = arsta->arvif->ar; |
703 | struct htt_ext_stats_cfg_params cfg_params = { 0 }; |
704 | int ret; |
705 | u8 type; |
706 | |
707 | ret = kstrtou8_from_user(s: user_buf, count, base: 0, res: &type); |
708 | if (ret) |
709 | return ret; |
710 | |
711 | if (!type) |
712 | return ret; |
713 | |
714 | mutex_lock(&ar->conf_mutex); |
715 | cfg_params.cfg0 = HTT_STAT_PEER_INFO_MAC_ADDR; |
716 | cfg_params.cfg0 |= FIELD_PREP(GENMASK(15, 1), |
717 | HTT_PEER_STATS_REQ_MODE_FLUSH_TQM); |
718 | |
719 | cfg_params.cfg1 = HTT_STAT_DEFAULT_PEER_REQ_TYPE; |
720 | |
721 | cfg_params.cfg2 |= FIELD_PREP(GENMASK(7, 0), sta->addr[0]); |
722 | cfg_params.cfg2 |= FIELD_PREP(GENMASK(15, 8), sta->addr[1]); |
723 | cfg_params.cfg2 |= FIELD_PREP(GENMASK(23, 16), sta->addr[2]); |
724 | cfg_params.cfg2 |= FIELD_PREP(GENMASK(31, 24), sta->addr[3]); |
725 | |
726 | cfg_params.cfg3 |= FIELD_PREP(GENMASK(7, 0), sta->addr[4]); |
727 | cfg_params.cfg3 |= FIELD_PREP(GENMASK(15, 8), sta->addr[5]); |
728 | |
729 | cfg_params.cfg3 |= ATH11K_HTT_PEER_STATS_RESET; |
730 | |
731 | ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, |
732 | type: ATH11K_DBG_HTT_EXT_STATS_PEER_INFO, |
733 | cfg_params: &cfg_params, |
734 | cookie: 0ULL); |
735 | if (ret) { |
736 | ath11k_warn(ab: ar->ab, fmt: "failed to send htt peer stats request: %d\n" , ret); |
737 | mutex_unlock(lock: &ar->conf_mutex); |
738 | return ret; |
739 | } |
740 | |
741 | mutex_unlock(lock: &ar->conf_mutex); |
742 | |
743 | ret = count; |
744 | |
745 | return ret; |
746 | } |
747 | |
748 | static const struct file_operations fops_htt_peer_stats_reset = { |
749 | .write = ath11k_write_htt_peer_stats_reset, |
750 | .open = simple_open, |
751 | .owner = THIS_MODULE, |
752 | .llseek = default_llseek, |
753 | }; |
754 | |
755 | static ssize_t ath11k_dbg_sta_read_peer_ps_state(struct file *file, |
756 | char __user *user_buf, |
757 | size_t count, loff_t *ppos) |
758 | { |
759 | struct ieee80211_sta *sta = file->private_data; |
760 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
761 | struct ath11k *ar = arsta->arvif->ar; |
762 | char buf[20]; |
763 | int len; |
764 | |
765 | spin_lock_bh(lock: &ar->data_lock); |
766 | |
767 | len = scnprintf(buf, size: sizeof(buf), fmt: "%d\n" , arsta->peer_ps_state); |
768 | |
769 | spin_unlock_bh(lock: &ar->data_lock); |
770 | |
771 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
772 | } |
773 | |
774 | static const struct file_operations fops_peer_ps_state = { |
775 | .open = simple_open, |
776 | .read = ath11k_dbg_sta_read_peer_ps_state, |
777 | .owner = THIS_MODULE, |
778 | .llseek = default_llseek, |
779 | }; |
780 | |
781 | static ssize_t ath11k_dbg_sta_read_current_ps_duration(struct file *file, |
782 | char __user *user_buf, |
783 | size_t count, |
784 | loff_t *ppos) |
785 | { |
786 | struct ieee80211_sta *sta = file->private_data; |
787 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
788 | struct ath11k *ar = arsta->arvif->ar; |
789 | u64 time_since_station_in_power_save; |
790 | char buf[20]; |
791 | int len; |
792 | |
793 | spin_lock_bh(lock: &ar->data_lock); |
794 | |
795 | if (arsta->peer_ps_state == WMI_PEER_PS_STATE_ON && |
796 | arsta->peer_current_ps_valid) |
797 | time_since_station_in_power_save = jiffies_to_msecs(j: jiffies |
798 | - arsta->ps_start_jiffies); |
799 | else |
800 | time_since_station_in_power_save = 0; |
801 | |
802 | len = scnprintf(buf, size: sizeof(buf), fmt: "%llu\n" , |
803 | time_since_station_in_power_save); |
804 | spin_unlock_bh(lock: &ar->data_lock); |
805 | |
806 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
807 | } |
808 | |
809 | static const struct file_operations fops_current_ps_duration = { |
810 | .open = simple_open, |
811 | .read = ath11k_dbg_sta_read_current_ps_duration, |
812 | .owner = THIS_MODULE, |
813 | .llseek = default_llseek, |
814 | }; |
815 | |
816 | static ssize_t ath11k_dbg_sta_read_total_ps_duration(struct file *file, |
817 | char __user *user_buf, |
818 | size_t count, loff_t *ppos) |
819 | { |
820 | struct ieee80211_sta *sta = file->private_data; |
821 | struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); |
822 | struct ath11k *ar = arsta->arvif->ar; |
823 | char buf[20]; |
824 | u64 power_save_duration; |
825 | int len; |
826 | |
827 | spin_lock_bh(lock: &ar->data_lock); |
828 | |
829 | if (arsta->peer_ps_state == WMI_PEER_PS_STATE_ON && |
830 | arsta->peer_current_ps_valid) |
831 | power_save_duration = jiffies_to_msecs(j: jiffies |
832 | - arsta->ps_start_jiffies) |
833 | + arsta->ps_total_duration; |
834 | else |
835 | power_save_duration = arsta->ps_total_duration; |
836 | |
837 | len = scnprintf(buf, size: sizeof(buf), fmt: "%llu\n" , power_save_duration); |
838 | |
839 | spin_unlock_bh(lock: &ar->data_lock); |
840 | |
841 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
842 | } |
843 | |
844 | static const struct file_operations fops_total_ps_duration = { |
845 | .open = simple_open, |
846 | .read = ath11k_dbg_sta_read_total_ps_duration, |
847 | .owner = THIS_MODULE, |
848 | .llseek = default_llseek, |
849 | }; |
850 | |
851 | void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
852 | struct ieee80211_sta *sta, struct dentry *dir) |
853 | { |
854 | struct ath11k *ar = hw->priv; |
855 | |
856 | if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) |
857 | debugfs_create_file(name: "tx_stats" , mode: 0400, parent: dir, data: sta, |
858 | fops: &fops_tx_stats); |
859 | if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) |
860 | debugfs_create_file(name: "rx_stats" , mode: 0400, parent: dir, data: sta, |
861 | fops: &fops_rx_stats); |
862 | |
863 | debugfs_create_file(name: "htt_peer_stats" , mode: 0400, parent: dir, data: sta, |
864 | fops: &fops_htt_peer_stats); |
865 | |
866 | debugfs_create_file(name: "peer_pktlog" , mode: 0644, parent: dir, data: sta, |
867 | fops: &fops_peer_pktlog); |
868 | |
869 | debugfs_create_file(name: "aggr_mode" , mode: 0644, parent: dir, data: sta, fops: &fops_aggr_mode); |
870 | debugfs_create_file(name: "addba" , mode: 0200, parent: dir, data: sta, fops: &fops_addba); |
871 | debugfs_create_file(name: "addba_resp" , mode: 0200, parent: dir, data: sta, fops: &fops_addba_resp); |
872 | debugfs_create_file(name: "delba" , mode: 0200, parent: dir, data: sta, fops: &fops_delba); |
873 | |
874 | if (test_bit(WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET, |
875 | ar->ab->wmi_ab.svc_map)) |
876 | debugfs_create_file(name: "htt_peer_stats_reset" , mode: 0600, parent: dir, data: sta, |
877 | fops: &fops_htt_peer_stats_reset); |
878 | |
879 | debugfs_create_file(name: "peer_ps_state" , mode: 0400, parent: dir, data: sta, |
880 | fops: &fops_peer_ps_state); |
881 | |
882 | if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT, |
883 | ar->ab->wmi_ab.svc_map)) { |
884 | debugfs_create_file(name: "current_ps_duration" , mode: 0440, parent: dir, data: sta, |
885 | fops: &fops_current_ps_duration); |
886 | debugfs_create_file(name: "total_ps_duration" , mode: 0440, parent: dir, data: sta, |
887 | fops: &fops_total_ps_duration); |
888 | } |
889 | } |
890 | |