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