blob: 6d82e38f7925621f62746e0484c2ce1f9149730f [file] [log] [blame]
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -08001/*
2 * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/**
23 * DOC: wlan_hdd_debugfs_llstat.c
24 *
25 * WLAN Host Device Driver implementation to update
26 * debugfs with Link Layer statistics
27 */
28
29#include <wlan_hdd_debugfs_llstat.h>
30#include <cds_sched.h>
31#include <wlan_hdd_stats.h>
32#include <wma_api.h>
33
34struct ll_stats_buf {
35 ssize_t len;
36 uint8_t *result;
37};
38
39static struct ll_stats_buf ll_stats;
40
Jeff Johnson38a70f92017-08-29 14:37:43 -070041void hdd_debugfs_process_iface_stats(struct hdd_adapter *adapter,
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -080042 void *data, uint32_t num_peers)
43{
44 tpSirWifiIfaceStat iface_stat;
45 tpSirWifiInterfaceInfo iface_info;
46 tpSirWifiWmmAcStat ac_stat;
47 struct wifi_iface_offload_stat *offload_stat;
48 uint64_t average_tsf_offset;
49 int i;
50 ssize_t len = 0;
51 uint8_t *buffer;
52
53 ENTER();
54 iface_stat = data;
55
56 buffer = ll_stats.result;
57 buffer += ll_stats.len;
58 len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
59 "\n\n===LL_STATS_IFACE: num_peers: %d===", num_peers);
60
61 if (false == hdd_get_interface_info(adapter, &iface_stat->info)) {
62 hdd_err("hdd_get_interface_info get fail");
63 return;
64 }
65
66 iface_info = &iface_stat->info;
67 buffer += len;
68 ll_stats.len += len;
69 len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
70 "\nmode: %u, MAC_ADDR: %pM, state: %u, roaming: %u, capabilities: %u, SSID: %s, BSSID_MAC: %pM, ap_country_str: %s, country_str: %s",
71 iface_info->mode, &iface_info->macAddr.bytes[0],
72 iface_info->state, iface_info->roaming,
73 iface_info->capabilities, iface_info->ssid,
74 &iface_info->bssid.bytes[0], iface_info->apCountryStr,
75 iface_info->countryStr);
76
77 average_tsf_offset = iface_stat->avg_bcn_spread_offset_high;
78 average_tsf_offset = (average_tsf_offset << 32) |
79 iface_stat->avg_bcn_spread_offset_low;
80
81 buffer += len;
82 ll_stats.len += len;
83 len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
84 "\nbeacon_rx: %u, mgmt_rx: %u, mgmt_action_rx: %u, mgmt_action_tx: %u, rssi_mgmt: %u, rssi_data: %u, rssi_ack: %u, is_leaky_ap: %u, avg_rx_frms_leaked: %u, rx_leak_window: %u, average_tsf_offset: %llu, Tx RTS success count: %u, Tx RTS fail count: %u, Tx ppdu success count: %u, Tx ppdu fail count: %u, Connected duration: %u, Disconnected duration: %u, RTT ranging duration: %u, RTT responder duration: %u, Num tx probes: %u, Num beacon miss: %u,\n\nNumber of AC: %d",
85 iface_stat->beaconRx, iface_stat->mgmtRx,
86 iface_stat->mgmtActionRx, iface_stat->mgmtActionTx,
87 iface_stat->rssiMgmt, iface_stat->rssiData,
88 iface_stat->rssiAck, iface_stat->is_leaky_ap,
89 iface_stat->avg_rx_frms_leaked,
90 iface_stat->rx_leak_window, average_tsf_offset,
91 iface_stat->tx_rts_succ_cnt,
92 iface_stat->tx_rts_fail_cnt,
93 iface_stat->tx_ppdu_succ_cnt,
94 iface_stat->tx_ppdu_fail_cnt,
95 iface_stat->connected_duration,
96 iface_stat->disconnected_duration,
97 iface_stat->rtt_ranging_duration,
98 iface_stat->rtt_responder_duration,
99 iface_stat->num_probes_tx, iface_stat->num_beacon_miss,
100 iface_stat->num_ac);
101
102 for (i = 0; i < iface_stat->num_ac; i++) {
103 ac_stat = &iface_stat->AccessclassStats[i];
104 buffer += len;
105 ll_stats.len += len;
106 len = scnprintf(buffer,
107 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
108 "\nAC: %d, tx_mpdu: %u, rx_mpdu: %u, tx_mcast: %u, rx_mcast: %u, rx_ampdu: %u tx_ampdu: %u, mpdu_lost: %u, retries: %u, retries_short: %u, retries_long: %u, contention_time: min-%u max-%u avg-%u, contention num samples: %u",
109 ac_stat->ac, ac_stat->txMpdu, ac_stat->rxMpdu,
110 ac_stat->txMcast, ac_stat->rxMcast,
111 ac_stat->rxAmpdu, ac_stat->txAmpdu,
112 ac_stat->mpduLost, ac_stat->retries,
113 ac_stat->retriesShort, ac_stat->retriesLong,
114 ac_stat->contentionTimeMin,
115 ac_stat->contentionTimeMax,
116 ac_stat->contentionTimeAvg,
117 ac_stat->contentionNumSamples);
118 }
119
120 buffer += len;
121 ll_stats.len += len;
122 len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
123 "\n\nNumber of offload stats: %d",
124 iface_stat->num_offload_stats);
125
126 for (i = 0; i < iface_stat->num_offload_stats; i++) {
127 offload_stat = &iface_stat->offload_stat[i];
128 buffer += len;
129 ll_stats.len += len;
130 len = scnprintf(buffer,
131 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
132 "\ntype: %d, rx_count: %u, drp_count: %u, fwd_count: %u",
133 offload_stat->type, offload_stat->rx_count,
134 offload_stat->drp_count,
135 offload_stat->fwd_count);
136 }
137
138 ll_stats.len += len;
139 EXIT();
140}
141
Jeff Johnson38a70f92017-08-29 14:37:43 -0700142void hdd_debugfs_process_peer_stats(struct hdd_adapter *adapter, void *data)
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800143{
144 tpSirWifiPeerStat peer_stat;
145 tpSirWifiPeerInfo peer_info;
146 tpSirWifiRateStat rate_stat;
147 int i, j, num_rate;
148 ssize_t len = 0;
149 uint8_t *buffer;
150
151 ENTER();
152
153 if (!ll_stats.result) {
154 hdd_err("LL statistics buffer is NULL");
155 return;
156 }
157
158 peer_stat = (tpSirWifiPeerStat) data;
159
160 buffer = ll_stats.result;
161 buffer += ll_stats.len;
162 len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
163 "\n\n===LL_STATS_PEER_ALL : num_peers %u===",
164 peer_stat->numPeers);
165
166 peer_info = (tpSirWifiPeerInfo) ((uint8_t *) peer_stat->peerInfo);
167 for (i = 1; i <= peer_stat->numPeers; i++) {
168 buffer += len;
169 ll_stats.len += len;
170 len = scnprintf(buffer,
171 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
172 "\nType: %d, peer_mac: %pM, capabilities: %u\nnum_rates: %d",
173 wmi_to_sir_peer_type(peer_info->type),
174 &peer_info->peerMacAddress.bytes[0],
175 peer_info->capabilities, peer_info->numRate);
176
177 num_rate = peer_info->numRate;
178 for (j = 0; j < num_rate; j++) {
179 rate_stat = (tpSirWifiRateStat) ((uint8_t *)
180 peer_info->rateStats + (j *
181 sizeof(tSirWifiRateStat)));
182 buffer += len;
183 ll_stats.len += len;
184 len = scnprintf(buffer,
185 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
186 "\npreamble: %0x, nss: %0x, bw: %0x, mcs: %0x, bitrate: %0x, txmpdu: %u, rxmpdu: %u, mpdu_lost: %u, retries: %u, retries_short: %u, retries_long: %u",
187 rate_stat->rate.preamble, rate_stat->rate.nss,
188 rate_stat->rate.bw, rate_stat->rate.rateMcsIdx,
189 rate_stat->rate.bitrate, rate_stat->txMpdu,
190 rate_stat->rxMpdu, rate_stat->mpduLost,
191 rate_stat->retries, rate_stat->retriesShort,
192 rate_stat->retriesLong);
193 }
194 peer_info = (tpSirWifiPeerInfo) ((uint8_t *)
195 peer_stat->peerInfo + (i *
196 sizeof(tSirWifiPeerInfo)) +
197 (num_rate * sizeof(tSirWifiRateStat)));
198 }
199 ll_stats.len += len;
200 EXIT();
201
202}
203
Jeff Johnson38a70f92017-08-29 14:37:43 -0700204void hdd_debugfs_process_radio_stats(struct hdd_adapter *adapter,
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800205 uint32_t more_data, void *data, uint32_t num_radio)
206{
207 int i, j;
208 ssize_t len = 0;
209 uint8_t *buffer;
210 tSirWifiRadioStat *radio_stat = (tpSirWifiRadioStat) data;
211 tSirWifiChannelStats *chan_stat;
212
213 ENTER();
214
215 if (!ll_stats.result) {
216 hdd_err("LL statistics buffer is NULL");
217 return;
218 }
219
220 buffer = ll_stats.result;
221 buffer += ll_stats.len;
222 len = scnprintf(buffer, DEBUGFS_LLSTATS_BUF_SIZE,
223 "\n\n===LL_STATS_RADIO: number of radios: %u===",
224 num_radio);
225
226 for (i = 0; i < num_radio; i++) {
227 buffer += len;
228 ll_stats.len += len;
229 len = scnprintf(buffer,
230 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
231 "\nRadio: %u on_time: %u, tx_time: %u, rx_time: %u, on_time_scan: %u, on_time_nbd: %u, on_time_gscan: %u, on_time_roam_scan: %u, on_time_pno_scan: %u on_time_hs20: %u, on_time_host_scan: %u, on_time_lpi_scan: %u\ntotal_num_tx_pwr_levels: %u\n",
232 radio_stat->radio, radio_stat->onTime,
233 radio_stat->txTime, radio_stat->rxTime,
234 radio_stat->onTimeScan, radio_stat->onTimeNbd,
235 radio_stat->onTimeGscan,
236 radio_stat->onTimeRoamScan,
237 radio_stat->onTimePnoScan,
238 radio_stat->onTimeHs20,
239 radio_stat->on_time_host_scan,
240 radio_stat->on_time_lpi_scan,
241 radio_stat->total_num_tx_power_levels);
242
243 for (j = 0; j < radio_stat->total_num_tx_power_levels; j++) {
244 buffer += len;
245 ll_stats.len += len;
246 len = scnprintf(buffer,
247 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
248 "%d ", radio_stat->tx_time_per_power_level[j]);
249 }
250
251 buffer += len;
252 ll_stats.len += len;
253 len = scnprintf(buffer,
254 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
255 "\nNum channels: %d", radio_stat->numChannels);
256
257 for (j = 0; j < radio_stat->numChannels; j++) {
258 chan_stat = (tSirWifiChannelStats *)
259 ((uint8_t *)radio_stat->channels +
260 (j * sizeof(tSirWifiChannelStats)));
261
262 buffer += len;
263 ll_stats.len += len;
264 len = scnprintf(buffer,
265 DEBUGFS_LLSTATS_BUF_SIZE - ll_stats.len,
266 "\nChan width: %d, center_freq: %d, center_freq0: %d, center_freq1: %d, on_time: %d, cca_busy_time: %d",
267 chan_stat->channel.width,
268 chan_stat->channel.centerFreq,
269 chan_stat->channel.centerFreq0,
270 chan_stat->channel.centerFreq1,
271 chan_stat->onTime, chan_stat->ccaBusyTime);
272 }
273
274 radio_stat++;
275 }
276 ll_stats.len += len;
277 EXIT();
278}
279
280static inline void wlan_hdd_llstats_free_buf(void)
281{
282 qdf_mem_free(ll_stats.result);
283 ll_stats.result = NULL;
284 ll_stats.len = 0;
285}
286
287static int wlan_hdd_llstats_alloc_buf(void)
288{
289 ll_stats.len = 0;
290
291 ll_stats.result = qdf_mem_malloc(DEBUGFS_LLSTATS_BUF_SIZE);
292 if (!ll_stats.result) {
293 hdd_err("LL Stats buffer allocation failed");
294 return -EINVAL;
295 }
296
297 return 0;
298}
299
300/**
301 * hdd_debugfs_stats_update() - Update userspace with local statistics buffer
302 * @buf: userspace buffer (to which data is being copied into)
303 * @count: max data that can be copied into buf
304 * @pos: offset (where data should be copied into)
305 *
306 * This function should copies link layer statistics buffer into debugfs
307 * entry.
308 *
309 * Return: number of characters copied; 0 on no-copy
310 */
311static ssize_t hdd_debugfs_stats_update(char __user *buf, size_t count,
312 loff_t *pos)
313{
314 ssize_t ret_cnt;
315
316 ENTER();
317
318 if (!ll_stats.result) {
319 hdd_err("Trying to read from NULL buffer");
320 return 0;
321 }
322
323 hdd_info("LL stats read req: count: %zu, pos: %lld", count, *pos);
324 ret_cnt = simple_read_from_buffer(buf, count, pos,
325 ll_stats.result, ll_stats.len);
326
327 EXIT();
328 return ret_cnt;
329}
330
331/**
332 * __wlan_hdd_read_ll_stats_debugfs() - API to collect LL stats from FW
333 * @file: file pointer
334 * @buf: buffer
335 * @count: count
336 * @pos: position pointer
337 *
338 * Return: Number of bytes read on success, error number otherwise
339 */
340static ssize_t __wlan_hdd_read_ll_stats_debugfs(struct file *file,
341 char __user *buf, size_t count, loff_t *pos)
342{
Jeff Johnson38a70f92017-08-29 14:37:43 -0700343 struct hdd_adapter *adapter;
Jeff Johnson03f8f362017-08-28 12:04:24 -0700344 struct hdd_context *hdd_ctx;
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800345 ssize_t ret = 0;
346
347 ENTER();
348
Jeff Johnson38a70f92017-08-29 14:37:43 -0700349 adapter = (struct hdd_adapter *)file->private_data;
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800350 if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
351 hdd_err("Invalid adapter or adapter has invalid magic");
352 return -EINVAL;
353 }
354
355 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
356 ret = wlan_hdd_validate_context(hdd_ctx);
357 if (0 != ret)
358 return ret;
359
360 /* All the events are received and buffer is populated */
361 ret = hdd_debugfs_stats_update(buf, count, pos);
362 hdd_info("%zu characters written into debugfs", ret);
363
364 EXIT();
365 return ret;
366
367}
368
369/**
370 * wlan_hdd_read_ll_stats_debugfs() - SSR wrapper function to read LL debugfs
371 * @file: file pointer
372 * @buf: buffer
373 * @count: count
374 * @pos: position pointer
375 *
376 * Return: Number of bytes read on success, error number otherwise
377 */
378static ssize_t wlan_hdd_read_ll_stats_debugfs(struct file *file,
379 char __user *buf,
380 size_t count, loff_t *pos)
381{
382 int ret;
383
384 cds_ssr_protect(__func__);
385 ret = __wlan_hdd_read_ll_stats_debugfs(file, buf, count, pos);
386 cds_ssr_unprotect(__func__);
387
388 return ret;
389}
390
391/**
392 * __wlan_hdd_open_ll_stats_debugfs() - Function to save private on open
393 * @inode: Pointer to inode structure
394 * @file: file pointer
395 *
396 * Return: zero
397 */
398static int __wlan_hdd_open_ll_stats_debugfs(struct inode *inode,
399 struct file *file)
400{
Jeff Johnson38a70f92017-08-29 14:37:43 -0700401 struct hdd_adapter *adapter;
Jeff Johnson03f8f362017-08-28 12:04:24 -0700402 struct hdd_context *hdd_ctx;
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800403 int ret;
404
405 ENTER();
406
407 if (inode->i_private)
408 file->private_data = inode->i_private;
409
Jeff Johnson38a70f92017-08-29 14:37:43 -0700410 adapter = (struct hdd_adapter *)file->private_data;
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800411 if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
412 hdd_err("Invalid adapter or adapter has invalid magic");
413 return -EINVAL;
414 }
415
416 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
417 ret = wlan_hdd_validate_context(hdd_ctx);
418 if (0 != ret)
419 return ret;
420
421 ret = wlan_hdd_llstats_alloc_buf();
422 if (0 != ret)
423 return ret;
424
425 ret = wlan_hdd_ll_stats_get(adapter, DEBUGFS_LLSTATS_REQID,
426 DEBUGFS_LLSTATS_REQMASK);
427 if (0 != ret)
428 return ret;
429
430 EXIT();
431 return 0;
432}
433
434
435/**
436 * wlan_hdd_open_ll_stats_debugfs() - SSR wrapper function to save private
437 * on open
438 * @inode: Pointer to inode structure
439 * @file: file pointer
440 *
441 * Return: zero
442 */
443static int wlan_hdd_open_ll_stats_debugfs(struct inode *inode,
444 struct file *file)
445{
446 int ret;
447
448 cds_ssr_protect(__func__);
449 ret = __wlan_hdd_open_ll_stats_debugfs(inode, file);
450 cds_ssr_unprotect(__func__);
451
452 return ret;
453}
454
455/**
456 * __wlan_hdd_release_ll_stats_debugfs() - Function to save private on release
457 * @inode: Pointer to inode structure
458 * @file: file pointer
459 *
460 * Return: zero
461 */
462static int __wlan_hdd_release_ll_stats_debugfs(struct inode *inode,
463 struct file *file)
464{
Jeff Johnson38a70f92017-08-29 14:37:43 -0700465 struct hdd_adapter *adapter;
Jeff Johnson03f8f362017-08-28 12:04:24 -0700466 struct hdd_context *hdd_ctx;
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800467 int ret;
468
469 ENTER();
470
471 if (inode->i_private)
472 file->private_data = inode->i_private;
473
Jeff Johnson38a70f92017-08-29 14:37:43 -0700474 adapter = (struct hdd_adapter *)file->private_data;
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800475 if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
476 hdd_err("Invalid adapter or adapter has invalid magic");
477 return -EINVAL;
478 }
479
480 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
481 ret = wlan_hdd_validate_context(hdd_ctx);
482 if (0 != ret)
483 return ret;
484
485 wlan_hdd_llstats_free_buf();
486
487 EXIT();
488 return 0;
489}
490
491/**
492 * wlan_hdd_release_ll_stats_debugfs() - SSR wrapper function to save private
493 * on release
494 * @inode: Pointer to inode structure
495 * @file: file pointer
496 *
497 * Return: zero
498 */
499static int wlan_hdd_release_ll_stats_debugfs(struct inode *inode,
500 struct file *file)
501{
502 int ret;
503
504 cds_ssr_protect(__func__);
505 ret = __wlan_hdd_release_ll_stats_debugfs(inode, file);
506 cds_ssr_unprotect(__func__);
507
508 return ret;
509}
510
511static const struct file_operations fops_ll_stats_debugfs = {
512 .read = wlan_hdd_read_ll_stats_debugfs,
513 .open = wlan_hdd_open_ll_stats_debugfs,
514 .release = wlan_hdd_release_ll_stats_debugfs,
515 .owner = THIS_MODULE,
516 .llseek = default_llseek,
517};
518
Jeff Johnson38a70f92017-08-29 14:37:43 -0700519int wlan_hdd_create_ll_stats_file(struct hdd_adapter *adapter)
Krishna Kumaar Natarajanc7e2bb72017-03-10 10:38:46 -0800520{
521 if (!debugfs_create_file("ll_stats", 0444, adapter->debugfs_phy,
522 adapter, &fops_ll_stats_debugfs))
523 return -EINVAL;
524
525 return 0;
526}