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