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(&params);
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