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