1 /* 2 * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * DOC: contains scan cache api and functionality 21 * The Scan entries are protected by scan_db_lock. Holding the lock 22 * for whole scan operation during get/flush scan results may take 23 * more than 5 ms and thus ref count is used along with scan_db_lock. 24 * Below are the operation on scan cache entry: 25 * - While adding new node to the entry scan_db_lock is taken and ref_cnt 26 * is initialized and incremented. 27 * - While reading the entry ref_cnt is incremented while holding the lock. 28 * - Once reading operation is done ref_cnt is decremented while holding 29 * the lock. 30 * - Once ref_cnt become 0 the node is deleted from the scan cache. 31 */ 32 #include <qdf_status.h> 33 #include <wlan_objmgr_psoc_obj.h> 34 #include <wlan_objmgr_pdev_obj.h> 35 #include <wlan_objmgr_vdev_obj.h> 36 #include <wlan_scan_public_structs.h> 37 #include <wlan_scan_utils_api.h> 38 #include "wlan_scan_main.h" 39 #include "wlan_scan_cache_db_i.h" 40 #include "wlan_reg_services_api.h" 41 #include "wlan_reg_ucfg_api.h" 42 43 /** 44 * scm_del_scan_node() - API to remove scan node from the list 45 * @list: hash list 46 * @scan_node: node to be removed 47 * 48 * This should be called while holding scan_db_lock. 49 * 50 * Return: void 51 */ 52 static void scm_del_scan_node(qdf_list_t *list, 53 struct scan_cache_node *scan_node) 54 { 55 QDF_STATUS status; 56 57 status = qdf_list_remove_node(list, &scan_node->node); 58 if (QDF_IS_STATUS_SUCCESS(status)) { 59 util_scan_free_cache_entry(scan_node->entry); 60 qdf_mem_free(scan_node); 61 } 62 } 63 64 /** 65 * scm_del_scan_node_from_db() - API to del the scan entry 66 * @scan_db: scan database 67 * @scan_entry:entry scan_node 68 * 69 * API to flush the scan entry. This should be called while 70 * holding scan_db_lock. 71 * 72 * Return: QDF status. 73 */ 74 static QDF_STATUS scm_del_scan_node_from_db(struct scan_dbs *scan_db, 75 struct scan_cache_node *scan_node) 76 { 77 QDF_STATUS status = QDF_STATUS_SUCCESS; 78 uint8_t hash_idx; 79 80 if (!scan_node) 81 return QDF_STATUS_E_INVAL; 82 83 hash_idx = SCAN_GET_HASH(scan_node->entry->bssid.bytes); 84 scm_del_scan_node(&scan_db->scan_hash_tbl[hash_idx], scan_node); 85 scan_db->num_entries--; 86 87 return status; 88 } 89 90 /** 91 * scm_scan_entry_get_ref() - api to increase ref count of scan entry 92 * @scan_node: scan node 93 * 94 * Return: void 95 */ 96 static void scm_scan_entry_get_ref(struct scan_cache_node *scan_node) 97 { 98 if (scan_node == NULL) { 99 scm_err("scan_node is NULL"); 100 QDF_ASSERT(0); 101 return; 102 } 103 qdf_atomic_inc(&scan_node->ref_cnt); 104 } 105 106 /** 107 * scm_scan_entry_put_ref() - Api to decrease ref count of scan entry 108 * and free if it become 0 109 * @scan_db: scan database 110 * @scan_node: scan node 111 * @lock_needed: if scan_db_lock is needed 112 * @delete: logically delete the entry 113 * 114 * Return: void 115 */ 116 static void scm_scan_entry_put_ref(struct scan_dbs *scan_db, 117 struct scan_cache_node *scan_node, bool lock_needed, bool delete) 118 { 119 120 if (!scan_node) { 121 scm_err("scan_node is NULL"); 122 QDF_ASSERT(0); 123 return; 124 } 125 126 if (lock_needed) 127 qdf_spin_lock_bh(&scan_db->scan_db_lock); 128 129 if (delete && !scan_node->active) { 130 if (lock_needed) 131 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 132 scm_warn("node is already deleted"); 133 return; 134 } 135 136 if (!qdf_atomic_read(&scan_node->ref_cnt)) { 137 if (lock_needed) 138 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 139 scm_err("scan_node ref cnt is 0"); 140 QDF_ASSERT(0); 141 return; 142 } 143 144 if (delete) 145 scan_node->active = false; 146 147 /* Decrement ref count, free scan_node, if ref count == 0 */ 148 if (qdf_atomic_dec_and_test(&scan_node->ref_cnt)) 149 scm_del_scan_node_from_db(scan_db, scan_node); 150 151 if (lock_needed) 152 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 153 } 154 155 /** 156 * scm_add_scan_node() - API to add scan node 157 * @scan_db: data base 158 * @scan_node: node to be removed 159 * 160 * Return: void 161 */ 162 static void scm_add_scan_node(struct scan_dbs *scan_db, 163 struct scan_cache_node *scan_node) 164 { 165 uint8_t hash_idx; 166 167 hash_idx = 168 SCAN_GET_HASH(scan_node->entry->bssid.bytes); 169 170 qdf_spin_lock_bh(&scan_db->scan_db_lock); 171 qdf_atomic_init(&scan_node->ref_cnt); 172 scan_node->active = true; 173 scm_scan_entry_get_ref(scan_node); 174 qdf_list_insert_back(&scan_db->scan_hash_tbl[hash_idx], 175 &scan_node->node); 176 scan_db->num_entries++; 177 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 178 } 179 180 181 /** 182 * scm_get_next_valid_node() - API get the next valid scan node from 183 * the list 184 * @list: hash list 185 * @cur_node: current node pointer 186 * 187 * API to get next active node from the list. If cur_node is NULL 188 * it will return first node of the list. 189 * Call must be protected by scan_db->scan_db_lock 190 * 191 * Return: next scan node 192 */ 193 static qdf_list_node_t * 194 scm_get_next_valid_node(qdf_list_t *list, 195 qdf_list_node_t *cur_node) 196 { 197 qdf_list_node_t *next_node = NULL; 198 qdf_list_node_t *temp_node = NULL; 199 struct scan_cache_node *scan_node; 200 201 if (cur_node) 202 qdf_list_peek_next(list, cur_node, &next_node); 203 else 204 qdf_list_peek_front(list, &next_node); 205 206 while (next_node) { 207 scan_node = qdf_container_of(next_node, 208 struct scan_cache_node, node); 209 if (scan_node->active) 210 return next_node; 211 /* 212 * If node is not valid check for next entry 213 * to get next valid node. 214 */ 215 qdf_list_peek_next(list, next_node, &temp_node); 216 next_node = temp_node; 217 temp_node = NULL; 218 } 219 220 return next_node; 221 } 222 223 /** 224 * scm_get_next_node() - API get the next scan node from 225 * the list 226 * @scan_db: scan data base 227 * @list: hash list 228 * @cur_node: current node pointer 229 * 230 * API get the next node from the list. If cur_node is NULL 231 * it will return first node of the list 232 * 233 * Return: next scan cache node 234 */ 235 static struct scan_cache_node * 236 scm_get_next_node(struct scan_dbs *scan_db, 237 qdf_list_t *list, struct scan_cache_node *cur_node) 238 { 239 struct scan_cache_node *next_node = NULL; 240 qdf_list_node_t *next_list = NULL; 241 242 qdf_spin_lock_bh(&scan_db->scan_db_lock); 243 if (cur_node) { 244 next_list = scm_get_next_valid_node(list, &cur_node->node); 245 /* Decrement the ref count of the previous node */ 246 scm_scan_entry_put_ref(scan_db, 247 cur_node, false, false); 248 } else { 249 next_list = scm_get_next_valid_node(list, NULL); 250 } 251 /* Increase the ref count of the obtained node */ 252 if (next_list) { 253 next_node = qdf_container_of(next_list, 254 struct scan_cache_node, node); 255 scm_scan_entry_get_ref(next_node); 256 } 257 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 258 259 return next_node; 260 } 261 262 /** 263 * scm_check_and_age_out() - check and age out the old entries 264 * @scan_db: scan db 265 * @scan_node: node to check for age out 266 * @scan_aging_time: scan cache aging time 267 * 268 * Return: void 269 */ 270 static void scm_check_and_age_out(struct scan_dbs *scan_db, 271 struct scan_cache_node *node, 272 uint32_t scan_aging_time) 273 { 274 if (util_scan_entry_age(node->entry) >= 275 scan_aging_time) { 276 scm_info("Aging out BSSID: %pM with age %d ms", 277 node->entry->bssid.bytes, 278 util_scan_entry_age(node->entry)); 279 scm_scan_entry_put_ref(scan_db, node, true, true); 280 } 281 } 282 283 void scm_age_out_entries(struct wlan_objmgr_psoc *psoc, 284 struct scan_dbs *scan_db) 285 { 286 int i; 287 struct scan_cache_node *cur_node = NULL; 288 struct scan_cache_node *next_node = NULL; 289 struct scan_default_params *def_param; 290 291 def_param = wlan_scan_psoc_get_def_params(psoc); 292 if (!def_param) { 293 scm_err("wlan_scan_psoc_get_def_params failed"); 294 return; 295 } 296 297 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 298 cur_node = scm_get_next_node(scan_db, 299 &scan_db->scan_hash_tbl[i], NULL); 300 while (cur_node) { 301 scm_check_and_age_out(scan_db, cur_node, 302 def_param->scan_cache_aging_time); 303 next_node = scm_get_next_node(scan_db, 304 &scan_db->scan_hash_tbl[i], cur_node); 305 cur_node = next_node; 306 next_node = NULL; 307 } 308 } 309 } 310 311 /** 312 * scm_flush_oldest_entry() - flust out the oldest entry 313 * @scan_db: scan db from which oldest etry needs to be flushed 314 * 315 * Return: QDF_STATUS 316 */ 317 static QDF_STATUS scm_flush_oldest_entry(struct scan_dbs *scan_db) 318 { 319 int i; 320 struct scan_cache_node *oldest_node = NULL; 321 struct scan_cache_node *cur_node; 322 qdf_list_node_t *cur_list; 323 324 qdf_spin_lock_bh(&scan_db->scan_db_lock); 325 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 326 /* Get the first valid node for the hash */ 327 cur_list = scm_get_next_valid_node(&scan_db->scan_hash_tbl[i], 328 NULL); 329 /* 330 * Check only the first valid node if present as new 331 * entry are added to tail and thus first valid 332 * node is the oldest 333 */ 334 if (cur_list) { 335 cur_node = qdf_container_of(cur_list, 336 struct scan_cache_node, node); 337 if (!oldest_node || 338 (util_scan_entry_age(oldest_node->entry) < 339 util_scan_entry_age(cur_node->entry))) 340 oldest_node = cur_node; 341 } 342 } 343 344 if (oldest_node) { 345 scm_debug("Flush oldest BSSID: %pM with age %d ms", 346 oldest_node->entry->bssid.bytes, 347 util_scan_entry_age(oldest_node->entry)); 348 scm_scan_entry_put_ref(scan_db, oldest_node, false, true); 349 } 350 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 351 352 return QDF_STATUS_SUCCESS; 353 } 354 355 /** 356 * scm_update_alt_wcn_ie() - update the alternate WCN IE 357 * @from: copy from 358 * @dst: copy to 359 * 360 * Return: void 361 */ 362 static void scm_update_alt_wcn_ie(struct scan_cache_entry *from, 363 struct scan_cache_entry *dst) 364 { 365 uint32_t alt_wcn_ie_len; 366 367 if (from->frm_subtype == dst->frm_subtype) 368 return; 369 370 if (!from->ie_list.wcn && !dst->ie_list.wcn) 371 return; 372 373 /* Existing WCN IE is empty. */ 374 if (!from->ie_list.wcn) 375 return; 376 377 alt_wcn_ie_len = 2 + from->ie_list.wcn[1]; 378 if (alt_wcn_ie_len > WLAN_MAX_IE_LEN + 2) { 379 scm_err("invalid IE len"); 380 return; 381 } 382 383 if (!dst->alt_wcn_ie.ptr) { 384 /* allocate this additional buffer for alternate WCN IE */ 385 dst->alt_wcn_ie.ptr = qdf_mem_malloc(WLAN_MAX_IE_LEN + 2); 386 if (!dst->alt_wcn_ie.ptr) { 387 scm_err("failed to allocate memory"); 388 return; 389 } 390 } 391 qdf_mem_copy(dst->alt_wcn_ie.ptr, 392 from->ie_list.wcn, alt_wcn_ie_len); 393 dst->alt_wcn_ie.len = alt_wcn_ie_len; 394 } 395 396 /** 397 * scm_add_scan_entry() - add new scan entry to the database 398 * @scan_db: scan database 399 * @scan_params: new entry to be added 400 * 401 * Return: QDF_STATUS 402 */ 403 static QDF_STATUS scm_add_scan_entry(struct scan_dbs *scan_db, 404 struct scan_cache_entry *scan_params) 405 { 406 struct scan_cache_node *scan_node; 407 QDF_STATUS status; 408 409 if (scan_db->num_entries >= MAX_SCAN_CACHE_SIZE) { 410 status = scm_flush_oldest_entry(scan_db); 411 if (QDF_IS_STATUS_ERROR(status)) 412 return status; 413 } 414 415 scan_node = qdf_mem_malloc(sizeof(*scan_node)); 416 if (!scan_node) 417 return QDF_STATUS_E_NOMEM; 418 419 scan_node->entry = scan_params; 420 scm_add_scan_node(scan_db, scan_node); 421 422 return QDF_STATUS_SUCCESS; 423 } 424 425 /** 426 * scm_update_mlme_info() - update mlme info 427 * @src: source scan entry 428 * @dest: destination scan entry 429 * 430 * Return: void 431 */ 432 static inline void 433 scm_update_mlme_info(struct scan_cache_entry *src, 434 struct scan_cache_entry *dest) 435 { 436 qdf_mem_copy(&dest->mlme_info, &src->mlme_info, 437 sizeof(struct mlme_info)); 438 } 439 440 /** 441 * scm_delete_duplicate_entry() - remove duplicate node entry 442 * @scan_db: scan database 443 * @scan_params: new entry to be added 444 * @scan_node: old entry to removed 445 * 446 * Remove duplicate node after copying required 447 * info into new entry 448 * 449 * Return: void 450 */ 451 static void scm_delete_duplicate_entry(struct scan_dbs *scan_db, 452 struct scan_cache_entry *scan_params, 453 struct scan_cache_node *scan_node) 454 { 455 struct scan_cache_entry *scan_entry; 456 uint64_t time_gap; 457 458 scan_entry = scan_node->entry; 459 /* If old entry have the ssid but new entry does not */ 460 if (!scan_params->ssid.length && scan_entry->ssid.length) { 461 /* 462 * New entry has a hidden SSID and old one has the SSID. 463 * Add the entry by using the ssid of the old entry 464 * only if diff of saved SSID time and current time is 465 * less than HIDDEN_SSID_TIME time. 466 * This will avoid issues in case AP changes its SSID 467 * while remain hidden. 468 */ 469 time_gap = 470 qdf_mc_timer_get_system_time() - 471 scan_entry->hidden_ssid_timestamp; 472 if (time_gap <= HIDDEN_SSID_TIME) { 473 scan_params->hidden_ssid_timestamp = 474 scan_entry->hidden_ssid_timestamp; 475 scan_params->ssid.length = 476 scan_entry->ssid.length; 477 qdf_mem_copy(scan_params->ssid.ssid, 478 scan_entry->ssid.ssid, 479 scan_params->ssid.length); 480 } 481 } 482 483 /* 484 * Due to Rx sensitivity issue, sometime beacons are seen on adjacent 485 * channel so workaround in software is needed. If DS params or HT info 486 * are present driver can get proper channel info from these IEs and set 487 * channel_mismatch so that the older RSSI values are used in new entry. 488 * 489 * For the cases where DS params and HT info is not present, driver 490 * needs to check below conditions to get proper channel and set 491 * channel_mismatch so that the older RSSI values are used in new entry: 492 * -- The old entry channel and new entry channel are not same 493 * -- RSSI is less than -80, this indicate that the signal has leaked 494 * in adjacent channel. 495 */ 496 if ((scan_params->frm_subtype == MGMT_SUBTYPE_BEACON) && 497 !util_scan_entry_htinfo(scan_params) && 498 !util_scan_entry_ds_param(scan_params) && 499 (scan_params->channel.chan_idx != scan_entry->channel.chan_idx) && 500 (scan_params->rssi_raw < ADJACENT_CHANNEL_RSSI_THRESHOLD)) { 501 scan_params->channel.chan_idx = scan_entry->channel.chan_idx; 502 scan_params->channel_mismatch = true; 503 } 504 505 /* Use old value for rssi if beacon was heard on adjacent channel. */ 506 if (scan_params->channel_mismatch) { 507 scan_params->rssi_raw = scan_entry->rssi_raw; 508 scan_params->avg_rssi = scan_entry->avg_rssi; 509 scan_params->rssi_timestamp = 510 scan_entry->rssi_timestamp; 511 } else { 512 /* If elapsed time since last rssi update for this 513 * entry is smaller than a thresold, calculate a 514 * running average of the RSSI values. 515 * Otherwise new frames RSSI is more representive 516 * of the signal strength. 517 */ 518 time_gap = 519 scan_params->scan_entry_time - 520 scan_entry->rssi_timestamp; 521 if (time_gap > WLAN_RSSI_AVERAGING_TIME) 522 scan_params->avg_rssi = 523 WLAN_RSSI_IN(scan_params->rssi_raw); 524 else { 525 /* Copy previous average rssi to new entry */ 526 scan_params->avg_rssi = scan_entry->avg_rssi; 527 /* Average with previous samples */ 528 WLAN_RSSI_LPF(scan_params->avg_rssi, 529 scan_params->rssi_raw); 530 } 531 532 scan_params->rssi_timestamp = scan_params->scan_entry_time; 533 } 534 535 /* copy wsn ie from scan_entry to scan_params*/ 536 scm_update_alt_wcn_ie(scan_entry, scan_params); 537 538 /* copy mlme info from scan_entry to scan_params*/ 539 scm_update_mlme_info(scan_entry, scan_params); 540 541 /* Mark delete the duplicate node */ 542 scm_scan_entry_put_ref(scan_db, scan_node, true, true); 543 } 544 545 /** 546 * scm_find_duplicate_and_del() - find duplicate entry if present 547 * and update it 548 * @scan_db: scan db 549 * @entry: input scan cache entry 550 * 551 * Return: true if entry is found and updated else false 552 */ 553 static bool 554 scm_find_duplicate_and_del(struct scan_dbs *scan_db, 555 struct scan_cache_entry *entry) 556 { 557 uint8_t hash_idx; 558 struct scan_cache_node *cur_node; 559 struct scan_cache_node *next_node = NULL; 560 561 hash_idx = SCAN_GET_HASH(entry->bssid.bytes); 562 563 cur_node = scm_get_next_node(scan_db, 564 &scan_db->scan_hash_tbl[hash_idx], NULL); 565 566 while (cur_node) { 567 if (util_is_scan_entry_match(entry, 568 cur_node->entry)) { 569 scm_delete_duplicate_entry(scan_db, 570 entry, cur_node); 571 scm_scan_entry_put_ref(scan_db, 572 cur_node, true, false); 573 return true; 574 } 575 next_node = scm_get_next_node(scan_db, 576 &scan_db->scan_hash_tbl[hash_idx], cur_node); 577 cur_node = next_node; 578 next_node = NULL; 579 } 580 581 return false; 582 } 583 584 /** 585 * scm_add_update_entry() - add or update scan entry 586 * @psoc: psoc ptr 587 * @pdev: pdev pointer 588 * @scan_params: new received entry 589 * 590 * Return: QDF_STATUS 591 */ 592 static QDF_STATUS scm_add_update_entry(struct wlan_objmgr_psoc *psoc, 593 struct wlan_objmgr_pdev *pdev, struct scan_cache_entry *scan_params) 594 { 595 QDF_STATUS status; 596 struct scan_dbs *scan_db; 597 struct wlan_scan_obj *scan_obj; 598 599 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 600 if (!scan_db) { 601 scm_err("scan_db is NULL"); 602 return QDF_STATUS_E_INVAL; 603 } 604 605 scan_obj = wlan_psoc_get_scan_obj(psoc); 606 if (!scan_obj) { 607 scm_err("scan_obj is NULL"); 608 return QDF_STATUS_E_INVAL; 609 } 610 611 if (scan_params->frm_subtype == 612 MGMT_SUBTYPE_PROBE_RESP && 613 !scan_params->ie_list.ssid) 614 scm_info("Probe resp doesnt contain SSID"); 615 616 617 if (scan_params->ie_list.csa || 618 scan_params->ie_list.xcsa || 619 scan_params->ie_list.cswrp) 620 scm_info("CSA IE present for BSSID: %pM", 621 scan_params->bssid.bytes); 622 623 scm_find_duplicate_and_del(scan_db, scan_params); 624 625 if (scan_obj->cb.inform_beacon) 626 scan_obj->cb.inform_beacon(pdev, scan_params); 627 628 status = scm_add_scan_entry(scan_db, scan_params); 629 630 return status; 631 } 632 633 QDF_STATUS scm_handle_bcn_probe(struct scheduler_msg *msg) 634 { 635 struct scan_bcn_probe_event *bcn; 636 struct wlan_objmgr_psoc *psoc; 637 struct wlan_objmgr_pdev *pdev = NULL; 638 struct scan_cache_entry *scan_entry; 639 struct wlan_scan_obj *scan_obj; 640 qdf_list_t *scan_list = NULL; 641 QDF_STATUS status = QDF_STATUS_SUCCESS; 642 uint32_t list_count, i; 643 qdf_list_node_t *next_node = NULL; 644 struct scan_cache_node *scan_node; 645 646 bcn = msg->bodyptr; 647 if (!bcn) { 648 scm_err("bcn is NULL"); 649 return QDF_STATUS_E_INVAL; 650 } 651 if (!bcn->rx_data) { 652 scm_err("rx_data iS NULL"); 653 status = QDF_STATUS_E_INVAL; 654 goto free_nbuf; 655 } 656 if (!bcn->buf) { 657 scm_err("buf is NULL"); 658 status = QDF_STATUS_E_INVAL; 659 goto free_nbuf; 660 } 661 662 psoc = bcn->psoc; 663 pdev = wlan_objmgr_get_pdev_by_id(psoc, 664 bcn->rx_data->pdev_id, WLAN_SCAN_ID); 665 if (!pdev) { 666 scm_err("pdev is NULL"); 667 status = QDF_STATUS_E_INVAL; 668 goto free_nbuf; 669 } 670 scan_obj = wlan_psoc_get_scan_obj(psoc); 671 if (!scan_obj) { 672 scm_err("scan_obj is NULL"); 673 status = QDF_STATUS_E_INVAL; 674 goto free_nbuf; 675 } 676 677 if (qdf_nbuf_len(bcn->buf) <= 678 (sizeof(struct wlan_frame_hdr) + 679 offsetof(struct wlan_bcn_frame, ie))) { 680 scm_err("invalid beacon/probe length"); 681 status = QDF_STATUS_E_INVAL; 682 goto free_nbuf; 683 } 684 685 scan_list = 686 util_scan_unpack_beacon_frame(qdf_nbuf_data(bcn->buf), 687 qdf_nbuf_len(bcn->buf), bcn->frm_type, 688 bcn->rx_data); 689 if (!scan_list || qdf_list_empty(scan_list)) { 690 scm_err("failed to unpack frame"); 691 status = QDF_STATUS_E_INVAL; 692 goto free_nbuf; 693 } 694 695 list_count = qdf_list_size(scan_list); 696 for (i = 0; i < list_count; i++) { 697 status = qdf_list_remove_front(scan_list, &next_node); 698 if (QDF_IS_STATUS_ERROR(status) || next_node == NULL) { 699 scm_err("failed to unpack frame"); 700 status = QDF_STATUS_E_INVAL; 701 goto free_nbuf; 702 } 703 704 scan_node = qdf_container_of(next_node, 705 struct scan_cache_node, node); 706 707 scan_entry = scan_node->entry; 708 709 scm_debug("Received %s from BSSID: %pM tsf_delta = %u Seq Num: %x ssid:%.*s, rssi: %d", 710 (bcn->frm_type == MGMT_SUBTYPE_PROBE_RESP) ? 711 "Probe Rsp" : "Beacon", scan_entry->bssid.bytes, 712 scan_entry->tsf_delta, scan_entry->seq_num, 713 scan_entry->ssid.length, scan_entry->ssid.ssid, 714 scan_entry->rssi_raw); 715 716 if (scan_obj->drop_bcn_on_chan_mismatch && 717 scan_entry->channel_mismatch) { 718 scm_debug("Drop frame, as channel mismatch Received for from BSSID: %pM ", 719 scan_entry->bssid.bytes); 720 util_scan_free_cache_entry(scan_entry); 721 qdf_mem_free(scan_node); 722 continue; 723 } 724 725 if (scan_obj->cb.update_beacon) 726 scan_obj->cb.update_beacon(pdev, scan_entry); 727 728 if (wlan_reg_11d_enabled_on_host(psoc)) 729 scm_11d_handle_country_info(psoc, pdev, scan_entry); 730 731 status = scm_add_update_entry(psoc, pdev, scan_entry); 732 if (QDF_IS_STATUS_ERROR(status)) { 733 scm_debug("failed to add entry for BSSID: %pM", 734 scan_entry->bssid.bytes); 735 util_scan_free_cache_entry(scan_entry); 736 qdf_mem_free(scan_node); 737 continue; 738 } 739 740 qdf_mem_free(scan_node); 741 } 742 743 free_nbuf: 744 if (scan_list) 745 qdf_mem_free(scan_list); 746 if (bcn->psoc) 747 wlan_objmgr_psoc_release_ref(bcn->psoc, WLAN_SCAN_ID); 748 if (pdev) 749 wlan_objmgr_pdev_release_ref(pdev, WLAN_SCAN_ID); 750 if (bcn->rx_data) 751 qdf_mem_free(bcn->rx_data); 752 if (bcn->buf) 753 qdf_nbuf_free(bcn->buf); 754 qdf_mem_free(bcn); 755 756 return status; 757 } 758 759 /** 760 * scm_list_insert_sorted() - add the entries in scan_list in sorted way 761 * @psoc: psoc ptr 762 * @filter: scan filter 763 * @scan_node: node entry to be inserted 764 * @scan_list: Temp scan list 765 * 766 * Add the entries in scan_list in sorted way considering 767 * cap_val and prefer val. The node is copy of original scan entry and 768 * thus no lock is required. 769 * 770 * Return: void 771 */ 772 static void scm_list_insert_sorted(struct wlan_objmgr_psoc *psoc, 773 struct scan_filter *filter, 774 struct scan_cache_node *scan_node, 775 qdf_list_t *scan_list) 776 { 777 struct scan_cache_node *cur_node; 778 qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; 779 struct scan_default_params *params; 780 int pcl_chan_weight = 0; 781 782 params = wlan_scan_psoc_get_def_params(psoc); 783 if (!params) { 784 scm_err("wlan_scan_psoc_get_def_params failed"); 785 return; 786 } 787 788 if (filter->num_of_pcl_channels > 0 && 789 (scan_node->entry->rssi_raw > SCM_PCL_RSSI_THRESHOLD)) { 790 if (scm_get_pcl_weight_of_channel( 791 scan_node->entry->channel.chan_idx, 792 filter, &pcl_chan_weight, 793 filter->pcl_weight_list)) { 794 scm_debug("pcl channel %d pcl_chan_weight %d", 795 scan_node->entry->channel.chan_idx, 796 pcl_chan_weight); 797 } 798 } 799 if (params->is_bssid_hint_priority && 800 !qdf_mem_cmp(filter->bssid_hint.bytes, 801 scan_node->entry->bssid.bytes, 802 QDF_MAC_ADDR_SIZE)) 803 scan_node->entry->bss_score = BEST_CANDIDATE_MAX_BSS_SCORE; 804 else 805 scm_calculate_bss_score(psoc, params, 806 scan_node->entry, pcl_chan_weight); 807 808 if (qdf_list_empty(scan_list)) { 809 qdf_list_insert_front(scan_list, &scan_node->node); 810 return; 811 } 812 813 qdf_list_peek_front(scan_list, &cur_lst); 814 815 while (cur_lst) { 816 cur_node = qdf_container_of(cur_lst, 817 struct scan_cache_node, node); 818 if (scm_is_better_bss(params, 819 scan_node->entry, cur_node->entry)) { 820 qdf_list_insert_before(scan_list, 821 &scan_node->node, 822 &cur_node->node); 823 break; 824 } 825 qdf_list_peek_next(scan_list, 826 cur_lst, &next_lst); 827 cur_lst = next_lst; 828 next_lst = NULL; 829 } 830 831 if (!cur_lst) 832 qdf_list_insert_back(scan_list, 833 &scan_node->node); 834 835 } 836 837 /** 838 * scm_scan_apply_filter_get_entry() - apply filter and get the 839 * scan entry 840 * @psoc: psoc pointer 841 * @db_entry: scan entry 842 * @filter: filter to be applied 843 * @scan_list: scan list to which entry is added 844 * 845 * Return: QDF_STATUS 846 */ 847 static QDF_STATUS 848 scm_scan_apply_filter_get_entry(struct wlan_objmgr_psoc *psoc, 849 struct scan_cache_entry *db_entry, 850 struct scan_filter *filter, 851 qdf_list_t *scan_list) 852 { 853 struct scan_cache_node *scan_node = NULL; 854 struct security_info security = {0}; 855 bool match; 856 857 if (!filter) 858 match = true; 859 else 860 match = scm_filter_match(psoc, db_entry, 861 filter, &security); 862 863 if (!match) 864 return QDF_STATUS_SUCCESS; 865 866 scan_node = qdf_mem_malloc(sizeof(*scan_node)); 867 if (!scan_node) 868 return QDF_STATUS_E_NOMEM; 869 870 scan_node->entry = 871 util_scan_copy_cache_entry(db_entry); 872 873 if (!scan_node->entry) { 874 qdf_mem_free(scan_node); 875 return QDF_STATUS_E_NOMEM; 876 } 877 878 qdf_mem_copy(&scan_node->entry->neg_sec_info, 879 &security, sizeof(scan_node->entry->neg_sec_info)); 880 881 if (!filter || !filter->bss_scoring_required) 882 qdf_list_insert_front(scan_list, 883 &scan_node->node); 884 else 885 scm_list_insert_sorted(psoc, filter, scan_node, scan_list); 886 887 return QDF_STATUS_SUCCESS; 888 } 889 890 /** 891 * scm_get_results() - Iterate and get scan results 892 * @psoc: psoc ptr 893 * @scan_db: scan db 894 * @filter: filter to be applied 895 * @scan_list: scan list to which entry is added 896 * 897 * Return: void 898 */ 899 static void scm_get_results(struct wlan_objmgr_psoc *psoc, 900 struct scan_dbs *scan_db, struct scan_filter *filter, 901 qdf_list_t *scan_list) 902 { 903 int i, count; 904 struct scan_cache_node *cur_node; 905 struct scan_cache_node *next_node = NULL; 906 907 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 908 cur_node = scm_get_next_node(scan_db, 909 &scan_db->scan_hash_tbl[i], NULL); 910 count = qdf_list_size(&scan_db->scan_hash_tbl[i]); 911 if (!count) 912 continue; 913 while (cur_node) { 914 scm_scan_apply_filter_get_entry(psoc, 915 cur_node->entry, filter, scan_list); 916 next_node = scm_get_next_node(scan_db, 917 &scan_db->scan_hash_tbl[i], cur_node); 918 cur_node = next_node; 919 } 920 } 921 } 922 923 QDF_STATUS scm_purge_scan_results(qdf_list_t *scan_list) 924 { 925 QDF_STATUS status; 926 struct scan_cache_node *cur_node; 927 qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; 928 929 if (!scan_list) { 930 scm_err("scan_result is NULL"); 931 return QDF_STATUS_E_INVAL; 932 } 933 934 status = qdf_list_peek_front(scan_list, &cur_lst); 935 936 while (cur_lst) { 937 qdf_list_peek_next( 938 scan_list, cur_lst, &next_lst); 939 cur_node = qdf_container_of(cur_lst, 940 struct scan_cache_node, node); 941 status = qdf_list_remove_node(scan_list, 942 cur_lst); 943 if (QDF_IS_STATUS_SUCCESS(status)) { 944 util_scan_free_cache_entry(cur_node->entry); 945 qdf_mem_free(cur_node); 946 } 947 cur_lst = next_lst; 948 next_lst = NULL; 949 } 950 951 qdf_list_destroy(scan_list); 952 qdf_mem_free(scan_list); 953 954 return status; 955 } 956 957 qdf_list_t *scm_get_scan_result(struct wlan_objmgr_pdev *pdev, 958 struct scan_filter *filter) 959 { 960 struct wlan_objmgr_psoc *psoc; 961 struct scan_dbs *scan_db; 962 qdf_list_t *tmp_list; 963 964 if (!pdev) { 965 scm_err("pdev is NULL"); 966 return NULL; 967 } 968 969 psoc = wlan_pdev_get_psoc(pdev); 970 if (!psoc) { 971 scm_err("psoc is NULL"); 972 return NULL; 973 } 974 975 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 976 if (!scan_db) { 977 scm_err("scan_db is NULL"); 978 return NULL; 979 } 980 981 tmp_list = qdf_mem_malloc(sizeof(*tmp_list)); 982 if (!tmp_list) { 983 scm_err("failed tp allocate scan_result"); 984 return NULL; 985 } 986 qdf_list_create(tmp_list, 987 MAX_SCAN_CACHE_SIZE); 988 scm_age_out_entries(psoc, scan_db); 989 scm_get_results(psoc, scan_db, filter, tmp_list); 990 991 return tmp_list; 992 } 993 994 /** 995 * scm_iterate_db_and_call_func() - iterate and call the func 996 * @scan_db: scan db 997 * @func: func to be called 998 * @arg: func arg 999 * 1000 * Return: QDF_STATUS 1001 */ 1002 static QDF_STATUS 1003 scm_iterate_db_and_call_func(struct scan_dbs *scan_db, 1004 scan_iterator_func func, void *arg) 1005 { 1006 int i; 1007 QDF_STATUS status = QDF_STATUS_SUCCESS; 1008 struct scan_cache_node *cur_node; 1009 struct scan_cache_node *next_node = NULL; 1010 1011 if (!func) 1012 return QDF_STATUS_E_INVAL; 1013 1014 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 1015 cur_node = scm_get_next_node(scan_db, 1016 &scan_db->scan_hash_tbl[i], NULL); 1017 while (cur_node) { 1018 status = func(arg, cur_node->entry); 1019 if (QDF_IS_STATUS_ERROR(status)) { 1020 scm_scan_entry_put_ref(scan_db, 1021 cur_node, true, false); 1022 return status; 1023 } 1024 next_node = scm_get_next_node(scan_db, 1025 &scan_db->scan_hash_tbl[i], cur_node); 1026 cur_node = next_node; 1027 } 1028 } 1029 1030 return status; 1031 } 1032 1033 QDF_STATUS 1034 scm_iterate_scan_db(struct wlan_objmgr_pdev *pdev, 1035 scan_iterator_func func, void *arg) 1036 { 1037 struct wlan_objmgr_psoc *psoc; 1038 struct scan_dbs *scan_db; 1039 QDF_STATUS status; 1040 1041 if (!func) { 1042 scm_err("func is NULL"); 1043 return QDF_STATUS_E_INVAL; 1044 } 1045 1046 if (!pdev) { 1047 scm_err("pdev is NULL"); 1048 return QDF_STATUS_E_INVAL; 1049 } 1050 1051 psoc = wlan_pdev_get_psoc(pdev); 1052 if (!psoc) { 1053 scm_err("psoc is NULL"); 1054 return QDF_STATUS_E_INVAL; 1055 } 1056 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 1057 if (!scan_db) { 1058 scm_err("scan_db is NULL"); 1059 return QDF_STATUS_E_INVAL; 1060 } 1061 1062 scm_age_out_entries(psoc, scan_db); 1063 status = scm_iterate_db_and_call_func(scan_db, func, arg); 1064 1065 return status; 1066 } 1067 1068 /** 1069 * scm_scan_apply_filter_flush_entry() -flush scan entries depending 1070 * on filter 1071 * @psoc: psoc ptr 1072 * @scan_db: scan db 1073 * @db_node: node on which filters are applied 1074 * @filter: filter to be applied 1075 * 1076 * Return: QDF_STATUS 1077 */ 1078 static QDF_STATUS 1079 scm_scan_apply_filter_flush_entry(struct wlan_objmgr_psoc *psoc, 1080 struct scan_dbs *scan_db, 1081 struct scan_cache_node *db_node, 1082 struct scan_filter *filter) 1083 { 1084 struct security_info security = {0}; 1085 bool match; 1086 1087 if (!filter) 1088 match = true; 1089 else 1090 match = scm_filter_match(psoc, db_node->entry, 1091 filter, &security); 1092 1093 if (!match) 1094 return QDF_STATUS_SUCCESS; 1095 1096 scm_scan_entry_put_ref(scan_db, db_node, true, true); 1097 1098 return QDF_STATUS_SUCCESS; 1099 } 1100 1101 /** 1102 * scm_flush_scan_entries() - API to flush scan entries depending on filters 1103 * @psoc: psoc ptr 1104 * @scan_db: scan db 1105 * @filter: filter 1106 * 1107 * Return: void 1108 */ 1109 static void scm_flush_scan_entries(struct wlan_objmgr_psoc *psoc, 1110 struct scan_dbs *scan_db, 1111 struct scan_filter *filter) 1112 { 1113 int i; 1114 struct scan_cache_node *cur_node; 1115 struct scan_cache_node *next_node = NULL; 1116 1117 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 1118 cur_node = scm_get_next_node(scan_db, 1119 &scan_db->scan_hash_tbl[i], NULL); 1120 while (cur_node) { 1121 scm_scan_apply_filter_flush_entry(psoc, scan_db, 1122 cur_node, filter); 1123 next_node = scm_get_next_node(scan_db, 1124 &scan_db->scan_hash_tbl[i], cur_node); 1125 cur_node = next_node; 1126 } 1127 } 1128 } 1129 1130 QDF_STATUS scm_flush_results(struct wlan_objmgr_pdev *pdev, 1131 struct scan_filter *filter) 1132 { 1133 struct wlan_objmgr_psoc *psoc; 1134 struct scan_dbs *scan_db; 1135 QDF_STATUS status = QDF_STATUS_SUCCESS; 1136 1137 if (!pdev) { 1138 scm_err("pdev is NULL"); 1139 return QDF_STATUS_E_INVAL; 1140 } 1141 1142 psoc = wlan_pdev_get_psoc(pdev); 1143 if (!psoc) { 1144 scm_err("psoc is NULL"); 1145 return QDF_STATUS_E_INVAL; 1146 } 1147 1148 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 1149 if (!scan_db) { 1150 scm_err("scan_db is NULL"); 1151 return QDF_STATUS_E_INVAL; 1152 } 1153 1154 scm_flush_scan_entries(psoc, scan_db, filter); 1155 1156 return status; 1157 } 1158 1159 /** 1160 * scm_filter_channels() - Remove entries not belonging to channel list 1161 * @scan_db: scan db 1162 * @db_node: node on which filters are applied 1163 * @chan_list: valid channel list 1164 * @num_chan: number of channels 1165 * 1166 * Return: QDF_STATUS 1167 */ 1168 static void scm_filter_channels(struct scan_dbs *scan_db, 1169 struct scan_cache_node *db_node, 1170 uint8_t *chan_list, uint32_t num_chan) 1171 { 1172 int i; 1173 bool match = false; 1174 1175 for (i = 0; i < num_chan; i++) { 1176 if (chan_list[i] == 1177 util_scan_entry_channel_num(db_node->entry)) { 1178 match = true; 1179 break; 1180 } 1181 } 1182 1183 if (!match) 1184 scm_scan_entry_put_ref(scan_db, db_node, true, true); 1185 1186 } 1187 1188 void scm_filter_valid_channel(struct wlan_objmgr_pdev *pdev, 1189 uint8_t *chan_list, uint32_t num_chan) 1190 { 1191 int i; 1192 struct wlan_objmgr_psoc *psoc; 1193 struct scan_dbs *scan_db; 1194 struct scan_cache_node *cur_node; 1195 struct scan_cache_node *next_node = NULL; 1196 1197 scm_info("num_chan = %d", num_chan); 1198 1199 if (!pdev) { 1200 scm_err("pdev is NULL"); 1201 return; 1202 } 1203 1204 psoc = wlan_pdev_get_psoc(pdev); 1205 if (!psoc) { 1206 scm_err("psoc is NULL"); 1207 return; 1208 } 1209 1210 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 1211 if (!scan_db) { 1212 scm_err("scan_db is NULL"); 1213 return; 1214 } 1215 1216 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 1217 cur_node = scm_get_next_node(scan_db, 1218 &scan_db->scan_hash_tbl[i], NULL); 1219 while (cur_node) { 1220 scm_filter_channels(scan_db, 1221 cur_node, chan_list, num_chan); 1222 next_node = scm_get_next_node(scan_db, 1223 &scan_db->scan_hash_tbl[i], cur_node); 1224 cur_node = next_node; 1225 } 1226 } 1227 } 1228 1229 QDF_STATUS scm_scan_register_bcn_cb(struct wlan_objmgr_psoc *psoc, 1230 update_beacon_cb cb, enum scan_cb_type type) 1231 { 1232 struct wlan_scan_obj *scan_obj; 1233 1234 scan_obj = wlan_psoc_get_scan_obj(psoc); 1235 if (!scan_obj) { 1236 scm_err("scan obj is NULL"); 1237 return QDF_STATUS_E_INVAL; 1238 } 1239 switch (type) { 1240 case SCAN_CB_TYPE_INFORM_BCN: 1241 scan_obj->cb.inform_beacon = cb; 1242 break; 1243 case SCAN_CB_TYPE_UPDATE_BCN: 1244 scan_obj->cb.update_beacon = cb; 1245 break; 1246 default: 1247 scm_err("invalid cb type %d", type); 1248 } 1249 1250 return QDF_STATUS_SUCCESS; 1251 } 1252 1253 QDF_STATUS scm_db_init(struct wlan_objmgr_psoc *psoc) 1254 { 1255 int i, j; 1256 struct scan_dbs *scan_db; 1257 1258 if (!psoc) { 1259 scm_err("psoc is NULL"); 1260 return QDF_STATUS_E_INVAL; 1261 } 1262 1263 /* Initialize the scan database per pdev */ 1264 for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) { 1265 scan_db = wlan_pdevid_get_scan_db(psoc, i); 1266 if (!scan_db) { 1267 scm_err("scan_db is NULL %d", i); 1268 continue; 1269 } 1270 scan_db->num_entries = 0; 1271 qdf_spinlock_create(&scan_db->scan_db_lock); 1272 for (j = 0; j < SCAN_HASH_SIZE; j++) 1273 qdf_list_create(&scan_db->scan_hash_tbl[j], 1274 MAX_SCAN_CACHE_SIZE); 1275 } 1276 1277 return QDF_STATUS_SUCCESS; 1278 } 1279 1280 QDF_STATUS scm_db_deinit(struct wlan_objmgr_psoc *psoc) 1281 { 1282 int i, j; 1283 struct scan_dbs *scan_db; 1284 1285 if (!psoc) { 1286 scm_err("scan obj is NULL"); 1287 return QDF_STATUS_E_INVAL; 1288 } 1289 1290 /* Initialize the scan database per pdev */ 1291 for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) { 1292 scan_db = wlan_pdevid_get_scan_db(psoc, i); 1293 if (!scan_db) { 1294 scm_err("scan_db is NULL %d", i); 1295 continue; 1296 } 1297 1298 scm_flush_scan_entries(psoc, scan_db, NULL); 1299 for (j = 0; j < SCAN_HASH_SIZE; j++) 1300 qdf_list_destroy(&scan_db->scan_hash_tbl[j]); 1301 qdf_spinlock_destroy(&scan_db->scan_db_lock); 1302 } 1303 1304 return QDF_STATUS_SUCCESS; 1305 } 1306 1307 QDF_STATUS scm_update_scan_mlme_info(struct wlan_objmgr_pdev *pdev, 1308 struct scan_cache_entry *entry) 1309 { 1310 uint8_t hash_idx; 1311 struct scan_dbs *scan_db; 1312 struct scan_cache_node *cur_node; 1313 struct scan_cache_node *next_node = NULL; 1314 struct wlan_objmgr_psoc *psoc; 1315 1316 psoc = wlan_pdev_get_psoc(pdev); 1317 if (!psoc) { 1318 scm_err("psoc is NULL"); 1319 return QDF_STATUS_E_INVAL; 1320 } 1321 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 1322 if (!scan_db) { 1323 scm_err("scan_db is NULL"); 1324 return QDF_STATUS_E_INVAL; 1325 } 1326 1327 hash_idx = SCAN_GET_HASH(entry->bssid.bytes); 1328 1329 cur_node = scm_get_next_node(scan_db, 1330 &scan_db->scan_hash_tbl[hash_idx], NULL); 1331 1332 while (cur_node) { 1333 if (util_is_scan_entry_match(entry, 1334 cur_node->entry)) { 1335 /* Acquire db lock to prevent simultaneous update */ 1336 qdf_spin_lock_bh(&scan_db->scan_db_lock); 1337 scm_update_mlme_info(entry, cur_node->entry); 1338 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 1339 scm_scan_entry_put_ref(scan_db, 1340 cur_node, true, false); 1341 return QDF_STATUS_SUCCESS; 1342 } 1343 next_node = scm_get_next_node(scan_db, 1344 &scan_db->scan_hash_tbl[hash_idx], cur_node); 1345 cur_node = next_node; 1346 } 1347 1348 return QDF_STATUS_E_INVAL; 1349 } 1350