1 /* 2 * Copyright (c) 2017 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 41 /** 42 * scm_del_scan_node() - API to remove scan node from the list 43 * @list: hash list 44 * @scan_node: node to be removed 45 * 46 * This should be called while holding scan_db_lock. 47 * 48 * Return: void 49 */ 50 static void scm_del_scan_node(qdf_list_t *list, 51 struct scan_cache_node *scan_node) 52 { 53 QDF_STATUS status; 54 55 status = qdf_list_remove_node(list, &scan_node->node); 56 if (QDF_IS_STATUS_SUCCESS(status)) { 57 util_scan_free_cache_entry(scan_node->entry); 58 qdf_mem_free(scan_node); 59 } 60 } 61 62 /** 63 * scm_del_scan_node_from_db() - API to del the scan entry 64 * @scan_db: scan database 65 * @scan_entry:entry scan_node 66 * 67 * API to flush the scan entry. This should be called while 68 * holding scan_db_lock. 69 * 70 * Return: QDF status. 71 */ 72 static QDF_STATUS scm_del_scan_node_from_db(struct scan_dbs *scan_db, 73 struct scan_cache_node *scan_node) 74 { 75 QDF_STATUS status = QDF_STATUS_SUCCESS; 76 uint8_t hash_idx; 77 78 if (!scan_node) 79 return QDF_STATUS_E_INVAL; 80 81 hash_idx = SCAN_GET_HASH(scan_node->entry->bssid.bytes); 82 scm_del_scan_node(&scan_db->scan_hash_tbl[hash_idx], scan_node); 83 scan_db->num_entries--; 84 85 return status; 86 } 87 88 /** 89 * scm_scan_entry_get_ref() - api to increase ref count of scan entry 90 * @scan_node: scan node 91 * 92 * Return: void 93 */ 94 static void scm_scan_entry_get_ref(struct scan_cache_node *scan_node) 95 { 96 if (scan_node == NULL) { 97 scm_err("scan_node is NULL"); 98 QDF_ASSERT(0); 99 return; 100 } 101 qdf_atomic_inc(&scan_node->ref_cnt); 102 } 103 104 /** 105 * scm_scan_entry_put_ref() - Api to decrease ref count of scan entry 106 * and free if it become 0 107 * @scan_db: scan database 108 * @scan_node: scan node 109 * @lock_needed: if scan_db_lock is needed 110 * 111 * Return: void 112 */ 113 static void scm_scan_entry_put_ref(struct scan_dbs *scan_db, 114 struct scan_cache_node *scan_node, bool lock_needed) 115 { 116 117 if (!scan_node) { 118 scm_err("scan_node is NULL"); 119 QDF_ASSERT(0); 120 return; 121 } 122 123 if (!qdf_atomic_read(&scan_node->ref_cnt)) { 124 scm_err("scan_node ref cnt is 0"); 125 QDF_ASSERT(0); 126 return; 127 } 128 129 if (lock_needed) 130 qdf_spin_lock_bh(&scan_db->scan_db_lock); 131 132 /* Decrement ref count, free scan_node, if ref count == 0 */ 133 if (qdf_atomic_dec_and_test(&scan_node->ref_cnt)) 134 scm_del_scan_node_from_db(scan_db, scan_node); 135 136 if (lock_needed) 137 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 138 } 139 140 /** 141 * scm_add_scan_node() - API to add scan node 142 * @scan_db: data base 143 * @scan_node: node to be removed 144 * 145 * Return: void 146 */ 147 static void scm_add_scan_node(struct scan_dbs *scan_db, 148 struct scan_cache_node *scan_node) 149 { 150 uint8_t hash_idx; 151 152 hash_idx = 153 SCAN_GET_HASH(scan_node->entry->bssid.bytes); 154 155 qdf_spin_lock_bh(&scan_db->scan_db_lock); 156 qdf_atomic_init(&scan_node->ref_cnt); 157 scm_scan_entry_get_ref(scan_node); 158 qdf_list_insert_back(&scan_db->scan_hash_tbl[hash_idx], 159 &scan_node->node); 160 scan_db->num_entries++; 161 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 162 } 163 164 165 /** 166 * scm_get_next_node() - API get the next scan node from 167 * the list 168 * @list: hash list 169 * @scan_node: node to be removed 170 * 171 * API get the next node from the list. If cur_node is NULL 172 * it will return first node of the list 173 * 174 * Return: next scan cache node 175 */ 176 static struct scan_cache_node * 177 scm_get_next_node(struct scan_dbs *scan_db, 178 qdf_list_t *list, 179 struct scan_cache_node *cur_node) 180 { 181 struct scan_cache_node *next_node = NULL; 182 qdf_list_node_t *next_list = NULL; 183 184 qdf_spin_lock_bh(&scan_db->scan_db_lock); 185 if (cur_node) { 186 qdf_list_peek_next(list, 187 &cur_node->node, &next_list); 188 /* Decrement the ref count of the previous node */ 189 scm_scan_entry_put_ref(scan_db, 190 cur_node, false); 191 } else { 192 qdf_list_peek_front(list, &next_list); 193 } 194 /* Increase the ref count of the obtained node */ 195 if (next_list) { 196 next_node = qdf_container_of(next_list, 197 struct scan_cache_node, node); 198 scm_scan_entry_get_ref(next_node); 199 } 200 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 201 202 return next_node; 203 } 204 205 /** 206 * scm_check_and_age_out() - check and age out the old entries 207 * @scan_db: scan db 208 * @scan_node: node to check for age out 209 * 210 * Return: void 211 */ 212 static void scm_check_and_age_out(struct scan_dbs *scan_db, 213 struct scan_cache_node *node) 214 { 215 if (util_scan_entry_age(node->entry) >= 216 SCAN_CACHE_AGING_TIME) { 217 scm_err("Aging out BSSID: %pM with age %d ms", 218 node->entry->bssid.bytes, 219 util_scan_entry_age(node->entry)); 220 scm_scan_entry_put_ref(scan_db, node, true); 221 } 222 } 223 224 void scm_age_out_entries(struct scan_dbs *scan_db) 225 { 226 int i; 227 struct scan_cache_node *cur_node = NULL; 228 struct scan_cache_node *next_node = NULL; 229 230 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 231 cur_node = scm_get_next_node(scan_db, 232 &scan_db->scan_hash_tbl[i], NULL); 233 while (cur_node) { 234 scm_check_and_age_out(scan_db, cur_node); 235 next_node = scm_get_next_node(scan_db, 236 &scan_db->scan_hash_tbl[i], cur_node); 237 cur_node = next_node; 238 next_node = NULL; 239 } 240 } 241 } 242 243 /** 244 * scm_flush_oldest_entry() - flust out the oldest entry 245 * @scan_db: scan db from which oldest etry needs to be flushed 246 * 247 * Return: QDF_STATUS 248 */ 249 static QDF_STATUS scm_flush_oldest_entry(struct scan_dbs *scan_db) 250 { 251 int i; 252 struct scan_cache_node *oldest_node = NULL; 253 struct scan_cache_node *cur_node; 254 qdf_list_node_t *cur_list = NULL; 255 256 qdf_spin_lock_bh(&scan_db->scan_db_lock); 257 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 258 cur_node = NULL; 259 qdf_list_peek_front(&scan_db->scan_hash_tbl[i], 260 &cur_list); 261 /* 262 * Check only the first node if present as new 263 * entry are added to tail and thus first 264 * node is the oldest 265 */ 266 if (cur_list) { 267 cur_node = qdf_container_of(cur_list, 268 struct scan_cache_node, node); 269 if (!oldest_node || 270 (util_scan_entry_age(oldest_node->entry) < 271 util_scan_entry_age(cur_node->entry))) 272 oldest_node = cur_node; 273 } 274 } 275 scm_scan_entry_put_ref(scan_db, oldest_node, false); 276 qdf_spin_unlock_bh(&scan_db->scan_db_lock); 277 278 return QDF_STATUS_SUCCESS; 279 } 280 281 /** 282 * scm_update_alt_wcn_ie() - update the alternate WCN IE 283 * @from: copy from 284 * @dst: copy to 285 * 286 * Return: void 287 */ 288 static void scm_update_alt_wcn_ie(struct scan_cache_entry *from, 289 struct scan_cache_entry *dst) 290 { 291 uint32_t alt_wcn_ie_len; 292 293 if (from->frm_subtype == dst->frm_subtype) 294 return; 295 296 if (!from->ie_list.wcn && !dst->ie_list.wcn) 297 return; 298 299 /* Existing WCN IE is empty. */ 300 if (!from->ie_list.wcn) 301 return; 302 303 alt_wcn_ie_len = 2 + from->ie_list.wcn[1]; 304 if (alt_wcn_ie_len > WLAN_MAX_IE_LEN + 2) { 305 scm_err("invalid IE len"); 306 return; 307 } 308 309 if (!dst->alt_wcn_ie.ptr) { 310 /* allocate this additional buffer for alternate WCN IE */ 311 dst->alt_wcn_ie.ptr = qdf_mem_malloc(WLAN_MAX_IE_LEN + 2); 312 if (!dst->alt_wcn_ie.ptr) { 313 scm_err("failed to allocate memory"); 314 return; 315 } 316 } 317 qdf_mem_copy(dst->alt_wcn_ie.ptr, 318 from->ie_list.wcn, alt_wcn_ie_len); 319 dst->alt_wcn_ie.len = alt_wcn_ie_len; 320 } 321 322 /** 323 * scm_add_scan_entry() - add new scan entry to the database 324 * @scan_db: scan database 325 * @scan_params: new entry to be added 326 * 327 * Return: QDF_STATUS 328 */ 329 static QDF_STATUS scm_add_scan_entry(struct scan_dbs *scan_db, 330 struct scan_cache_entry *scan_params) 331 { 332 struct scan_cache_node *scan_node; 333 QDF_STATUS status; 334 335 if (scan_db->num_entries >= MAX_SCAN_CACHE_SIZE) { 336 status = scm_flush_oldest_entry(scan_db); 337 if (QDF_IS_STATUS_ERROR(status)) 338 return status; 339 } 340 341 scan_node = qdf_mem_malloc(sizeof(*scan_node)); 342 if (!scan_node) 343 return QDF_STATUS_E_NOMEM; 344 345 scan_node->entry = scan_params; 346 scm_add_scan_node(scan_db, scan_node); 347 348 return QDF_STATUS_SUCCESS; 349 } 350 351 /** 352 * scm_delete_duplicate_entry() - remove duplicate node entry 353 * @scan_db: scan database 354 * @scan_params: new entry to be added 355 * @scan_node: old entry to removed 356 * 357 * Remove duplicate node after copying required 358 * info into new entry 359 * 360 * Return: void 361 */ 362 static void scm_delete_duplicate_entry(struct scan_dbs *scan_db, 363 struct scan_cache_entry *scan_params, 364 struct scan_cache_node *scan_node) 365 { 366 struct scan_cache_entry *scan_entry; 367 368 scan_entry = scan_node->entry; 369 /* If old entry have the ssid but new entry does not */ 370 if (!scan_params->ssid.length && scan_entry->ssid.length) { 371 uint64_t time_gap; 372 373 /* 374 * New entry has a hidden SSID and old one has the SSID. 375 * Add the entry by using the ssid of the old entry 376 * only if diff of saved SSID time and current time is 377 * less than HIDDEN_SSID_TIME time. 378 * This will avoid issues in case AP changes its SSID 379 * while remain hidden. 380 */ 381 time_gap = 382 qdf_mc_timer_get_system_time() - 383 scan_entry->hidden_ssid_timestamp; 384 if (time_gap <= HIDDEN_SSID_TIME) { 385 scan_params->hidden_ssid_timestamp = 386 scan_entry->hidden_ssid_timestamp; 387 scan_params->ssid.length = 388 scan_entry->ssid.length; 389 qdf_mem_copy(scan_params->ssid.ssid, 390 scan_entry->ssid.ssid, 391 scan_params->ssid.length); 392 } 393 } 394 /* 395 * Use old value for rssi if beacon 396 * was heard on adjacent channel. 397 */ 398 if (scan_params->channel_mismatch) { 399 scan_params->rssi_raw = scan_entry->rssi_raw; 400 scan_params->rssi_timestamp = 401 scan_entry->rssi_timestamp; 402 } 403 /* copy wsn ie from scan_entry to scan_params*/ 404 scm_update_alt_wcn_ie(scan_entry, scan_params); 405 406 /* Mark delete the duplicate node */ 407 scm_scan_entry_put_ref(scan_db, scan_node, true); 408 } 409 410 /** 411 * scm_find_duplicate_and_del() - find duplicate entry if present 412 * and update it 413 * @scan_db: scan db 414 * @entry: input scan cache entry 415 * 416 * Return: true if entry is found and updated else false 417 */ 418 static bool 419 scm_find_duplicate_and_del(struct scan_dbs *scan_db, 420 struct scan_cache_entry *entry) 421 { 422 uint8_t hash_idx; 423 struct scan_cache_node *cur_node; 424 struct scan_cache_node *next_node = NULL; 425 426 hash_idx = SCAN_GET_HASH(entry->bssid.bytes); 427 428 cur_node = scm_get_next_node(scan_db, 429 &scan_db->scan_hash_tbl[hash_idx], NULL); 430 431 while (cur_node) { 432 if (util_is_scan_entry_match(entry, 433 cur_node->entry)) { 434 scm_delete_duplicate_entry(scan_db, 435 entry, cur_node); 436 scm_scan_entry_put_ref(scan_db, 437 cur_node, true); 438 return true; 439 } 440 next_node = scm_get_next_node(scan_db, 441 &scan_db->scan_hash_tbl[hash_idx], cur_node); 442 cur_node = next_node; 443 next_node = NULL; 444 } 445 446 return false; 447 } 448 449 /** 450 * scm_add_update_entry() - add or update scan entry 451 * @scan_db: scan database 452 * @scan_params: new received entry 453 * 454 * Return: QDF_STATUS 455 */ 456 static QDF_STATUS scm_add_update_entry(struct scan_dbs *scan_db, 457 struct scan_cache_entry *scan_params) 458 { 459 QDF_STATUS status; 460 461 /* SSID shouldn't be NULL in probe resp */ 462 if (scan_params->frm_subtype == 463 MGMT_SUBTYPE_PROBE_RESP && 464 !scan_params->ie_list.ssid) 465 return QDF_STATUS_E_INVAL; 466 467 /* CSA or ECSA present ignore */ 468 if (scan_params->ie_list.csa || 469 scan_params->ie_list.xcsa || 470 scan_params->ie_list.cswrp) 471 return QDF_STATUS_E_INVAL; 472 473 scm_find_duplicate_and_del(scan_db, scan_params); 474 status = scm_add_scan_entry(scan_db, scan_params); 475 476 return status; 477 } 478 479 QDF_STATUS scm_handle_bcn_probe(struct scheduler_msg *msg) 480 { 481 struct scan_bcn_probe_event *bcn; 482 struct wlan_objmgr_psoc *psoc; 483 struct wlan_objmgr_pdev *pdev = NULL; 484 struct scan_cache_entry *scan_entry; 485 struct wlan_scan_obj *scan_obj; 486 struct scan_dbs *scan_db; 487 QDF_STATUS status; 488 489 bcn = msg->bodyptr; 490 491 if (!bcn) { 492 scm_err("bcn is NULL"); 493 return QDF_STATUS_E_INVAL; 494 } 495 if (!bcn->rx_data) { 496 scm_err("rx_data iS NULL"); 497 status = QDF_STATUS_E_INVAL; 498 goto free_nbuf; 499 } 500 if (!bcn->buf) { 501 scm_err("buf is NULL"); 502 status = QDF_STATUS_E_INVAL; 503 goto free_nbuf; 504 } 505 506 psoc = bcn->psoc; 507 pdev = wlan_objmgr_get_pdev_by_id(psoc, 508 bcn->rx_data->pdev_id, WLAN_SCAN_ID); 509 if (!pdev) { 510 scm_err("pdev is NULL"); 511 status = QDF_STATUS_E_INVAL; 512 goto free_nbuf; 513 } 514 scan_obj = wlan_psoc_get_scan_obj(psoc); 515 if (!scan_obj) { 516 scm_err("scan_obj is NULL"); 517 status = QDF_STATUS_E_INVAL; 518 goto free_nbuf; 519 } 520 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 521 if (!scan_db) { 522 scm_err("scan_db is NULL"); 523 status = QDF_STATUS_E_INVAL; 524 goto free_nbuf; 525 } 526 527 if (qdf_nbuf_len(bcn->buf) <= 528 (sizeof(struct wlan_frame_hdr) + 529 offsetof(struct wlan_bcn_frame, ie))) { 530 scm_err("invalid beacon/probe length"); 531 status = QDF_STATUS_E_INVAL; 532 goto free_nbuf; 533 } 534 535 scan_entry = 536 util_scan_unpack_beacon_frame(qdf_nbuf_data(bcn->buf), 537 qdf_nbuf_len(bcn->buf), bcn->frm_type, 538 bcn->rx_data); 539 if (!scan_entry) { 540 scm_err("failed to unpack frame"); 541 status = QDF_STATUS_E_INVAL; 542 goto free_nbuf; 543 } 544 545 scm_info("Received %s from BSSID: %pM tsf_delta = %u Seq Num: %x ssid:%.*s", 546 (bcn->frm_type == MGMT_SUBTYPE_PROBE_RESP) ? 547 "Probe Rsp" : "Beacon", scan_entry->bssid.bytes, 548 scan_entry->tsf_delta, scan_entry->seq_num, 549 scan_entry->ssid.length, scan_entry->ssid.ssid); 550 551 if (scan_obj->cb.update_beacon) 552 scan_obj->cb.update_beacon(pdev, scan_entry); 553 554 if (scan_obj->cb.inform_beacon) 555 scan_obj->cb.inform_beacon(pdev, scan_entry); 556 557 status = scm_add_update_entry(scan_db, scan_entry); 558 if (QDF_IS_STATUS_ERROR(status)) { 559 util_scan_free_cache_entry(scan_entry); 560 scm_err("failed to add entry"); 561 goto free_nbuf; 562 } 563 564 free_nbuf: 565 if (pdev) 566 wlan_objmgr_pdev_release_ref(pdev, WLAN_SCAN_ID); 567 if (bcn->rx_data) 568 qdf_mem_free(bcn->rx_data); 569 if (bcn->buf) 570 qdf_nbuf_free(bcn->buf); 571 qdf_mem_free(bcn); 572 573 return status; 574 } 575 576 /** 577 * scm_list_insert_sorted() - add the entries in scan_list in sorted way 578 * @filter: scan filter 579 * @scan_node: node entry to be inserted 580 * @scan_list: Temp scan list 581 * 582 * Add the entries in scan_list in sorted way considering 583 * cap_val and prefer val. The node is copy of original scan entry and 584 * thus no lock is required. 585 * 586 * Return: void 587 */ 588 static void scm_list_insert_sorted(struct scan_filter *filter, 589 struct scan_cache_node *scan_node, 590 qdf_list_t *scan_list) 591 { 592 struct scan_cache_node *cur_node; 593 qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; 594 595 scan_node->entry->cap_val = 596 scm_get_bss_cap_value(filter, scan_node->entry); 597 598 scan_node->entry->prefer_value = 599 scm_get_bss_prefer_value(filter, scan_node->entry); 600 601 if (filter->num_of_pcl_channels) 602 scm_calc_pref_val_by_pcl(filter, scan_node->entry); 603 604 if (qdf_list_empty(scan_list)) { 605 qdf_list_insert_front(scan_list, &scan_node->node); 606 return; 607 } 608 609 qdf_list_peek_front(scan_list, &cur_lst); 610 611 while (cur_lst) { 612 cur_node = qdf_container_of(cur_lst, 613 struct scan_cache_node, node); 614 if (scm_is_better_bss(filter, 615 scan_node->entry, cur_node->entry)) { 616 qdf_list_insert_before(scan_list, 617 &scan_node->node, 618 &cur_node->node); 619 break; 620 } 621 qdf_list_peek_next(scan_list, 622 cur_lst, &next_lst); 623 next_lst = next_lst; 624 next_lst = NULL; 625 } 626 627 if (!cur_lst) 628 qdf_list_insert_back(scan_list, 629 &scan_node->node); 630 631 } 632 633 /** 634 * scm_scan_apply_filter_get_entry() - apply filter and get the 635 * scan entry 636 * @db_entry: scan entry 637 * @filter: filter to be applied 638 * @scan_list: scan list to which entry is added 639 * 640 * Return: QDF_STATUS 641 */ 642 static QDF_STATUS 643 scm_scan_apply_filter_get_entry(struct scan_cache_entry *db_entry, 644 struct scan_filter *filter, 645 qdf_list_t *scan_list) 646 { 647 struct scan_cache_node *scan_node = NULL; 648 struct security_info security = {0}; 649 bool match; 650 651 if (!filter) 652 match = true; 653 else 654 match = scm_filter_match(db_entry, filter, &security); 655 656 if (!match) 657 return QDF_STATUS_SUCCESS; 658 659 scan_node = qdf_mem_malloc(sizeof(*scan_node)); 660 if (!scan_node) 661 return QDF_STATUS_E_NOMEM; 662 663 scan_node->entry = 664 util_scan_copy_cache_entry(db_entry); 665 666 if (!scan_node->entry) { 667 qdf_mem_free(scan_node); 668 return QDF_STATUS_E_NOMEM; 669 } 670 671 qdf_mem_copy(&scan_node->entry->neg_sec_info, 672 &security, sizeof(scan_node->entry->neg_sec_info)); 673 674 if (!filter) 675 qdf_list_insert_front(scan_list, 676 &scan_node->node); 677 else 678 scm_list_insert_sorted(filter, scan_node, scan_list); 679 680 return QDF_STATUS_SUCCESS; 681 } 682 683 /** 684 * scm_get_results() - Iterate and get scan results 685 * @scan_db: scan db 686 * @filter: filter to be applied 687 * @scan_list: scan list to which entry is added 688 * 689 * Return: void 690 */ 691 static void scm_get_results(struct scan_dbs *scan_db, 692 struct scan_filter *filter, 693 qdf_list_t *scan_list) 694 { 695 int i; 696 struct scan_cache_node *cur_node; 697 struct scan_cache_node *next_node = NULL; 698 699 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 700 cur_node = scm_get_next_node(scan_db, 701 &scan_db->scan_hash_tbl[i], NULL); 702 while (cur_node) { 703 scm_scan_apply_filter_get_entry( 704 cur_node->entry, filter, scan_list); 705 next_node = scm_get_next_node(scan_db, 706 &scan_db->scan_hash_tbl[i], cur_node); 707 cur_node = next_node; 708 } 709 } 710 } 711 712 QDF_STATUS scm_purge_scan_results(qdf_list_t *scan_list) 713 { 714 QDF_STATUS status; 715 struct scan_cache_node *cur_node; 716 qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; 717 718 if (!scan_list) { 719 scm_err("scan_result is NULL"); 720 return QDF_STATUS_E_INVAL; 721 } 722 723 status = qdf_list_peek_front(scan_list, &cur_lst); 724 725 while (cur_lst) { 726 qdf_list_peek_next( 727 scan_list, cur_lst, &next_lst); 728 cur_node = qdf_container_of(cur_lst, 729 struct scan_cache_node, node); 730 status = qdf_list_remove_node(scan_list, 731 cur_lst); 732 if (QDF_IS_STATUS_SUCCESS(status)) { 733 util_scan_free_cache_entry(cur_node->entry); 734 qdf_mem_free(cur_node); 735 } 736 cur_lst = next_lst; 737 next_lst = NULL; 738 } 739 740 qdf_list_destroy(scan_list); 741 qdf_mem_free(scan_list); 742 743 return status; 744 } 745 746 qdf_list_t *scm_get_scan_result(struct wlan_objmgr_pdev *pdev, 747 struct scan_filter *filter) 748 { 749 struct wlan_objmgr_psoc *psoc; 750 struct scan_dbs *scan_db; 751 qdf_list_t *tmp_list; 752 753 if (!pdev) { 754 scm_err("pdev is NULL"); 755 return NULL; 756 } 757 758 psoc = wlan_pdev_get_psoc(pdev); 759 if (!psoc) { 760 scm_err("psoc is NULL"); 761 return NULL; 762 } 763 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 764 if (!scan_db) { 765 scm_err("scan_db is NULL"); 766 return NULL; 767 } 768 769 tmp_list = qdf_mem_malloc(sizeof(*tmp_list)); 770 if (!tmp_list) { 771 scm_err("failed tp allocate scan_result"); 772 return NULL; 773 } 774 qdf_list_create(tmp_list, 775 MAX_SCAN_CACHE_SIZE); 776 777 scm_age_out_entries(scan_db); 778 scm_get_results(scan_db, filter, tmp_list); 779 780 return tmp_list; 781 } 782 783 /** 784 * scm_iterate_db_and_call_func() - iterate and call the func 785 * @scan_db: scan db 786 * @func: func to be called 787 * @arg: func arg 788 * 789 * Return: QDF_STATUS 790 */ 791 static QDF_STATUS 792 scm_iterate_db_and_call_func(struct scan_dbs *scan_db, 793 scan_iterator_func func, void *arg) 794 { 795 int i; 796 QDF_STATUS status = QDF_STATUS_SUCCESS; 797 struct scan_cache_node *cur_node; 798 struct scan_cache_node *next_node = NULL; 799 800 if (!func) 801 return QDF_STATUS_E_INVAL; 802 803 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 804 cur_node = scm_get_next_node(scan_db, 805 &scan_db->scan_hash_tbl[i], NULL); 806 while (cur_node) { 807 status = func(arg, cur_node->entry); 808 if (QDF_IS_STATUS_ERROR(status)) { 809 scm_scan_entry_put_ref(scan_db, 810 cur_node, true); 811 return status; 812 } 813 next_node = scm_get_next_node(scan_db, 814 &scan_db->scan_hash_tbl[i], cur_node); 815 cur_node = next_node; 816 } 817 } 818 819 return status; 820 } 821 822 QDF_STATUS 823 scm_iterate_scan_db(struct wlan_objmgr_pdev *pdev, 824 scan_iterator_func func, void *arg) 825 { 826 struct wlan_objmgr_psoc *psoc; 827 struct scan_dbs *scan_db; 828 QDF_STATUS status; 829 830 if (!func) { 831 scm_err("func is NULL"); 832 return QDF_STATUS_E_INVAL; 833 } 834 835 if (!pdev) { 836 scm_err("pdev is NULL"); 837 return QDF_STATUS_E_INVAL; 838 } 839 840 psoc = wlan_pdev_get_psoc(pdev); 841 if (!psoc) { 842 scm_err("psoc is NULL"); 843 return QDF_STATUS_E_INVAL; 844 } 845 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 846 if (!scan_db) { 847 scm_err("scan_db is NULL"); 848 return QDF_STATUS_E_INVAL; 849 } 850 851 scm_age_out_entries(scan_db); 852 status = scm_iterate_db_and_call_func(scan_db, func, arg); 853 854 return status; 855 } 856 857 /** 858 * scm_scan_apply_filter_flush_entry() -flush scan entries depending 859 * on filter 860 * @scan_db: scan db 861 * @db_node: node on which filters are applied 862 * @filter: filter to be applied 863 * 864 * Return: QDF_STATUS 865 */ 866 static QDF_STATUS 867 scm_scan_apply_filter_flush_entry(struct scan_dbs *scan_db, 868 struct scan_cache_node *db_node, 869 struct scan_filter *filter) 870 { 871 struct security_info security = {0}; 872 bool match; 873 874 if (!filter) 875 match = true; 876 else 877 match = scm_filter_match(db_node->entry, filter, &security); 878 879 if (!match) 880 return QDF_STATUS_SUCCESS; 881 882 scm_scan_entry_put_ref(scan_db, db_node, true); 883 884 return QDF_STATUS_SUCCESS; 885 } 886 887 /** 888 * scm_flush_scan_entries() - API to flush scan entries depending on filters 889 * @scan_db: scan db 890 * @filter: filter 891 * 892 * Return: void 893 */ 894 static void scm_flush_scan_entries(struct scan_dbs *scan_db, 895 struct scan_filter *filter) 896 { 897 int i; 898 struct scan_cache_node *cur_node; 899 struct scan_cache_node *next_node = NULL; 900 901 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 902 cur_node = scm_get_next_node(scan_db, 903 &scan_db->scan_hash_tbl[i], NULL); 904 while (cur_node) { 905 scm_scan_apply_filter_flush_entry(scan_db, 906 cur_node, filter); 907 next_node = scm_get_next_node(scan_db, 908 &scan_db->scan_hash_tbl[i], cur_node); 909 cur_node = next_node; 910 } 911 } 912 } 913 914 QDF_STATUS scm_flush_results(struct wlan_objmgr_pdev *pdev, 915 struct scan_filter *filter) 916 { 917 struct wlan_objmgr_psoc *psoc; 918 struct scan_dbs *scan_db; 919 QDF_STATUS status = QDF_STATUS_SUCCESS; 920 921 if (!pdev) { 922 scm_err("pdev is NULL"); 923 return QDF_STATUS_E_INVAL; 924 } 925 926 psoc = wlan_pdev_get_psoc(pdev); 927 if (!psoc) { 928 scm_err("psoc is NULL"); 929 return QDF_STATUS_E_INVAL; 930 } 931 932 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 933 if (!scan_db) { 934 scm_err("scan_db is NULL"); 935 return QDF_STATUS_E_INVAL; 936 } 937 938 scm_flush_scan_entries(scan_db, filter); 939 940 return status; 941 } 942 943 /** 944 * scm_filter_channels() - Remove entries not belonging to channel list 945 * @scan_db: scan db 946 * @db_node: node on which filters are applied 947 * @chan_list: valid channel list 948 * @num_chan: number of channels 949 * 950 * Return: QDF_STATUS 951 */ 952 static void scm_filter_channels(struct scan_dbs *scan_db, 953 struct scan_cache_node *db_node, 954 uint8_t *chan_list, uint32_t num_chan) 955 { 956 int i; 957 bool match = false; 958 959 for (i = 0; i < num_chan; i++) { 960 if (chan_list[i] == 961 util_scan_entry_channel_num(db_node->entry)) { 962 match = true; 963 break; 964 } 965 } 966 967 if (!match) 968 scm_scan_entry_put_ref(scan_db, db_node, true); 969 970 } 971 972 void scm_filter_valid_channel(struct wlan_objmgr_pdev *pdev, 973 uint8_t *chan_list, uint32_t num_chan) 974 { 975 int i; 976 struct wlan_objmgr_psoc *psoc; 977 struct scan_dbs *scan_db; 978 struct scan_cache_node *cur_node; 979 struct scan_cache_node *next_node = NULL; 980 981 scm_info("num_chan = %d", num_chan); 982 983 if (!pdev) { 984 scm_err("pdev is NULL"); 985 return; 986 } 987 988 psoc = wlan_pdev_get_psoc(pdev); 989 if (!psoc) { 990 scm_err("psoc is NULL"); 991 return; 992 } 993 994 scan_db = wlan_pdev_get_scan_db(psoc, pdev); 995 if (!scan_db) { 996 scm_err("scan_db is NULL"); 997 return; 998 } 999 1000 for (i = 0 ; i < SCAN_HASH_SIZE; i++) { 1001 cur_node = scm_get_next_node(scan_db, 1002 &scan_db->scan_hash_tbl[i], NULL); 1003 while (cur_node) { 1004 scm_filter_channels(scan_db, 1005 cur_node, chan_list, num_chan); 1006 next_node = scm_get_next_node(scan_db, 1007 &scan_db->scan_hash_tbl[i], cur_node); 1008 cur_node = next_node; 1009 } 1010 } 1011 } 1012 1013 QDF_STATUS scm_scan_register_bcn_cb(struct wlan_objmgr_psoc *psoc, 1014 update_beacon_cb cb, enum scan_cb_type type) 1015 { 1016 struct wlan_scan_obj *scan_obj; 1017 1018 scan_obj = wlan_psoc_get_scan_obj(psoc); 1019 if (!scan_obj) { 1020 scm_err("scan obj is NULL"); 1021 return QDF_STATUS_E_INVAL; 1022 } 1023 switch (type) { 1024 case SCAN_CB_TYPE_INFORM_BCN: 1025 scan_obj->cb.inform_beacon = cb; 1026 break; 1027 case SCAN_CB_TYPE_UPDATE_BCN: 1028 scan_obj->cb.update_beacon = cb; 1029 break; 1030 default: 1031 scm_err("invalid cb type %d", type); 1032 } 1033 1034 return QDF_STATUS_SUCCESS; 1035 } 1036 1037 QDF_STATUS scm_db_init(struct wlan_objmgr_psoc *psoc) 1038 { 1039 int i, j; 1040 struct scan_dbs *scan_db; 1041 1042 if (!psoc) { 1043 scm_err("psoc is NULL"); 1044 return QDF_STATUS_E_INVAL; 1045 } 1046 1047 /* Initialize the scan database per pdev */ 1048 for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) { 1049 scan_db = wlan_pdevid_get_scan_db(psoc, i); 1050 if (!scan_db) { 1051 scm_err("scan_db is NULL %d", i); 1052 continue; 1053 } 1054 scan_db->num_entries = 0; 1055 qdf_spinlock_create(&scan_db->scan_db_lock); 1056 for (j = 0; j < SCAN_HASH_SIZE; j++) 1057 qdf_list_create(&scan_db->scan_hash_tbl[j], 1058 MAX_SCAN_CACHE_SIZE); 1059 } 1060 1061 return QDF_STATUS_SUCCESS; 1062 } 1063 1064 QDF_STATUS scm_db_deinit(struct wlan_objmgr_psoc *psoc) 1065 { 1066 int i, j; 1067 struct scan_dbs *scan_db; 1068 1069 if (!psoc) { 1070 scm_err("scan obj is NULL"); 1071 return QDF_STATUS_E_INVAL; 1072 } 1073 1074 /* Initialize the scan database per pdev */ 1075 for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) { 1076 scan_db = wlan_pdevid_get_scan_db(psoc, i); 1077 if (!scan_db) { 1078 scm_err("scan_db is NULL %d", i); 1079 continue; 1080 } 1081 1082 scm_flush_scan_entries(scan_db, NULL); 1083 for (j = 0; j < SCAN_HASH_SIZE; j++) 1084 qdf_list_destroy(&scan_db->scan_hash_tbl[j]); 1085 qdf_spinlock_destroy(&scan_db->scan_db_lock); 1086 } 1087 1088 return QDF_STATUS_SUCCESS; 1089 } 1090