xref: /wlan-dirver/qca-wifi-host-cmn/umac/scan/core/src/wlan_scan_cache_db.c (revision 99a10d078d8c08bacd095650c5bfcdcef8f8bf20)
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