1 /*
2 * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: wlan_hdd_debugfs_roam.c
22 *
23 * WLAN Host Device Driver implementation to update
24 * debugfs with roaming information
25 */
26
27 #include <wlan_hdd_debugfs_csr.h>
28 #include <wlan_hdd_main.h>
29 #include <cds_sched.h>
30 #include <wma_api.h>
31 #include "qwlan_version.h"
32 #include "wmi_unified_param.h"
33 #include "wlan_osif_request_manager.h"
34
35 /**
36 * hdd_roam_scan_stats_debugfs_dealloc() - Dealloc objects in hdd request mgr
37 * @priv: Pointer to private data of hdd request object
38 *
39 * Return: None
40 */
hdd_roam_scan_stats_debugfs_dealloc(void * priv)41 static void hdd_roam_scan_stats_debugfs_dealloc(void *priv)
42 {
43 struct hdd_roam_scan_stats_debugfs_priv *local_priv = priv;
44 struct wmi_roam_scan_stats_res *roam_scan_stats_res;
45
46 if (!local_priv)
47 return;
48
49 roam_scan_stats_res = local_priv->roam_scan_stats_res;
50 local_priv->roam_scan_stats_res = NULL;
51
52 qdf_mem_free(roam_scan_stats_res);
53 }
54
55 /**
56 * hdd_roam_scan_stats_cb() - Call back invoked from roam scan stats evt
57 * @context: cookie to get request object
58 * @res: roam scan stats response from firmware
59 *
60 * Return: None
61 */
62 static void
hdd_roam_scan_stats_cb(void * context,struct wmi_roam_scan_stats_res * res)63 hdd_roam_scan_stats_cb(void *context, struct wmi_roam_scan_stats_res *res)
64 {
65 struct osif_request *request;
66 struct hdd_roam_scan_stats_debugfs_priv *priv;
67 struct wmi_roam_scan_stats_res *stats_res;
68 uint32_t total_len;
69
70 hdd_enter();
71
72 request = osif_request_get(context);
73 if (!request) {
74 hdd_err("Obsolete roam scan stats request");
75 return;
76 }
77
78 if (!res) {
79 hdd_err("Invalid response");
80 goto end;
81 }
82
83 priv = osif_request_priv(request);
84
85 total_len = sizeof(*res) + res->num_roam_scans *
86 sizeof(struct wmi_roam_scan_stats_params);
87
88 stats_res = qdf_mem_malloc(total_len);
89 if (!stats_res)
90 goto end;
91
92 qdf_mem_copy(stats_res, res, total_len);
93 priv->roam_scan_stats_res = stats_res;
94
95 end:
96 osif_request_complete(request);
97 osif_request_put(request);
98
99 hdd_exit();
100 }
101
102 /**
103 * hdd_get_roam_scan_stats() - Get roam scan stats using request manager
104 * @hdd_ctx: hdd context
105 * @adapter: pointer to adapter
106 *
107 * Return: Pointer to struct wmi_roam_scan_stats_res which contains response
108 * from firmware
109 */
110 static struct
hdd_get_roam_scan_stats(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter)111 wmi_roam_scan_stats_res *hdd_get_roam_scan_stats(struct hdd_context *hdd_ctx,
112 struct hdd_adapter *adapter)
113 {
114 struct wmi_roam_scan_stats_res *res;
115 struct wmi_roam_scan_stats_res *stats_res = NULL;
116 void *context;
117 struct osif_request *request;
118 struct hdd_roam_scan_stats_debugfs_priv *priv;
119 static const struct osif_request_params params = {
120 .priv_size = sizeof(*priv),
121 .timeout_ms = WLAN_WAIT_TIME_FW_ROAM_STATS,
122 .dealloc = hdd_roam_scan_stats_debugfs_dealloc,
123 };
124 QDF_STATUS status;
125 int ret;
126 uint32_t total_len;
127
128 hdd_enter();
129
130 if (!wlan_hdd_validate_modules_state(hdd_ctx))
131 return NULL;
132
133 request = osif_request_alloc(¶ms);
134 if (!request) {
135 hdd_err("Request allocation failure");
136 return NULL;
137 }
138
139 context = osif_request_cookie(request);
140
141 status = sme_get_roam_scan_stats(hdd_ctx->mac_handle,
142 hdd_roam_scan_stats_cb,
143 context, adapter->deflink->vdev_id);
144 if (!QDF_IS_STATUS_SUCCESS(status)) {
145 hdd_err("roam scan stats request failed");
146 goto cleanup;
147 }
148
149 ret = osif_request_wait_for_response(request);
150 if (ret) {
151 hdd_err("roam scan stats response time out");
152 goto cleanup;
153 }
154
155 priv = osif_request_priv(request);
156 res = priv->roam_scan_stats_res;
157 if (!res) {
158 hdd_err("Failure of roam scan stats response retrieval");
159 goto cleanup;
160 }
161
162 total_len = sizeof(*res) + res->num_roam_scans *
163 sizeof(struct wmi_roam_scan_stats_params);
164
165 stats_res = qdf_mem_malloc(total_len);
166 if (!stats_res)
167 goto cleanup;
168
169 qdf_mem_copy(stats_res, res, total_len);
170
171 cleanup:
172 osif_request_put(request);
173 hdd_exit();
174
175 return stats_res;
176 }
177
178 /**
179 * hdd_roam_scan_trigger_to_str() - Get string for
180 * enum WMI_ROAM_TRIGGER_REASON_ID
181 * @roam_scan_trigger: roam scan trigger ID
182 *
183 * Return: Meaningful string from enum WMI_ROAM_TRIGGER_REASON_ID
184 */
hdd_roam_scan_trigger_to_str(uint32_t roam_scan_trigger)185 static char *hdd_roam_scan_trigger_to_str(uint32_t roam_scan_trigger)
186 {
187 switch (roam_scan_trigger) {
188 case WMI_ROAM_TRIGGER_REASON_PER:
189 return "PER";
190 case WMI_ROAM_TRIGGER_REASON_BMISS:
191 return "BEACON MISS";
192 case WMI_ROAM_TRIGGER_REASON_LOW_RSSI:
193 return "LOW RSSI";
194 case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI:
195 return "HIGH RSSI";
196 case WMI_ROAM_TRIGGER_REASON_PERIODIC:
197 return "PERIODIC SCAN";
198 case WMI_ROAM_TRIGGER_REASON_MAWC:
199 return "WMI_ROAM_TRIGGER_REASON_MAWC";
200 case WMI_ROAM_TRIGGER_REASON_DENSE:
201 return "DENSE ENVIRONMENT";
202 case WMI_ROAM_TRIGGER_REASON_BACKGROUND:
203 return "BACKGROUND SCAN";
204 case WMI_ROAM_TRIGGER_REASON_FORCED:
205 return "FORCED SCAN";
206 case WMI_ROAM_TRIGGER_REASON_BTM:
207 return "BTM TRIGGER";
208 case WMI_ROAM_TRIGGER_REASON_UNIT_TEST:
209 return "TEST COMMAND";
210 default:
211 return "UNKNOWN REASON";
212 }
213 return "UNKNOWN REASON";
214 }
215
216 /**
217 * hdd_roam_scan_trigger_value() - Get trigger value string for
218 * enum WMI_ROAM_TRIGGER_REASON_ID
219 * @roam_scan_trigger: roam scan trigger ID
220 * @print: output pointer to hold whether to print trigger value
221 *
222 * Return: Meaningful string from trigger value
223 */
hdd_roam_scan_trigger_value(uint32_t roam_scan_trigger,bool * print)224 static char *hdd_roam_scan_trigger_value(uint32_t roam_scan_trigger,
225 bool *print)
226 {
227 *print = true;
228
229 switch (roam_scan_trigger) {
230 case WMI_ROAM_TRIGGER_REASON_PER:
231 return "percentage";
232 case WMI_ROAM_TRIGGER_REASON_LOW_RSSI:
233 return "dB";
234 case WMI_ROAM_TRIGGER_REASON_HIGH_RSSI:
235 return "dB";
236 case WMI_ROAM_TRIGGER_REASON_PERIODIC:
237 return "ms";
238 case WMI_ROAM_TRIGGER_REASON_DENSE:
239 return "(1 - Rx, 2 - Tx)";
240 default:
241 *print = false;
242 return NULL;
243 }
244 }
245
246 /**
247 * hdd_client_id_to_str() - Helper func to get meaningful string from client id
248 * @client_id: Id of the client which triggered roam scan in firmware
249 *
250 * Return: Meaningful string from enum WMI_SCAN_CLIENT_ID
251 */
hdd_client_id_to_str(uint32_t client_id)252 static char *hdd_client_id_to_str(uint32_t client_id)
253 {
254 switch (client_id) {
255 case WMI_SCAN_CLIENT_NLO:
256 return "PNO";
257 case WMI_SCAN_CLIENT_EXTSCAN:
258 return "GSCAN";
259 case WMI_SCAN_CLIENT_ROAM:
260 return "ROAM";
261 case WMI_SCAN_CLIENT_P2P:
262 return "P2P";
263 case WMI_SCAN_CLIENT_LPI:
264 return "LPI";
265 case WMI_SCAN_CLIENT_NAN:
266 return "NAN";
267 case WMI_SCAN_CLIENT_ANQP:
268 return "ANQP";
269 case WMI_SCAN_CLIENT_OBSS:
270 return "OBSS";
271 case WMI_SCAN_CLIENT_PLM:
272 return "PLM";
273 case WMI_SCAN_CLIENT_HOST:
274 return "HOST";
275 default:
276 return "UNKNOWN";
277 }
278 return "UNKNOWN";
279 }
280
281 /**
282 * hdd_roam_scan_trigger() - Print roam scan trigger info into buffer
283 * @scan: roam scan event data
284 * @buf: buffer to write roam scan trigger info
285 * @buf_avail_len: available buffer length
286 *
287 * Return: No.of bytes populated by this function in buffer
288 */
289 static ssize_t
hdd_roam_scan_trigger(struct wmi_roam_scan_stats_params * scan,uint8_t * buf,ssize_t buf_avail_len)290 hdd_roam_scan_trigger(struct wmi_roam_scan_stats_params *scan,
291 uint8_t *buf, ssize_t buf_avail_len)
292 {
293 ssize_t length = 0;
294 int ret;
295 char *str;
296 bool print_trigger_value;
297
298 ret = scnprintf(buf, buf_avail_len,
299 "Trigger reason is %s\n",
300 hdd_roam_scan_trigger_to_str(scan->trigger_id));
301 if (ret <= 0)
302 return length;
303
304 length = ret;
305
306 str = hdd_roam_scan_trigger_value(scan->trigger_id,
307 &print_trigger_value);
308 if (!print_trigger_value || !str)
309 return length;
310
311 if (length >= buf_avail_len) {
312 hdd_err("No sufficient buf_avail_len");
313 length = buf_avail_len;
314 return length;
315 }
316
317 ret = scnprintf(buf + length, buf_avail_len - length,
318 "Trigger value is: %u %s\n",
319 scan->trigger_value, str);
320 if (ret <= 0)
321 return length;
322
323 length += ret;
324 return length;
325 }
326
327 /**
328 * hdd_roam_scan_chan() - Print roam scan chan freq info into buffer
329 * @scan: roam scan event data
330 * @buf: buffer to write roam scan freq info
331 * @buf_avail_len: available buffer length
332 *
333 * Return: No.of bytes populated by this function in buffer
334 */
335 static ssize_t
hdd_roam_scan_chan(struct wmi_roam_scan_stats_params * scan,uint8_t * buf,ssize_t buf_avail_len)336 hdd_roam_scan_chan(struct wmi_roam_scan_stats_params *scan,
337 uint8_t *buf, ssize_t buf_avail_len)
338 {
339 ssize_t length = 0;
340 uint32_t i;
341 int ret;
342
343 ret = scnprintf(buf, buf_avail_len,
344 "Num of scan channels: %u\n"
345 "scan channel list:",
346 scan->num_scan_chans);
347 if (ret <= 0)
348 return length;
349
350 length = ret;
351
352 for (i = 0; i < scan->num_scan_chans; i++) {
353 if (length >= buf_avail_len) {
354 hdd_err("No sufficient buf_avail_len");
355 length = buf_avail_len;
356 return length;
357 }
358
359 ret = scnprintf(buf + length, buf_avail_len - length,
360 "%u ", scan->scan_freqs[i]);
361 if (ret <= 0)
362 return length;
363
364 length += ret;
365 }
366
367 return length;
368 }
369
370 /**
371 * wlan_hdd_update_roam_stats() - Internal function to get roam scan stats
372 * @hdd_ctx: hdd context
373 * @adapter: pointer to adapter
374 * @buf: buffer to hold the stats
375 * @buf_avail_len: maximum available length in response buffer
376 *
377 * Return: Size of formatted roam scan response stats
378 */
379 static ssize_t
wlan_hdd_update_roam_stats(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter,uint8_t * buf,ssize_t buf_avail_len)380 wlan_hdd_update_roam_stats(struct hdd_context *hdd_ctx,
381 struct hdd_adapter *adapter,
382 uint8_t *buf, ssize_t buf_avail_len)
383 {
384 ssize_t length = 0;
385 struct wmi_roam_scan_stats_res *roam_stats;
386 struct wmi_roam_scan_stats_params *scan;
387 int ret;
388 int rsi; /* roam scan iterator */
389 int rci; /* roam candidate iterator */
390
391 roam_stats = hdd_get_roam_scan_stats(hdd_ctx, adapter);
392 if (!roam_stats) {
393 hdd_err("Couldn't get roam stats");
394 ret = scnprintf(buf, buf_avail_len,
395 "Failed to fetch roam stats\n");
396 if (ret <= 0)
397 return length;
398 length += ret;
399 return length;
400 }
401
402 ret = scnprintf(buf, buf_avail_len,
403 "\n\nStats of last %u roam scans\n",
404 roam_stats->num_roam_scans);
405 if (ret <= 0)
406 goto free_mem;
407 length += ret;
408
409 for (rsi = 0; rsi < roam_stats->num_roam_scans; rsi++) {
410 if (length >= buf_avail_len) {
411 hdd_err("No sufficient buf_avail_len");
412 length = buf_avail_len;
413 goto free_mem;
414 }
415
416 scan = &roam_stats->roam_scan[rsi];
417 ret = scnprintf(buf + length, buf_avail_len - length,
418 "\nRoam scan[%u] details\n", rsi);
419 if (ret <= 0)
420 goto free_mem;
421 length += ret;
422
423 if (length >= buf_avail_len) {
424 hdd_err("No sufficient buf_avail_len");
425 length = buf_avail_len;
426 goto free_mem;
427 }
428
429 ret = scnprintf(buf + length, buf_avail_len - length,
430 "This scan is triggered by \"%s\" scan client\n",
431 hdd_client_id_to_str(scan->client_id));
432
433 if (ret <= 0)
434 goto free_mem;
435 length += ret;
436
437 if (length >= buf_avail_len) {
438 hdd_err("No sufficient buf_avail_len");
439 length = buf_avail_len;
440 goto free_mem;
441 }
442
443 length += hdd_roam_scan_trigger(scan, buf + length,
444 buf_avail_len - length);
445 if (length >= buf_avail_len) {
446 hdd_err("No sufficient buf_avail_len");
447 length = buf_avail_len;
448 goto free_mem;
449 }
450
451 length += hdd_roam_scan_chan(scan, buf + length,
452 buf_avail_len - length);
453 if (length >= buf_avail_len) {
454 hdd_err("No sufficient buf_avail_len");
455 length = buf_avail_len;
456 goto free_mem;
457 }
458
459 ret = scnprintf(buf + length, buf_avail_len - length,
460 "\nRoam Scan time: 0x%llx\n",
461 roam_stats->roam_scan[rsi].time_stamp);
462 if (ret <= 0)
463 goto free_mem;
464 length += ret;
465
466 if (length >= buf_avail_len) {
467 hdd_err("No sufficient buf_avail_len");
468 length = buf_avail_len;
469 goto free_mem;
470 }
471
472 if (scan->is_roam_successful) {
473 ret = scnprintf(buf + length,
474 buf_avail_len - length,
475 "\nSTA roamed from "
476 QDF_MAC_ADDR_FMT " to "
477 QDF_MAC_ADDR_FMT "\n",
478 QDF_MAC_ADDR_REF(scan->old_bssid),
479 QDF_MAC_ADDR_REF(scan->new_bssid));
480 } else {
481 ret = scnprintf(buf + length,
482 buf_avail_len - length,
483 "\nSTA is connected to " QDF_MAC_ADDR_FMT
484 " before and after scan, not roamed\n",
485 QDF_MAC_ADDR_REF(scan->old_bssid));
486 }
487 if (ret <= 0)
488 goto free_mem;
489 length += ret;
490
491 if (length >= buf_avail_len) {
492 hdd_err("No sufficient buf_avail_len");
493 length = buf_avail_len;
494 goto free_mem;
495 }
496
497 ret = scnprintf(buf + length, buf_avail_len - length,
498 "Roam candidate details\n");
499 if (ret <= 0)
500 goto free_mem;
501 length += ret;
502
503 if (length >= buf_avail_len) {
504 hdd_err("No sufficient buf_avail_len");
505 length = buf_avail_len;
506 goto free_mem;
507 }
508
509 ret = scnprintf(buf + length, buf_avail_len - length,
510 " BSSID FREQ SCORE RSSI\n");
511 if (ret <= 0)
512 goto free_mem;
513 length += ret;
514
515 for (rci = 0; rci < scan->num_roam_candidates; rci++) {
516 uint8_t *bssid = scan->cand[rci].bssid;
517
518 if (length >= buf_avail_len) {
519 hdd_err("No sufficient buf_avail_len");
520 length = buf_avail_len;
521 goto free_mem;
522 }
523
524 ret = scnprintf(buf + length,
525 buf_avail_len - length,
526 QDF_MAC_ADDR_FMT " %4u %3u %3u\n",
527 QDF_MAC_ADDR_REF(bssid),
528 scan->cand[rci].freq,
529 scan->cand[rci].score,
530 scan->cand[rci].rssi);
531 if (ret <= 0)
532 goto free_mem;
533 length += ret;
534 }
535 }
536
537 free_mem:
538 qdf_mem_free(roam_stats);
539 return length;
540 }
541
542 ssize_t
wlan_hdd_debugfs_update_roam_stats(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter,uint8_t * buf,ssize_t buf_avail_len)543 wlan_hdd_debugfs_update_roam_stats(struct hdd_context *hdd_ctx,
544 struct hdd_adapter *adapter,
545 uint8_t *buf, ssize_t buf_avail_len)
546 {
547 ssize_t len = 0;
548 int ret_val;
549
550 hdd_enter();
551
552 len = wlan_hdd_current_time_info_debugfs(buf, buf_avail_len - len);
553
554 if (len >= buf_avail_len) {
555 hdd_err("No sufficient buf_avail_len");
556 return buf_avail_len;
557 }
558 if (adapter->device_mode != QDF_STA_MODE) {
559 ret_val = scnprintf(buf + len, buf_avail_len - len,
560 "Interface is not in STA Mode\n");
561 if (ret_val <= 0)
562 return len;
563
564 len += ret_val;
565 return len;
566 }
567
568 if (len >= buf_avail_len) {
569 hdd_err("No sufficient buf_avail_len");
570 return buf_avail_len;
571 }
572 len += wlan_hdd_update_roam_stats(hdd_ctx, adapter, buf + len,
573 buf_avail_len - len);
574
575 hdd_exit();
576
577 return len;
578 }
579