xref: /wlan-dirver/qca-wifi-host-cmn/umac/scan/core/src/wlan_scan_cache_db.c (revision a175314c51a4ce5cec2835cc8a8c7dc0c1810915)
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 =
446 			qdf_mem_malloc_atomic(WLAN_MAX_IE_LEN + 2);
447 		if (!dst->alt_wcn_ie.ptr) {
448 			scm_err("failed to allocate memory");
449 			return;
450 		}
451 	}
452 	qdf_mem_copy(dst->alt_wcn_ie.ptr,
453 		from->ie_list.wcn, alt_wcn_ie_len);
454 	dst->alt_wcn_ie.len = alt_wcn_ie_len;
455 }
456 
457 /**
458  * scm_update_mlme_info() - update mlme info
459  * @src: source scan entry
460  * @dest: destination scan entry
461  *
462  * Return: void
463  */
464 static inline void
465 scm_update_mlme_info(struct scan_cache_entry *src,
466 	struct scan_cache_entry *dest)
467 {
468 	qdf_mem_copy(&dest->mlme_info, &src->mlme_info,
469 		sizeof(struct mlme_info));
470 }
471 
472 /**
473  * scm_copy_info_from_dup_entry() - copy duplicate node info
474  * to new scan entry
475  * @scan_db: scan database
476  * @scan_params: new entry to be added
477  * @scan_node: duplicate entry
478  *
479  * Copy duplicate node info to new entry.
480  *
481  * Return: void
482  */
483 static void
484 scm_copy_info_from_dup_entry(struct scan_dbs *scan_db,
485 			     struct scan_cache_entry *scan_params,
486 			     struct scan_cache_node *scan_node)
487 {
488 	struct scan_cache_entry *scan_entry;
489 	uint64_t time_gap;
490 
491 	scan_entry = scan_node->entry;
492 	/* If old entry have the ssid but new entry does not */
493 	if (!scan_params->ssid.length && scan_entry->ssid.length) {
494 		/*
495 		 * New entry has a hidden SSID and old one has the SSID.
496 		 * Add the entry by using the ssid of the old entry
497 		 * only if diff of saved SSID time and current time is
498 		 * less than HIDDEN_SSID_TIME time.
499 		 * This will avoid issues in case AP changes its SSID
500 		 * while remain hidden.
501 		 */
502 		time_gap =
503 			qdf_mc_timer_get_system_time() -
504 			scan_entry->hidden_ssid_timestamp;
505 		if (time_gap <= HIDDEN_SSID_TIME) {
506 			scan_params->hidden_ssid_timestamp =
507 				scan_entry->hidden_ssid_timestamp;
508 			scan_params->ssid.length =
509 				scan_entry->ssid.length;
510 			qdf_mem_copy(scan_params->ssid.ssid,
511 				scan_entry->ssid.ssid,
512 				scan_params->ssid.length);
513 		}
514 	}
515 
516 	/*
517 	 * Due to Rx sensitivity issue, sometime beacons are seen on adjacent
518 	 * channel so workaround in software is needed. If DS params or HT info
519 	 * are present driver can get proper channel info from these IEs and set
520 	 * channel_mismatch so that the older RSSI values are used in new entry.
521 	 *
522 	 * For the cases where DS params and HT info is not present, driver
523 	 * needs to check below conditions to get proper channel and set
524 	 * channel_mismatch so that the older RSSI values are used in new entry:
525 	 *   -- The old entry channel and new entry channel are not same
526 	 *   -- RSSI is less than -80, this indicate that the signal has leaked
527 	 *       in adjacent channel.
528 	 */
529 	if ((scan_params->frm_subtype == MGMT_SUBTYPE_BEACON) &&
530 	    !util_scan_entry_htinfo(scan_params) &&
531 	    !util_scan_entry_ds_param(scan_params) &&
532 	    (scan_params->channel.chan_idx != scan_entry->channel.chan_idx) &&
533 	    (scan_params->rssi_raw  < ADJACENT_CHANNEL_RSSI_THRESHOLD)) {
534 		scan_params->channel.chan_idx = scan_entry->channel.chan_idx;
535 		scan_params->channel_mismatch = true;
536 	}
537 
538 	/* Use old value for rssi if beacon was heard on adjacent channel. */
539 	if (scan_params->channel_mismatch) {
540 		scan_params->rssi_raw = scan_entry->rssi_raw;
541 		scan_params->avg_rssi = scan_entry->avg_rssi;
542 		scan_params->rssi_timestamp =
543 			scan_entry->rssi_timestamp;
544 	} else {
545 		/* If elapsed time since last rssi update for this
546 		 * entry is smaller than a thresold, calculate a
547 		 * running average of the RSSI values.
548 		 * Otherwise new frames RSSI is more representive
549 		 * of the signal strength.
550 		 */
551 		time_gap =
552 			scan_params->scan_entry_time -
553 			scan_entry->rssi_timestamp;
554 		if (time_gap > WLAN_RSSI_AVERAGING_TIME)
555 			scan_params->avg_rssi =
556 				WLAN_RSSI_IN(scan_params->rssi_raw);
557 		else {
558 			/* Copy previous average rssi to new entry */
559 			scan_params->avg_rssi = scan_entry->avg_rssi;
560 			/* Average with previous samples */
561 			WLAN_RSSI_LPF(scan_params->avg_rssi,
562 					scan_params->rssi_raw);
563 		}
564 
565 		scan_params->rssi_timestamp = scan_params->scan_entry_time;
566 	}
567 
568 	/* copy wsn ie from scan_entry to scan_params*/
569 	scm_update_alt_wcn_ie(scan_entry, scan_params);
570 
571 	/* copy mlme info from scan_entry to scan_params*/
572 	scm_update_mlme_info(scan_entry, scan_params);
573 }
574 
575 /**
576  * scm_find_duplicate() - find duplicate entry,
577  * if present, add input scan entry before it and delete
578  * duplicate entry. otherwise add entry to tail
579  * @scan_db: scan db
580  * @entry: input scan cache entry
581  * @dup_node: node before which new entry to be added
582  *
583  * ref_cnt is taken for dup_node, caller should release ref taken
584  * if returns true.
585  *
586  * Return: bool
587  */
588 static bool
589 scm_find_duplicate(struct scan_dbs *scan_db,
590 		   struct scan_cache_entry *entry,
591 		   struct scan_cache_node **dup_node)
592 {
593 	uint8_t hash_idx;
594 	struct scan_cache_node *cur_node;
595 	struct scan_cache_node *next_node = NULL;
596 
597 	hash_idx = SCAN_GET_HASH(entry->bssid.bytes);
598 
599 	cur_node = scm_get_next_node(scan_db,
600 				     &scan_db->scan_hash_tbl[hash_idx],
601 				     NULL);
602 
603 	while (cur_node) {
604 		if (util_is_scan_entry_match(entry,
605 		   cur_node->entry)) {
606 			scm_copy_info_from_dup_entry(scan_db, entry, cur_node);
607 			*dup_node = cur_node;
608 			return true;
609 		}
610 		next_node = scm_get_next_node(scan_db,
611 			 &scan_db->scan_hash_tbl[hash_idx], cur_node);
612 		cur_node = next_node;
613 		next_node = NULL;
614 	}
615 
616 	return false;
617 }
618 
619 /**
620  * scm_add_update_entry() - add or update scan entry
621  * @psoc: psoc ptr
622  * @pdev: pdev pointer
623  * @scan_params: new received entry
624  *
625  * Return: QDF_STATUS
626  */
627 static QDF_STATUS scm_add_update_entry(struct wlan_objmgr_psoc *psoc,
628 	struct wlan_objmgr_pdev *pdev, struct scan_cache_entry *scan_params)
629 {
630 	struct scan_cache_node *dup_node = NULL;
631 	struct scan_cache_node *scan_node = NULL;
632 	bool is_dup_found = false;
633 	QDF_STATUS status;
634 	struct scan_dbs *scan_db;
635 	struct wlan_scan_obj *scan_obj;
636 
637 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
638 	if (!scan_db) {
639 		scm_err("scan_db is NULL");
640 		return QDF_STATUS_E_INVAL;
641 	}
642 
643 	scan_obj = wlan_psoc_get_scan_obj(psoc);
644 	if (!scan_obj) {
645 		scm_err("scan_obj is NULL");
646 		return QDF_STATUS_E_INVAL;
647 	}
648 
649 	if (scan_params->frm_subtype ==
650 	   MGMT_SUBTYPE_PROBE_RESP &&
651 	   !scan_params->ie_list.ssid)
652 		scm_info("Probe resp doesn't contain SSID");
653 
654 
655 	if (scan_params->ie_list.csa ||
656 	   scan_params->ie_list.xcsa ||
657 	   scan_params->ie_list.cswrp)
658 		scm_info("CSA IE present for BSSID: %pM",
659 			scan_params->bssid.bytes);
660 
661 	is_dup_found = scm_find_duplicate(scan_db, scan_params, &dup_node);
662 
663 	if (scan_obj->cb.inform_beacon)
664 		scan_obj->cb.inform_beacon(pdev, scan_params);
665 
666 	if (scan_db->num_entries >= MAX_SCAN_CACHE_SIZE) {
667 		status = scm_flush_oldest_entry(scan_db);
668 		if (QDF_IS_STATUS_ERROR(status)) {
669 			/* release ref taken for dup node */
670 			if (is_dup_found)
671 				scm_scan_entry_put_ref(scan_db, dup_node, true);
672 			return status;
673 		}
674 	}
675 
676 	scan_node = qdf_mem_malloc(sizeof(*scan_node));
677 	if (!scan_node) {
678 		/* release ref taken for dup node */
679 		if (is_dup_found)
680 			scm_scan_entry_put_ref(scan_db, dup_node, true);
681 		return QDF_STATUS_E_NOMEM;
682 	}
683 
684 	scan_node->entry = scan_params;
685 	qdf_spin_lock_bh(&scan_db->scan_db_lock);
686 	scm_add_scan_node(scan_db, scan_node, dup_node);
687 
688 	if (is_dup_found) {
689 		/* release ref taken for dup node and delete it */
690 		scm_scan_entry_del(scan_db, dup_node);
691 		scm_scan_entry_put_ref(scan_db, dup_node, false);
692 	}
693 	qdf_spin_unlock_bh(&scan_db->scan_db_lock);
694 
695 	return QDF_STATUS_SUCCESS;
696 }
697 
698 QDF_STATUS scm_handle_bcn_probe(struct scheduler_msg *msg)
699 {
700 	struct scan_bcn_probe_event *bcn;
701 	struct wlan_objmgr_psoc *psoc;
702 	struct wlan_objmgr_pdev *pdev = NULL;
703 	struct scan_cache_entry *scan_entry;
704 	struct wlan_scan_obj *scan_obj;
705 	qdf_list_t *scan_list = NULL;
706 	QDF_STATUS status = QDF_STATUS_SUCCESS;
707 	uint32_t list_count, i;
708 	qdf_list_node_t *next_node = NULL;
709 	struct scan_cache_node *scan_node;
710 
711 	bcn = msg->bodyptr;
712 	if (!bcn) {
713 		scm_err("bcn is NULL");
714 		return QDF_STATUS_E_INVAL;
715 	}
716 	if (!bcn->rx_data) {
717 		scm_err("rx_data iS NULL");
718 		status = QDF_STATUS_E_INVAL;
719 		goto free_nbuf;
720 	}
721 	if (!bcn->buf) {
722 		scm_err("buf is NULL");
723 		status = QDF_STATUS_E_INVAL;
724 		goto free_nbuf;
725 	}
726 
727 	psoc = bcn->psoc;
728 	pdev = wlan_objmgr_get_pdev_by_id(psoc,
729 			   bcn->rx_data->pdev_id, WLAN_SCAN_ID);
730 	if (!pdev) {
731 		scm_err("pdev is NULL");
732 		status = QDF_STATUS_E_INVAL;
733 		goto free_nbuf;
734 	}
735 	scan_obj = wlan_psoc_get_scan_obj(psoc);
736 	if (!scan_obj) {
737 		scm_err("scan_obj is NULL");
738 		status = QDF_STATUS_E_INVAL;
739 		goto free_nbuf;
740 	}
741 
742 	if (qdf_nbuf_len(bcn->buf) <=
743 	   (sizeof(struct wlan_frame_hdr) +
744 	   offsetof(struct wlan_bcn_frame, ie))) {
745 		scm_debug("invalid beacon/probe length");
746 		status = QDF_STATUS_E_INVAL;
747 		goto free_nbuf;
748 	}
749 
750 	scan_list =
751 		 util_scan_unpack_beacon_frame(pdev, qdf_nbuf_data(bcn->buf),
752 			qdf_nbuf_len(bcn->buf), bcn->frm_type,
753 			bcn->rx_data);
754 	if (!scan_list || qdf_list_empty(scan_list)) {
755 		scm_debug("failed to unpack frame");
756 		status = QDF_STATUS_E_INVAL;
757 		goto free_nbuf;
758 	}
759 
760 	list_count = qdf_list_size(scan_list);
761 	for (i = 0; i < list_count; i++) {
762 		status = qdf_list_remove_front(scan_list, &next_node);
763 		if (QDF_IS_STATUS_ERROR(status) || next_node == NULL) {
764 			scm_debug("failed to unpack frame");
765 			status = QDF_STATUS_E_INVAL;
766 			goto free_nbuf;
767 		}
768 
769 		scan_node = qdf_container_of(next_node,
770 			struct scan_cache_node, node);
771 
772 		scan_entry = scan_node->entry;
773 
774 		scm_debug("Received %s from BSSID: %pM tsf_delta = %u Seq Num: %x  ssid:%.*s, rssi: %d",
775 			  (bcn->frm_type == MGMT_SUBTYPE_PROBE_RESP) ?
776 			  "Probe Rsp" : "Beacon", scan_entry->bssid.bytes,
777 			  scan_entry->tsf_delta, scan_entry->seq_num,
778 			  scan_entry->ssid.length, scan_entry->ssid.ssid,
779 			  scan_entry->rssi_raw);
780 
781 		if (scan_obj->drop_bcn_on_chan_mismatch &&
782 			scan_entry->channel_mismatch) {
783 			scm_debug("Drop frame, as channel mismatch Received for from BSSID: %pM ",
784 				   scan_entry->bssid.bytes);
785 			util_scan_free_cache_entry(scan_entry);
786 			qdf_mem_free(scan_node);
787 			continue;
788 		}
789 
790 		if (scan_obj->cb.update_beacon)
791 			scan_obj->cb.update_beacon(pdev, scan_entry);
792 
793 		if (wlan_reg_11d_enabled_on_host(psoc))
794 			scm_11d_handle_country_info(psoc, pdev, scan_entry);
795 
796 		status = scm_add_update_entry(psoc, pdev, scan_entry);
797 		if (QDF_IS_STATUS_ERROR(status)) {
798 			scm_debug("failed to add entry for BSSID: %pM",
799 				  scan_entry->bssid.bytes);
800 			util_scan_free_cache_entry(scan_entry);
801 			qdf_mem_free(scan_node);
802 			continue;
803 		}
804 
805 		qdf_mem_free(scan_node);
806 	}
807 
808 free_nbuf:
809 	if (scan_list)
810 		qdf_mem_free(scan_list);
811 	if (bcn->psoc)
812 		wlan_objmgr_psoc_release_ref(bcn->psoc, WLAN_SCAN_ID);
813 	if (pdev)
814 		wlan_objmgr_pdev_release_ref(pdev, WLAN_SCAN_ID);
815 	if (bcn->rx_data)
816 		qdf_mem_free(bcn->rx_data);
817 	if (bcn->buf)
818 		qdf_nbuf_free(bcn->buf);
819 	qdf_mem_free(bcn);
820 
821 	return status;
822 }
823 
824 /**
825  * scm_list_insert_sorted() - add the entries in scan_list in sorted way
826  * @psoc: psoc ptr
827  * @filter: scan filter
828  * @scan_node: node entry to be inserted
829  * @scan_list: Temp scan list
830  *
831  * Add the entries in scan_list in sorted way considering
832  * cap_val and prefer val. The node is copy of original scan entry and
833  * thus no lock is required.
834  *
835  * Return: void
836  */
837 static void scm_list_insert_sorted(struct wlan_objmgr_psoc *psoc,
838 	struct scan_filter *filter,
839 	struct scan_cache_node *scan_node,
840 	qdf_list_t *scan_list)
841 {
842 	struct scan_cache_node *cur_node;
843 	qdf_list_node_t *cur_lst = NULL, *next_lst = NULL;
844 	struct scan_default_params *params;
845 	int pcl_chan_weight = 0;
846 
847 	params = wlan_scan_psoc_get_def_params(psoc);
848 	if (!params) {
849 		scm_err("wlan_scan_psoc_get_def_params failed");
850 		return;
851 	}
852 
853 	if (filter->num_of_pcl_channels > 0 &&
854 			(scan_node->entry->rssi_raw > SCM_PCL_RSSI_THRESHOLD)) {
855 		if (scm_get_pcl_weight_of_channel(
856 					scan_node->entry->channel.chan_idx,
857 					filter, &pcl_chan_weight,
858 					filter->pcl_weight_list)) {
859 			scm_debug("pcl channel %d pcl_chan_weight %d",
860 					scan_node->entry->channel.chan_idx,
861 					pcl_chan_weight);
862 		}
863 	}
864 	if (params->is_bssid_hint_priority &&
865 	    !qdf_mem_cmp(filter->bssid_hint.bytes,
866 			 scan_node->entry->bssid.bytes,
867 			 QDF_MAC_ADDR_SIZE))
868 		scan_node->entry->bss_score = BEST_CANDIDATE_MAX_BSS_SCORE;
869 	else
870 		scm_calculate_bss_score(psoc, params,
871 					scan_node->entry, pcl_chan_weight);
872 
873 	if (qdf_list_empty(scan_list)) {
874 		qdf_list_insert_front(scan_list, &scan_node->node);
875 		return;
876 	}
877 
878 	qdf_list_peek_front(scan_list, &cur_lst);
879 
880 	while (cur_lst) {
881 		cur_node = qdf_container_of(cur_lst,
882 				struct scan_cache_node, node);
883 		if (scm_is_better_bss(params,
884 		   scan_node->entry, cur_node->entry)) {
885 			qdf_list_insert_before(scan_list,
886 				&scan_node->node,
887 				&cur_node->node);
888 			break;
889 		}
890 		qdf_list_peek_next(scan_list,
891 			cur_lst, &next_lst);
892 		cur_lst = next_lst;
893 		next_lst = NULL;
894 	}
895 
896 	if (!cur_lst)
897 		qdf_list_insert_back(scan_list,
898 			&scan_node->node);
899 
900 }
901 
902 /**
903  * scm_scan_apply_filter_get_entry() - apply filter and get the
904  * scan entry
905  * @psoc: psoc pointer
906  * @db_entry: scan entry
907  * @filter: filter to be applied
908  * @scan_list: scan list to which entry is added
909  *
910  * Return: QDF_STATUS
911  */
912 static QDF_STATUS
913 scm_scan_apply_filter_get_entry(struct wlan_objmgr_psoc *psoc,
914 	struct scan_cache_entry *db_entry,
915 	struct scan_filter *filter,
916 	qdf_list_t *scan_list)
917 {
918 	struct scan_cache_node *scan_node = NULL;
919 	struct security_info security = {0};
920 	bool match;
921 
922 	if (!filter)
923 		match = true;
924 	else
925 		match = scm_filter_match(psoc, db_entry,
926 					filter, &security);
927 
928 	if (!match)
929 		return QDF_STATUS_SUCCESS;
930 
931 	scan_node = qdf_mem_malloc_atomic(sizeof(*scan_node));
932 	if (!scan_node)
933 		return QDF_STATUS_E_NOMEM;
934 
935 	scan_node->entry =
936 		util_scan_copy_cache_entry(db_entry);
937 
938 	if (!scan_node->entry) {
939 		qdf_mem_free(scan_node);
940 		return QDF_STATUS_E_NOMEM;
941 	}
942 
943 	qdf_mem_copy(&scan_node->entry->neg_sec_info,
944 		&security, sizeof(scan_node->entry->neg_sec_info));
945 
946 	if (!filter || !filter->bss_scoring_required)
947 		qdf_list_insert_front(scan_list,
948 			&scan_node->node);
949 	else
950 		scm_list_insert_sorted(psoc, filter, scan_node, scan_list);
951 
952 	return QDF_STATUS_SUCCESS;
953 }
954 
955 /**
956  * scm_get_results() - Iterate and get scan results
957  * @psoc: psoc ptr
958  * @scan_db: scan db
959  * @filter: filter to be applied
960  * @scan_list: scan list to which entry is added
961  *
962  * Return: void
963  */
964 static void scm_get_results(struct wlan_objmgr_psoc *psoc,
965 	struct scan_dbs *scan_db, struct scan_filter *filter,
966 	qdf_list_t *scan_list)
967 {
968 	int i, count;
969 	struct scan_cache_node *cur_node;
970 	struct scan_cache_node *next_node = NULL;
971 
972 	for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
973 		cur_node = scm_get_next_node(scan_db,
974 			   &scan_db->scan_hash_tbl[i], NULL);
975 		count = qdf_list_size(&scan_db->scan_hash_tbl[i]);
976 		if (!count)
977 			continue;
978 		while (cur_node) {
979 			scm_scan_apply_filter_get_entry(psoc,
980 				cur_node->entry, filter, scan_list);
981 			next_node = scm_get_next_node(scan_db,
982 				&scan_db->scan_hash_tbl[i], cur_node);
983 			cur_node = next_node;
984 		}
985 	}
986 }
987 
988 QDF_STATUS scm_purge_scan_results(qdf_list_t *scan_list)
989 {
990 	QDF_STATUS status;
991 	struct scan_cache_node *cur_node;
992 	qdf_list_node_t *cur_lst = NULL, *next_lst = NULL;
993 
994 	if (!scan_list) {
995 		scm_err("scan_result is NULL");
996 		return QDF_STATUS_E_INVAL;
997 	}
998 
999 	status = qdf_list_peek_front(scan_list, &cur_lst);
1000 
1001 	while (cur_lst) {
1002 		qdf_list_peek_next(
1003 			scan_list, cur_lst, &next_lst);
1004 		cur_node = qdf_container_of(cur_lst,
1005 			struct scan_cache_node, node);
1006 		status = qdf_list_remove_node(scan_list,
1007 					cur_lst);
1008 		if (QDF_IS_STATUS_SUCCESS(status)) {
1009 			util_scan_free_cache_entry(cur_node->entry);
1010 			qdf_mem_free(cur_node);
1011 		}
1012 		cur_lst = next_lst;
1013 		next_lst = NULL;
1014 	}
1015 
1016 	qdf_list_destroy(scan_list);
1017 	qdf_mem_free(scan_list);
1018 
1019 	return status;
1020 }
1021 
1022 qdf_list_t *scm_get_scan_result(struct wlan_objmgr_pdev *pdev,
1023 	struct scan_filter *filter)
1024 {
1025 	struct wlan_objmgr_psoc *psoc;
1026 	struct scan_dbs *scan_db;
1027 	qdf_list_t *tmp_list;
1028 
1029 	if (!pdev) {
1030 		scm_err("pdev is NULL");
1031 		return NULL;
1032 	}
1033 
1034 	psoc = wlan_pdev_get_psoc(pdev);
1035 	if (!psoc) {
1036 		scm_err("psoc is NULL");
1037 		return NULL;
1038 	}
1039 
1040 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
1041 	if (!scan_db) {
1042 		scm_err("scan_db is NULL");
1043 		return NULL;
1044 	}
1045 
1046 	tmp_list = qdf_mem_malloc_atomic(sizeof(*tmp_list));
1047 	if (!tmp_list) {
1048 		scm_err("failed tp allocate scan_result");
1049 		return NULL;
1050 	}
1051 	qdf_list_create(tmp_list,
1052 			MAX_SCAN_CACHE_SIZE);
1053 	scm_age_out_entries(psoc, scan_db);
1054 	scm_get_results(psoc, scan_db, filter, tmp_list);
1055 
1056 	return tmp_list;
1057 }
1058 
1059 /**
1060  * scm_iterate_db_and_call_func() - iterate and call the func
1061  * @scan_db: scan db
1062  * @func: func to be called
1063  * @arg: func arg
1064  *
1065  * Return: QDF_STATUS
1066  */
1067 static QDF_STATUS
1068 scm_iterate_db_and_call_func(struct scan_dbs *scan_db,
1069 	scan_iterator_func func, void *arg)
1070 {
1071 	int i;
1072 	QDF_STATUS status = QDF_STATUS_SUCCESS;
1073 	struct scan_cache_node *cur_node;
1074 	struct scan_cache_node *next_node = NULL;
1075 
1076 	if (!func)
1077 		return QDF_STATUS_E_INVAL;
1078 
1079 	for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
1080 		cur_node = scm_get_next_node(scan_db,
1081 			&scan_db->scan_hash_tbl[i], NULL);
1082 		while (cur_node) {
1083 			status = func(arg, cur_node->entry);
1084 			if (QDF_IS_STATUS_ERROR(status)) {
1085 				scm_scan_entry_put_ref(scan_db,
1086 					cur_node, true);
1087 				return status;
1088 			}
1089 			next_node = scm_get_next_node(scan_db,
1090 				&scan_db->scan_hash_tbl[i], cur_node);
1091 			cur_node = next_node;
1092 		}
1093 	}
1094 
1095 	return status;
1096 }
1097 
1098 QDF_STATUS
1099 scm_iterate_scan_db(struct wlan_objmgr_pdev *pdev,
1100 	scan_iterator_func func, void *arg)
1101 {
1102 	struct wlan_objmgr_psoc *psoc;
1103 	struct scan_dbs *scan_db;
1104 	QDF_STATUS status;
1105 
1106 	if (!func) {
1107 		scm_err("func is NULL");
1108 		return QDF_STATUS_E_INVAL;
1109 	}
1110 
1111 	if (!pdev) {
1112 		scm_err("pdev is NULL");
1113 		return QDF_STATUS_E_INVAL;
1114 	}
1115 
1116 	psoc = wlan_pdev_get_psoc(pdev);
1117 	if (!psoc) {
1118 		scm_err("psoc is NULL");
1119 		return QDF_STATUS_E_INVAL;
1120 	}
1121 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
1122 	if (!scan_db) {
1123 		scm_err("scan_db is NULL");
1124 		return QDF_STATUS_E_INVAL;
1125 	}
1126 
1127 	scm_age_out_entries(psoc, scan_db);
1128 	status = scm_iterate_db_and_call_func(scan_db, func, arg);
1129 
1130 	return status;
1131 }
1132 
1133 /**
1134  * scm_scan_apply_filter_flush_entry() -flush scan entries depending
1135  * on filter
1136  * @psoc: psoc ptr
1137  * @scan_db: scan db
1138  * @db_node: node on which filters are applied
1139  * @filter: filter to be applied
1140  *
1141  * Return: QDF_STATUS
1142  */
1143 static QDF_STATUS
1144 scm_scan_apply_filter_flush_entry(struct wlan_objmgr_psoc *psoc,
1145 	struct scan_dbs *scan_db,
1146 	struct scan_cache_node *db_node,
1147 	struct scan_filter *filter)
1148 {
1149 	struct security_info security = {0};
1150 	bool match;
1151 
1152 	if (!filter)
1153 		match = true;
1154 	else
1155 		match = scm_filter_match(psoc, db_node->entry,
1156 					filter, &security);
1157 
1158 	if (!match)
1159 		return QDF_STATUS_SUCCESS;
1160 
1161 	qdf_spin_lock_bh(&scan_db->scan_db_lock);
1162 	scm_scan_entry_del(scan_db, db_node);
1163 	qdf_spin_unlock_bh(&scan_db->scan_db_lock);
1164 
1165 	return QDF_STATUS_SUCCESS;
1166 }
1167 
1168 /**
1169  * scm_flush_scan_entries() - API to flush scan entries depending on filters
1170  * @psoc: psoc ptr
1171  * @scan_db: scan db
1172  * @filter: filter
1173  *
1174  * Return: void
1175  */
1176 static void scm_flush_scan_entries(struct wlan_objmgr_psoc *psoc,
1177 	struct scan_dbs *scan_db,
1178 	struct scan_filter *filter)
1179 {
1180 	int i;
1181 	struct scan_cache_node *cur_node;
1182 	struct scan_cache_node *next_node = NULL;
1183 
1184 	for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
1185 		cur_node = scm_get_next_node(scan_db,
1186 			   &scan_db->scan_hash_tbl[i], NULL);
1187 		while (cur_node) {
1188 			scm_scan_apply_filter_flush_entry(psoc, scan_db,
1189 				cur_node, filter);
1190 			next_node = scm_get_next_node(scan_db,
1191 				&scan_db->scan_hash_tbl[i], cur_node);
1192 			cur_node = next_node;
1193 		}
1194 	}
1195 }
1196 
1197 QDF_STATUS scm_flush_results(struct wlan_objmgr_pdev *pdev,
1198 	struct scan_filter *filter)
1199 {
1200 	struct wlan_objmgr_psoc *psoc;
1201 	struct scan_dbs *scan_db;
1202 	QDF_STATUS status = QDF_STATUS_SUCCESS;
1203 
1204 	if (!pdev) {
1205 		scm_err("pdev is NULL");
1206 		return QDF_STATUS_E_INVAL;
1207 	}
1208 
1209 	psoc = wlan_pdev_get_psoc(pdev);
1210 	if (!psoc) {
1211 		scm_err("psoc is NULL");
1212 		return QDF_STATUS_E_INVAL;
1213 	}
1214 
1215 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
1216 	if (!scan_db) {
1217 		scm_err("scan_db is NULL");
1218 		return QDF_STATUS_E_INVAL;
1219 	}
1220 
1221 	scm_flush_scan_entries(psoc, scan_db, filter);
1222 
1223 	return status;
1224 }
1225 
1226 /**
1227  * scm_filter_channels() - Remove entries not belonging to channel list
1228  * @scan_db: scan db
1229  * @db_node: node on which filters are applied
1230  * @chan_list: valid channel list
1231  * @num_chan: number of channels
1232  *
1233  * Return: QDF_STATUS
1234  */
1235 static void scm_filter_channels(struct scan_dbs *scan_db,
1236 	struct scan_cache_node *db_node,
1237 	uint8_t *chan_list, uint32_t num_chan)
1238 {
1239 	int i;
1240 	bool match = false;
1241 
1242 	for (i = 0; i < num_chan; i++) {
1243 		if (chan_list[i] ==
1244 		   util_scan_entry_channel_num(db_node->entry)) {
1245 			match = true;
1246 			break;
1247 		}
1248 	}
1249 
1250 	if (!match) {
1251 		qdf_spin_lock_bh(&scan_db->scan_db_lock);
1252 		scm_scan_entry_del(scan_db, db_node);
1253 		qdf_spin_unlock_bh(&scan_db->scan_db_lock);
1254 	}
1255 }
1256 
1257 void scm_filter_valid_channel(struct wlan_objmgr_pdev *pdev,
1258 	uint8_t *chan_list, uint32_t num_chan)
1259 {
1260 	int i;
1261 	struct wlan_objmgr_psoc *psoc;
1262 	struct scan_dbs *scan_db;
1263 	struct scan_cache_node *cur_node;
1264 	struct scan_cache_node *next_node = NULL;
1265 
1266 	scm_info("num_chan = %d", num_chan);
1267 
1268 	if (!pdev) {
1269 		scm_err("pdev is NULL");
1270 		return;
1271 	}
1272 
1273 	psoc = wlan_pdev_get_psoc(pdev);
1274 	if (!psoc) {
1275 		scm_err("psoc is NULL");
1276 		return;
1277 	}
1278 
1279 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
1280 	if (!scan_db) {
1281 		scm_err("scan_db is NULL");
1282 		return;
1283 	}
1284 
1285 	for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
1286 		cur_node = scm_get_next_node(scan_db,
1287 			   &scan_db->scan_hash_tbl[i], NULL);
1288 		while (cur_node) {
1289 			scm_filter_channels(scan_db,
1290 				cur_node, chan_list, num_chan);
1291 			next_node = scm_get_next_node(scan_db,
1292 				&scan_db->scan_hash_tbl[i], cur_node);
1293 			cur_node = next_node;
1294 		}
1295 	}
1296 }
1297 
1298 QDF_STATUS scm_scan_register_bcn_cb(struct wlan_objmgr_psoc *psoc,
1299 	update_beacon_cb cb, enum scan_cb_type type)
1300 {
1301 	struct wlan_scan_obj *scan_obj;
1302 
1303 	scan_obj = wlan_psoc_get_scan_obj(psoc);
1304 	if (!scan_obj) {
1305 		scm_err("scan obj is NULL");
1306 		return QDF_STATUS_E_INVAL;
1307 	}
1308 	switch (type) {
1309 	case SCAN_CB_TYPE_INFORM_BCN:
1310 		scan_obj->cb.inform_beacon = cb;
1311 		break;
1312 	case SCAN_CB_TYPE_UPDATE_BCN:
1313 		scan_obj->cb.update_beacon = cb;
1314 		break;
1315 	default:
1316 		scm_err("invalid cb type %d", type);
1317 	}
1318 
1319 	return QDF_STATUS_SUCCESS;
1320 }
1321 
1322 QDF_STATUS scm_db_init(struct wlan_objmgr_psoc *psoc)
1323 {
1324 	int i, j;
1325 	struct scan_dbs *scan_db;
1326 
1327 	if (!psoc) {
1328 		scm_err("psoc is NULL");
1329 		return QDF_STATUS_E_INVAL;
1330 	}
1331 
1332 	/* Initialize the scan database per pdev */
1333 	for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) {
1334 		scan_db = wlan_pdevid_get_scan_db(psoc, i);
1335 		if (!scan_db) {
1336 			scm_err("scan_db is NULL %d", i);
1337 			continue;
1338 		}
1339 		scan_db->num_entries = 0;
1340 		qdf_spinlock_create(&scan_db->scan_db_lock);
1341 		for (j = 0; j < SCAN_HASH_SIZE; j++)
1342 			qdf_list_create(&scan_db->scan_hash_tbl[j],
1343 				MAX_SCAN_CACHE_SIZE);
1344 	}
1345 
1346 	return QDF_STATUS_SUCCESS;
1347 }
1348 
1349 QDF_STATUS scm_db_deinit(struct wlan_objmgr_psoc *psoc)
1350 {
1351 	int i, j;
1352 	struct scan_dbs *scan_db;
1353 
1354 	if (!psoc) {
1355 		scm_err("scan obj is NULL");
1356 		return QDF_STATUS_E_INVAL;
1357 	}
1358 
1359 	/* Initialize the scan database per pdev */
1360 	for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) {
1361 		scan_db = wlan_pdevid_get_scan_db(psoc, i);
1362 		if (!scan_db) {
1363 			scm_err("scan_db is NULL %d", i);
1364 			continue;
1365 		}
1366 
1367 		scm_flush_scan_entries(psoc, scan_db, NULL);
1368 		for (j = 0; j < SCAN_HASH_SIZE; j++)
1369 			qdf_list_destroy(&scan_db->scan_hash_tbl[j]);
1370 		qdf_spinlock_destroy(&scan_db->scan_db_lock);
1371 	}
1372 
1373 	return QDF_STATUS_SUCCESS;
1374 }
1375 
1376 QDF_STATUS scm_update_scan_mlme_info(struct wlan_objmgr_pdev *pdev,
1377 	struct scan_cache_entry *entry)
1378 {
1379 	uint8_t hash_idx;
1380 	struct scan_dbs *scan_db;
1381 	struct scan_cache_node *cur_node;
1382 	struct scan_cache_node *next_node = NULL;
1383 	struct wlan_objmgr_psoc *psoc;
1384 
1385 	psoc = wlan_pdev_get_psoc(pdev);
1386 	if (!psoc) {
1387 		scm_err("psoc is NULL");
1388 		return QDF_STATUS_E_INVAL;
1389 	}
1390 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
1391 	if (!scan_db) {
1392 		scm_err("scan_db is NULL");
1393 		return QDF_STATUS_E_INVAL;
1394 	}
1395 
1396 	hash_idx = SCAN_GET_HASH(entry->bssid.bytes);
1397 
1398 	cur_node = scm_get_next_node(scan_db,
1399 			&scan_db->scan_hash_tbl[hash_idx], NULL);
1400 
1401 	while (cur_node) {
1402 		if (util_is_scan_entry_match(entry,
1403 					cur_node->entry)) {
1404 			/* Acquire db lock to prevent simultaneous update */
1405 			qdf_spin_lock_bh(&scan_db->scan_db_lock);
1406 			scm_update_mlme_info(entry, cur_node->entry);
1407 			qdf_spin_unlock_bh(&scan_db->scan_db_lock);
1408 			scm_scan_entry_put_ref(scan_db,
1409 					cur_node, true);
1410 			return QDF_STATUS_SUCCESS;
1411 		}
1412 		next_node = scm_get_next_node(scan_db,
1413 				&scan_db->scan_hash_tbl[hash_idx], cur_node);
1414 		cur_node = next_node;
1415 	}
1416 
1417 	return QDF_STATUS_E_INVAL;
1418 }
1419 
1420 QDF_STATUS scm_scan_update_mlme_by_bssinfo(struct wlan_objmgr_pdev *pdev,
1421 		struct bss_info *bss_info, struct mlme_info *mlme)
1422 {
1423 	uint8_t hash_idx;
1424 	struct scan_dbs *scan_db;
1425 	struct scan_cache_node *cur_node;
1426 	struct scan_cache_node *next_node = NULL;
1427 	struct wlan_objmgr_psoc *psoc;
1428 	struct scan_cache_entry *entry;
1429 
1430 	psoc = wlan_pdev_get_psoc(pdev);
1431 	if (!psoc) {
1432 		scm_err("psoc is NULL");
1433 		return QDF_STATUS_E_INVAL;
1434 	}
1435 	scan_db = wlan_pdev_get_scan_db(psoc, pdev);
1436 	if (!scan_db) {
1437 		scm_err("scan_db is NULL");
1438 		return QDF_STATUS_E_INVAL;
1439 	}
1440 
1441 	hash_idx = SCAN_GET_HASH(bss_info->bssid.bytes);
1442 	cur_node = scm_get_next_node(scan_db,
1443 			&scan_db->scan_hash_tbl[hash_idx], NULL);
1444 	while (cur_node) {
1445 		entry = cur_node->entry;
1446 		if (qdf_is_macaddr_equal(&bss_info->bssid, &entry->bssid) &&
1447 			(util_is_ssid_match(&bss_info->ssid, &entry->ssid)) &&
1448 			(bss_info->chan == entry->channel.chan_idx)) {
1449 			/* Acquire db lock to prevent simultaneous update */
1450 			qdf_spin_lock_bh(&scan_db->scan_db_lock);
1451 			qdf_mem_copy(&entry->mlme_info, mlme,
1452 					sizeof(struct mlme_info));
1453 			scm_scan_entry_put_ref(scan_db,
1454 					cur_node, false);
1455 			qdf_spin_unlock_bh(&scan_db->scan_db_lock);
1456 			return QDF_STATUS_SUCCESS;
1457 		}
1458 		next_node = scm_get_next_node(scan_db,
1459 				&scan_db->scan_hash_tbl[hash_idx], cur_node);
1460 		cur_node = next_node;
1461 	}
1462 
1463 	return QDF_STATUS_E_INVAL;
1464 }
1465