blob: 8bd1de670ca20672f233ac8b07f6e9562eae1168 [file] [log] [blame]
/*
* Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* DOC: contains scan cache api and functionality
* The Scan entries are protected by scan_db_lock. Holding the lock
* for whole scan operation during get/flush scan results may take
* more than 5 ms and thus ref count is used along with scan_db_lock.
* Below are the operation on scan cache entry:
* - While adding new node to the entry scan_db_lock is taken and ref_cnt
* is initialized and incremented. Also the cookie will be set to valid value.
* - The ref count incremented during adding new node should be decremented only
* by a delete operation on the node. But there can be multiple concurrent
* delete operations on a node from different threads which may lead to ref
* count being decremented multiple time and freeing the node even if node
* is in use. So to maintain atomicity between multiple delete operations
* on a same node from different threads, a cookie is used to check if node is
* logically deleted or not. A delete operation will set the cookie to 0
* making it invalid. So if the 2nd thread find the cookie as invalid it will
* not try to delete and decrement the ref count of the node again.
* - This Cookie is also used to check if node is valid while iterating through
* the scan cache to avoid duplicate entries.
* - Once ref_cnt become 0, i.e. it is logically deleted and no thread is using
* it the node is physically deleted from the scan cache.
* - While reading the node the ref_cnt should be incremented. Once reading
* operation is done ref_cnt is decremented.
*/
#include <qdf_status.h>
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_vdev_obj.h>
#include <wlan_scan_public_structs.h>
#include <wlan_scan_utils_api.h>
#include "wlan_scan_main.h"
#include "wlan_scan_cache_db_i.h"
#include "wlan_reg_services_api.h"
#include "wlan_reg_ucfg_api.h"
#include <wlan_objmgr_vdev_obj.h>
#include <wlan_dfs_utils_api.h>
#include "wlan_crypto_global_def.h"
#include "wlan_crypto_global_api.h"
#include "wlan_cm_bss_score_param.h"
#ifdef FEATURE_6G_SCAN_CHAN_SORT_ALGO
struct channel_list_db *scm_get_rnr_channel_db(struct wlan_objmgr_psoc *psoc)
{
struct wlan_scan_obj *scan_obj = NULL;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj)
return NULL;
return &scan_obj->rnr_channel_db;
}
struct meta_rnr_channel *scm_get_chan_meta(struct wlan_objmgr_psoc *psoc,
uint32_t chan_freq)
{
int i;
struct channel_list_db *rnr_channel_db;
if (!psoc || !chan_freq || !wlan_reg_is_6ghz_chan_freq(chan_freq))
return NULL;
rnr_channel_db = scm_get_rnr_channel_db(psoc);
if (!rnr_channel_db)
return NULL;
for (i = 0; i < QDF_ARRAY_SIZE(rnr_channel_db->channel); i++)
if (rnr_channel_db->channel[i].chan_freq == chan_freq)
return &rnr_channel_db->channel[i];
return NULL;
}
static void scm_add_rnr_channel_db(struct wlan_objmgr_psoc *psoc,
struct scan_cache_entry *entry)
{
uint32_t chan_freq;
uint8_t is_6g_bss, i;
struct meta_rnr_channel *channel;
struct rnr_bss_info *rnr_bss;
struct scan_rnr_node *rnr_node;
chan_freq = entry->channel.chan_freq;
is_6g_bss = wlan_reg_is_6ghz_chan_freq(chan_freq);
/* Return if the BSS is not 6G and RNR IE is not present */
if (!(is_6g_bss || entry->ie_list.rnrie))
return;
scm_debug("BSS freq %d BSSID: "QDF_MAC_ADDR_FMT, chan_freq,
QDF_MAC_ADDR_REF(entry->bssid.bytes));
if (is_6g_bss) {
channel = scm_get_chan_meta(psoc, chan_freq);
if (!channel) {
scm_debug("Failed to get chan Meta freq %d", chan_freq);
return;
}
channel->bss_beacon_probe_count++;
channel->beacon_probe_last_time_found = entry->scan_entry_time;
}
/*
* If scan entry got RNR IE then loop through all
* entries and increase the BSS count in respective channels
*/
if (!entry->ie_list.rnrie)
return;
for (i = 0; i < MAX_RNR_BSS; i++) {
rnr_bss = &entry->rnr.bss_info[i];
/* Skip if entry is not valid */
if (!rnr_bss->channel_number)
continue;
chan_freq = wlan_reg_chan_opclass_to_freq(rnr_bss->channel_number,
rnr_bss->operating_class,
true);
channel = scm_get_chan_meta(psoc, chan_freq);
if (!channel) {
scm_debug("Failed to get chan Meta freq %d", chan_freq);
continue;
}
channel->bss_beacon_probe_count++;
/* Don't add RNR entry if list is full */
if (qdf_list_size(&channel->rnr_list) >= WLAN_MAX_RNR_COUNT) {
scm_debug("List is full");
return;
}
rnr_node = qdf_mem_malloc(sizeof(struct scan_rnr_node));
if (!rnr_node)
return;
rnr_node->entry.timestamp = entry->scan_entry_time;
if (!qdf_is_macaddr_zero(&rnr_bss->bssid))
qdf_mem_copy(&rnr_node->entry.bssid,
&rnr_bss->bssid,
QDF_MAC_ADDR_SIZE);
if (rnr_bss->short_ssid)
rnr_node->entry.short_ssid = rnr_bss->short_ssid;
if (rnr_bss->bss_params)
rnr_node->entry.bss_params = rnr_bss->bss_params;
scm_debug("Add freq %d: "QDF_MAC_ADDR_FMT" short ssid %x", chan_freq,
QDF_MAC_ADDR_REF(rnr_bss->bssid.bytes),
rnr_bss->short_ssid);
qdf_list_insert_back(&channel->rnr_list,
&rnr_node->node);
}
}
void scm_filter_rnr_flag_pno(struct wlan_objmgr_vdev *vdev,
uint32_t short_ssid,
struct chan_list *pno_chan_list)
{
uint8_t i;
uint32_t freq;
struct meta_rnr_channel *chan;
struct scan_rnr_node *rnr_node;
enum scan_mode_6ghz scan_mode;
struct wlan_scan_obj *scan_obj;
struct wlan_objmgr_psoc *psoc;
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc)
return;
scan_obj = wlan_vdev_get_scan_obj(vdev);
if (!scan_obj) {
scm_err("scan_obj is NULL");
return;
}
scan_mode = scan_obj->scan_def.scan_mode_6g;
/* No Filteration required for below scan modes since
* no RNR flag marked.
*/
if (scan_mode == SCAN_MODE_6G_NO_CHANNEL ||
scan_mode == SCAN_MODE_6G_ALL_CHANNEL ||
scan_mode == SCAN_MODE_6G_ALL_DUTY_CYCLE)
return;
for (i = 0; i < pno_chan_list->num_chan; i++) {
freq = pno_chan_list->chan[i].freq;
chan = scm_get_chan_meta(psoc, freq);
if (!chan || qdf_list_empty(&chan->rnr_list))
continue;
qdf_list_for_each(&chan->rnr_list, rnr_node, node) {
if (rnr_node->entry.short_ssid) {
if (rnr_node->entry.short_ssid == short_ssid) {
/* If short ssid entry present in RNR db cache, remove
* FLAG_SCAN_ONLY_IF_RNR_FOUND flag from the channel.
*/
pno_chan_list->chan[i].flags &=
~FLAG_SCAN_ONLY_IF_RNR_FOUND;
break;
}
}
}
}
}
#endif
/**
* scm_del_scan_node() - API to remove scan node from the list
* @list: hash list
* @scan_node: node to be removed
*
* This should be called while holding scan_db_lock.
*
* Return: void
*/
static void scm_del_scan_node(qdf_list_t *list,
struct scan_cache_node *scan_node)
{
QDF_STATUS status;
status = qdf_list_remove_node(list, &scan_node->node);
if (QDF_IS_STATUS_SUCCESS(status)) {
util_scan_free_cache_entry(scan_node->entry);
qdf_mem_free(scan_node);
}
}
/**
* scm_del_scan_node_from_db() - API to del the scan entry
* @scan_db: scan database
* @scan_entry:entry scan_node
*
* API to flush the scan entry. This should be called while
* holding scan_db_lock.
*
* Return: QDF status.
*/
static QDF_STATUS scm_del_scan_node_from_db(struct scan_dbs *scan_db,
struct scan_cache_node *scan_node)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint8_t hash_idx;
if (!scan_node)
return QDF_STATUS_E_INVAL;
hash_idx = SCAN_GET_HASH(scan_node->entry->bssid.bytes);
scm_del_scan_node(&scan_db->scan_hash_tbl[hash_idx], scan_node);
scan_db->num_entries--;
return status;
}
/**
* scm_scan_entry_get_ref() - api to increase ref count of scan entry
* @scan_node: scan node
*
* Return: void
*/
static void scm_scan_entry_get_ref(struct scan_cache_node *scan_node)
{
if (!scan_node) {
scm_err("scan_node is NULL");
QDF_ASSERT(0);
return;
}
qdf_atomic_inc(&scan_node->ref_cnt);
}
/**
* scm_scan_entry_put_ref() - Api to decrease ref count of scan entry
* and free if it become 0
* @scan_db: scan database
* @scan_node: scan node
* @lock_needed: if scan_db_lock is needed
*
* Return: void
*/
static void scm_scan_entry_put_ref(struct scan_dbs *scan_db,
struct scan_cache_node *scan_node, bool lock_needed)
{
if (!scan_node) {
scm_err("scan_node is NULL");
QDF_ASSERT(0);
return;
}
if (lock_needed)
qdf_spin_lock_bh(&scan_db->scan_db_lock);
if (!qdf_atomic_read(&scan_node->ref_cnt)) {
if (lock_needed)
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
scm_err("scan_node ref cnt is 0");
QDF_ASSERT(0);
return;
}
/* Decrement ref count, free scan_node, if ref count == 0 */
if (qdf_atomic_dec_and_test(&scan_node->ref_cnt))
scm_del_scan_node_from_db(scan_db, scan_node);
if (lock_needed)
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
}
/**
* scm_scan_entry_del() - API to delete scan node
* @scan_db: data base
* @scan_node: node to be deleted
*
* Call must be protected by scan_db->scan_db_lock
*
* Return: void
*/
static void scm_scan_entry_del(struct scan_dbs *scan_db,
struct scan_cache_node *scan_node)
{
if (!scan_node) {
scm_err("scan node is NULL");
QDF_ASSERT(0);
return;
}
if (scan_node->cookie != SCAN_NODE_ACTIVE_COOKIE) {
scm_debug("node is already deleted");
return;
}
/* Seems node is already deleted */
if (!qdf_atomic_read(&scan_node->ref_cnt)) {
scm_debug("node is already deleted ref 0");
return;
}
scan_node->cookie = 0;
scm_scan_entry_put_ref(scan_db, scan_node, false);
}
/**
* scm_add_scan_node() - API to add scan node
* @scan_db: data base
* @scan_node: node to be added
* @dup_node: node before which new node to be added
* if it's not NULL, otherwise add node to tail
*
* Call must be protected by scan_db->scan_db_lock
*
* Return: void
*/
static void scm_add_scan_node(struct scan_dbs *scan_db,
struct scan_cache_node *scan_node,
struct scan_cache_node *dup_node)
{
uint8_t hash_idx;
hash_idx =
SCAN_GET_HASH(scan_node->entry->bssid.bytes);
qdf_atomic_init(&scan_node->ref_cnt);
scan_node->cookie = SCAN_NODE_ACTIVE_COOKIE;
scm_scan_entry_get_ref(scan_node);
if (!dup_node)
qdf_list_insert_back(&scan_db->scan_hash_tbl[hash_idx],
&scan_node->node);
else
qdf_list_insert_before(&scan_db->scan_hash_tbl[hash_idx],
&scan_node->node, &dup_node->node);
scan_db->num_entries++;
}
/**
* scm_get_next_valid_node() - API get the next valid scan node from
* the list
* @list: hash list
* @cur_node: current node pointer
*
* API to get next active node from the list. If cur_node is NULL
* it will return first node of the list.
* Call must be protected by scan_db->scan_db_lock
*
* Return: next scan node
*/
static qdf_list_node_t *
scm_get_next_valid_node(qdf_list_t *list,
qdf_list_node_t *cur_node)
{
qdf_list_node_t *next_node = NULL;
qdf_list_node_t *temp_node = NULL;
struct scan_cache_node *scan_node;
if (cur_node)
qdf_list_peek_next(list, cur_node, &next_node);
else
qdf_list_peek_front(list, &next_node);
while (next_node) {
scan_node = qdf_container_of(next_node,
struct scan_cache_node, node);
if (scan_node->cookie == SCAN_NODE_ACTIVE_COOKIE)
return next_node;
/*
* If node is not valid check for next entry
* to get next valid node.
*/
qdf_list_peek_next(list, next_node, &temp_node);
next_node = temp_node;
temp_node = NULL;
}
return next_node;
}
/**
* scm_get_next_node() - API get the next scan node from
* the list
* @scan_db: scan data base
* @list: hash list
* @cur_node: current node pointer
*
* API get the next node from the list. If cur_node is NULL
* it will return first node of the list
*
* Return: next scan cache node
*/
static struct scan_cache_node *
scm_get_next_node(struct scan_dbs *scan_db,
qdf_list_t *list, struct scan_cache_node *cur_node)
{
struct scan_cache_node *next_node = NULL;
qdf_list_node_t *next_list = NULL;
qdf_spin_lock_bh(&scan_db->scan_db_lock);
if (cur_node) {
next_list = scm_get_next_valid_node(list, &cur_node->node);
/* Decrement the ref count of the previous node */
scm_scan_entry_put_ref(scan_db,
cur_node, false);
} else {
next_list = scm_get_next_valid_node(list, NULL);
}
/* Increase the ref count of the obtained node */
if (next_list) {
next_node = qdf_container_of(next_list,
struct scan_cache_node, node);
scm_scan_entry_get_ref(next_node);
}
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
return next_node;
}
/**
* scm_check_and_age_out() - check and age out the old entries
* @scan_db: scan db
* @scan_node: node to check for age out
* @scan_aging_time: scan cache aging time
*
* Return: void
*/
static void scm_check_and_age_out(struct scan_dbs *scan_db,
struct scan_cache_node *node,
qdf_time_t scan_aging_time)
{
if (util_scan_entry_age(node->entry) >=
scan_aging_time) {
scm_debug("Aging out BSSID: "QDF_MAC_ADDR_FMT" with age %lu ms",
QDF_MAC_ADDR_REF(node->entry->bssid.bytes),
util_scan_entry_age(node->entry));
qdf_spin_lock_bh(&scan_db->scan_db_lock);
scm_scan_entry_del(scan_db, node);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
}
}
static bool scm_bss_is_connected(struct scan_cache_entry *entry)
{
if (entry->mlme_info.assoc_state == SCAN_ENTRY_CON_STATE_ASSOC)
return true;
return false;
}
/**
* scm_get_conn_node() - Get the scan cache entry node of the connected BSS
* @scan_db: scan DB pointer
*
* Return: scan cache entry node of connected BSS if exists, NULL otherwise
*/
static
struct scan_cache_node *scm_get_conn_node(struct scan_dbs *scan_db)
{
int i;
struct scan_cache_node *cur_node = NULL;
struct scan_cache_node *next_node = NULL;
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
while (cur_node) {
if (scm_bss_is_connected(cur_node->entry))
return cur_node;
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], cur_node);
cur_node = next_node;
next_node = NULL;
}
}
return NULL;
}
static bool
scm_bss_is_nontx_of_conn_bss(struct scan_cache_node *conn_node,
struct scan_cache_node *cur_node)
{
if (cur_node->entry->mbssid_info.profile_num &&
!memcmp(conn_node->entry->mbssid_info.trans_bssid,
cur_node->entry->mbssid_info.trans_bssid,
QDF_MAC_ADDR_SIZE))
return true;
return false;
}
void scm_age_out_entries(struct wlan_objmgr_psoc *psoc,
struct scan_dbs *scan_db)
{
int i;
struct scan_cache_node *cur_node = NULL;
struct scan_cache_node *next_node = NULL;
struct scan_cache_node *conn_node = NULL;
struct scan_default_params *def_param;
def_param = wlan_scan_psoc_get_def_params(psoc);
if (!def_param) {
scm_err("wlan_scan_psoc_get_def_params failed");
return;
}
conn_node = scm_get_conn_node(scan_db);
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
while (cur_node) {
if (!conn_node /* if there is no connected node */ ||
/* OR cur_node is not part of the MBSSID of the
* connected node
*/
(!scm_bss_is_connected(cur_node->entry) &&
!scm_bss_is_nontx_of_conn_bss(conn_node,
cur_node))) {
scm_check_and_age_out(scan_db, cur_node,
def_param->scan_cache_aging_time);
}
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], cur_node);
cur_node = next_node;
next_node = NULL;
}
}
if (conn_node)
scm_scan_entry_put_ref(scan_db, conn_node, true);
}
/**
* scm_flush_oldest_entry() - Iterate over scan db and flust out the
* oldest entry
* @scan_db: scan db from which oldest entry needs to be flushed
*
* Return: QDF_STATUS
*/
static QDF_STATUS scm_flush_oldest_entry(struct scan_dbs *scan_db)
{
int i;
struct scan_cache_node *oldest_node = NULL;
struct scan_cache_node *cur_node;
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
/* Get the first valid node for the hash */
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i],
NULL);
/* Iterate scan db and flush out oldest node
* take ref_cnt for oldest_node
*/
while (cur_node) {
if (!oldest_node ||
(util_scan_entry_age(oldest_node->entry) <
util_scan_entry_age(cur_node->entry))) {
if (oldest_node)
scm_scan_entry_put_ref(scan_db,
oldest_node,
true);
qdf_spin_lock_bh(&scan_db->scan_db_lock);
oldest_node = cur_node;
scm_scan_entry_get_ref(oldest_node);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
}
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i],
cur_node);
};
}
if (oldest_node) {
scm_debug("Flush oldest BSSID: "QDF_MAC_ADDR_FMT" with age %lu ms",
QDF_MAC_ADDR_REF(oldest_node->entry->bssid.bytes),
util_scan_entry_age(oldest_node->entry));
/* Release ref_cnt taken for oldest_node and delete it */
qdf_spin_lock_bh(&scan_db->scan_db_lock);
scm_scan_entry_del(scan_db, oldest_node);
scm_scan_entry_put_ref(scan_db, oldest_node, false);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
}
return QDF_STATUS_SUCCESS;
}
/**
* scm_update_alt_wcn_ie() - update the alternate WCN IE
* @from: copy from
* @dst: copy to
*
* Return: void
*/
static void scm_update_alt_wcn_ie(struct scan_cache_entry *from,
struct scan_cache_entry *dst)
{
uint32_t alt_wcn_ie_len;
if (from->frm_subtype == dst->frm_subtype)
return;
if (!from->ie_list.wcn && !dst->ie_list.wcn)
return;
/* Existing WCN IE is empty. */
if (!from->ie_list.wcn)
return;
alt_wcn_ie_len = 2 + from->ie_list.wcn[1];
if (alt_wcn_ie_len > WLAN_MAX_IE_LEN + 2) {
scm_err("invalid IE len");
return;
}
if (!dst->alt_wcn_ie.ptr) {
/* allocate this additional buffer for alternate WCN IE */
dst->alt_wcn_ie.ptr =
qdf_mem_malloc_atomic(WLAN_MAX_IE_LEN + 2);
if (!dst->alt_wcn_ie.ptr) {
scm_err("failed to allocate memory");
return;
}
}
qdf_mem_copy(dst->alt_wcn_ie.ptr,
from->ie_list.wcn, alt_wcn_ie_len);
dst->alt_wcn_ie.len = alt_wcn_ie_len;
}
/**
* scm_update_mlme_info() - update mlme info
* @src: source scan entry
* @dest: destination scan entry
*
* Return: void
*/
static inline void
scm_update_mlme_info(struct scan_cache_entry *src,
struct scan_cache_entry *dest)
{
qdf_mem_copy(&dest->mlme_info, &src->mlme_info,
sizeof(struct mlme_info));
}
/**
* scm_copy_info_from_dup_entry() - copy duplicate node info
* to new scan entry
* @pdev: pdev ptr
* @scan_obj: scan obj ptr
* @scan_db: scan database
* @scan_params: new entry to be added
* @scan_node: duplicate entry
*
* Copy duplicate node info to new entry.
*
* Return: void
*/
static void
scm_copy_info_from_dup_entry(struct wlan_objmgr_pdev *pdev,
struct wlan_scan_obj *scan_obj,
struct scan_dbs *scan_db,
struct scan_cache_entry *scan_params,
struct scan_cache_node *scan_node)
{
struct scan_cache_entry *scan_entry;
uint64_t time_gap;
scan_entry = scan_node->entry;
/* Update probe resp entry as well if AP is in hidden mode */
if (scan_params->frm_subtype == MGMT_SUBTYPE_PROBE_RESP &&
scan_entry->is_hidden_ssid)
scan_params->is_hidden_ssid = true;
/*
* If AP changed its beacon from not having an SSID to showing it the
* kernel will drop the entry asumming that something is wrong with AP.
* This can result in connection failure while updating the bss during
* connection. So flush the hidden entry from kernel before indicating
* the new entry.
*/
if (scan_entry->is_hidden_ssid &&
scan_params->frm_subtype == MGMT_SUBTYPE_BEACON &&
!util_scan_is_null_ssid(&scan_params->ssid)) {
if (scan_obj->cb.unlink_bss) {
scm_debug("Hidden AP "QDF_MAC_ADDR_FMT" switch to non-hidden SSID, So unlink the entry",
QDF_MAC_ADDR_REF(scan_entry->bssid.bytes));
scan_obj->cb.unlink_bss(pdev, scan_entry);
}
}
/* If old entry have the ssid but new entry does not */
if (util_scan_is_null_ssid(&scan_params->ssid) &&
scan_entry->ssid.length) {
/*
* New entry has a hidden SSID and old one has the SSID.
* Add the entry by using the ssid of the old entry
* only if diff of saved SSID time and current time is
* less than HIDDEN_SSID_TIME time.
* This will avoid issues in case AP changes its SSID
* while remain hidden.
*/
time_gap =
qdf_mc_timer_get_system_time() -
scan_entry->hidden_ssid_timestamp;
if (time_gap <= HIDDEN_SSID_TIME) {
scan_params->hidden_ssid_timestamp =
scan_entry->hidden_ssid_timestamp;
scan_params->ssid.length =
scan_entry->ssid.length;
qdf_mem_copy(scan_params->ssid.ssid,
scan_entry->ssid.ssid,
scan_entry->ssid.length);
}
}
/*
* Due to Rx sensitivity issue, sometime beacons are seen on adjacent
* channel so workaround in software is needed. If DS params or HT info
* are present driver can get proper channel info from these IEs and set
* channel_mismatch so that the older RSSI values are used in new entry.
*
* For the cases where DS params and HT info is not present, driver
* needs to check below conditions to get proper channel and set
* channel_mismatch so that the older RSSI values are used in new entry:
* -- The old entry channel and new entry channel are not same
* -- RSSI is less than -80, this indicate that the signal has leaked
* in adjacent channel.
*/
if ((scan_params->frm_subtype == MGMT_SUBTYPE_BEACON) &&
!util_scan_entry_htinfo(scan_params) &&
!util_scan_entry_ds_param(scan_params) &&
(scan_params->channel.chan_freq != scan_entry->channel.chan_freq) &&
(scan_params->rssi_raw < ADJACENT_CHANNEL_RSSI_THRESHOLD)) {
scan_params->channel.chan_freq = scan_entry->channel.chan_freq;
scan_params->channel_mismatch = true;
}
/* Use old value for rssi if beacon was heard on adjacent channel. */
if (scan_params->channel_mismatch) {
scan_params->snr = scan_entry->snr;
scan_params->avg_snr = scan_entry->avg_snr;
scan_params->rssi_raw = scan_entry->rssi_raw;
scan_params->avg_rssi = scan_entry->avg_rssi;
scan_params->rssi_timestamp =
scan_entry->rssi_timestamp;
} else {
/* If elapsed time since last rssi and snr update for this
* entry is smaller than a thresold, calculate a
* running average of the RSSI and SNR values.
* Otherwise new frames RSSI and SNR are more representive
* of the signal strength.
*/
time_gap =
scan_params->scan_entry_time -
scan_entry->rssi_timestamp;
if (time_gap > WLAN_RSSI_AVERAGING_TIME) {
scan_params->avg_rssi =
WLAN_RSSI_IN(scan_params->rssi_raw);
scan_params->avg_snr =
WLAN_SNR_IN(scan_params->snr);
}
else {
/* Copy previous average rssi and snr to new entry */
scan_params->avg_snr = scan_entry->avg_snr;
scan_params->avg_rssi = scan_entry->avg_rssi;
/* Average with previous samples */
WLAN_RSSI_LPF(scan_params->avg_rssi,
scan_params->rssi_raw);
WLAN_SNR_LPF(scan_params->avg_snr,
scan_params->snr);
}
scan_params->rssi_timestamp = scan_params->scan_entry_time;
}
/* copy wsn ie from scan_entry to scan_params*/
scm_update_alt_wcn_ie(scan_entry, scan_params);
/* copy mlme info from scan_entry to scan_params*/
scm_update_mlme_info(scan_entry, scan_params);
}
/**
* scm_find_duplicate() - find duplicate entry,
* if present, add input scan entry before it and delete
* duplicate entry. otherwise add entry to tail
* @pdev: pdev ptr
* @scan_obj: scan obj ptr
* @scan_db: scan db
* @entry: input scan cache entry
* @dup_node: node before which new entry to be added
*
* ref_cnt is taken for dup_node, caller should release ref taken
* if returns true.
*
* Return: bool
*/
static bool
scm_find_duplicate(struct wlan_objmgr_pdev *pdev,
struct wlan_scan_obj *scan_obj,
struct scan_dbs *scan_db,
struct scan_cache_entry *entry,
struct scan_cache_node **dup_node)
{
uint8_t hash_idx;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
hash_idx = SCAN_GET_HASH(entry->bssid.bytes);
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[hash_idx],
NULL);
while (cur_node) {
if (util_is_scan_entry_match(entry,
cur_node->entry)) {
scm_copy_info_from_dup_entry(pdev, scan_obj, scan_db,
entry, cur_node);
*dup_node = cur_node;
return true;
}
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[hash_idx], cur_node);
cur_node = next_node;
next_node = NULL;
}
return false;
}
/**
* scm_add_update_entry() - add or update scan entry
* @psoc: psoc ptr
* @pdev: pdev pointer
* @scan_params: new received entry
*
* Return: QDF_STATUS
*/
static QDF_STATUS scm_add_update_entry(struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_pdev *pdev, struct scan_cache_entry *scan_params)
{
struct scan_cache_node *dup_node = NULL;
struct scan_cache_node *scan_node = NULL;
bool is_dup_found = false;
QDF_STATUS status;
struct scan_dbs *scan_db;
struct wlan_scan_obj *scan_obj;
uint8_t security_type;
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return QDF_STATUS_E_INVAL;
}
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("scan_obj is NULL");
return QDF_STATUS_E_INVAL;
}
if (scan_params->frm_subtype ==
MGMT_SUBTYPE_PROBE_RESP &&
!scan_params->ie_list.ssid)
scm_debug("Probe resp doesn't contain SSID");
if (scan_params->ie_list.csa ||
scan_params->ie_list.xcsa ||
scan_params->ie_list.cswrp)
scm_debug("CSA IE present for BSSID: "QDF_MAC_ADDR_FMT,
QDF_MAC_ADDR_REF(scan_params->bssid.bytes));
is_dup_found = scm_find_duplicate(pdev, scan_obj, scan_db, scan_params,
&dup_node);
security_type = scan_params->security_type;
scm_nofl_debug("Received %s: "QDF_MAC_ADDR_FMT" \"%.*s\" freq %d rssi %d tsf_delta %u seq %d snr %d phy %d hidden %d mismatch %d %s%s%s%s pdev %d boot_time %llu ns",
(scan_params->frm_subtype == MGMT_SUBTYPE_PROBE_RESP) ?
"prb rsp" : "bcn",
QDF_MAC_ADDR_REF(scan_params->bssid.bytes),
scan_params->ssid.length, scan_params->ssid.ssid,
scan_params->channel.chan_freq, scan_params->rssi_raw,
scan_params->tsf_delta, scan_params->seq_num,
scan_params->snr, scan_params->phy_mode,
scan_params->is_hidden_ssid,
scan_params->channel_mismatch,
security_type & SCAN_SECURITY_TYPE_WPA ? "[WPA]" : "",
security_type & SCAN_SECURITY_TYPE_RSN ? "[RSN]" : "",
security_type & SCAN_SECURITY_TYPE_WAPI ? "[WAPI]" : "",
security_type & SCAN_SECURITY_TYPE_WEP ? "[WEP]" : "",
wlan_objmgr_pdev_get_pdev_id(pdev),
scan_params->boottime_ns);
if (scan_obj->cb.inform_beacon)
scan_obj->cb.inform_beacon(pdev, scan_params);
if (scan_db->num_entries >= MAX_SCAN_CACHE_SIZE) {
status = scm_flush_oldest_entry(scan_db);
if (QDF_IS_STATUS_ERROR(status)) {
/* release ref taken for dup node */
if (is_dup_found)
scm_scan_entry_put_ref(scan_db, dup_node, true);
return status;
}
}
scan_node = qdf_mem_malloc(sizeof(*scan_node));
if (!scan_node) {
/* release ref taken for dup node */
if (is_dup_found)
scm_scan_entry_put_ref(scan_db, dup_node, true);
return QDF_STATUS_E_NOMEM;
}
scan_node->entry = scan_params;
qdf_spin_lock_bh(&scan_db->scan_db_lock);
scm_add_scan_node(scan_db, scan_node, dup_node);
if (is_dup_found) {
/* release ref taken for dup node and delete it */
scm_scan_entry_del(scan_db, dup_node);
scm_scan_entry_put_ref(scan_db, dup_node, false);
}
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
return QDF_STATUS_SUCCESS;
}
#ifdef CONFIG_REG_CLIENT
/**
* scm_is_bss_allowed_for_country() - Check if bss is allowed to start for a
* specific country and power mode (VLP?LPI/SP) for 6GHz.
* @psoc: psoc ptr
* @scan_entry: ptr to scan entry
*
* Return: True if allowed, False if not.
*/
static bool scm_is_bss_allowed_for_country(struct wlan_objmgr_psoc *psoc,
struct scan_cache_entry *scan_entry)
{
struct wlan_country_ie *cc_ie;
uint8_t programmed_country[REG_ALPHA2_LEN + 1];
if (wlan_reg_is_6ghz_chan_freq(scan_entry->channel.chan_freq)) {
cc_ie = util_scan_entry_country(scan_entry);
if (!cc_ie)
return false;
wlan_reg_read_current_country(psoc, programmed_country);
if (cc_ie && qdf_mem_cmp(cc_ie->cc, programmed_country,
REG_ALPHA2_LEN)) {
if (wlan_reg_is_us(programmed_country))
return false;
}
}
return true;
}
#else
static bool scm_is_bss_allowed_for_country(struct wlan_objmgr_psoc *psoc,
struct scan_cache_entry *scan_entry)
{
return true;
}
#endif
QDF_STATUS __scm_handle_bcn_probe(struct scan_bcn_probe_event *bcn)
{
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_pdev *pdev = NULL;
struct scan_cache_entry *scan_entry;
struct wlan_scan_obj *scan_obj;
qdf_list_t *scan_list = NULL;
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint32_t list_count, i;
qdf_list_node_t *next_node = NULL;
struct scan_cache_node *scan_node;
struct wlan_frame_hdr *hdr = NULL;
struct wlan_crypto_params sec_params;
if (!bcn) {
scm_err("bcn is NULL");
return QDF_STATUS_E_INVAL;
}
if (!bcn->rx_data) {
scm_err("rx_data iS NULL");
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
if (!bcn->buf) {
scm_err("buf is NULL");
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
hdr = (struct wlan_frame_hdr *)qdf_nbuf_data(bcn->buf);
psoc = bcn->psoc;
pdev = wlan_objmgr_get_pdev_by_id(psoc,
bcn->rx_data->pdev_id, WLAN_SCAN_ID);
if (!pdev) {
scm_err("pdev is NULL");
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("scan_obj is NULL");
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
if (qdf_nbuf_len(bcn->buf) <=
(sizeof(struct wlan_frame_hdr) +
offsetof(struct wlan_bcn_frame, ie))) {
scm_debug("invalid beacon/probe length");
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
if (bcn->frm_type == MGMT_SUBTYPE_BEACON &&
wlan_reg_is_dfs_for_freq(pdev, bcn->rx_data->chan_freq)) {
util_scan_add_hidden_ssid(pdev, bcn->buf);
}
scan_list =
util_scan_unpack_beacon_frame(pdev, qdf_nbuf_data(bcn->buf),
qdf_nbuf_len(bcn->buf), bcn->frm_type,
bcn->rx_data);
if (!scan_list || qdf_list_empty(scan_list)) {
scm_debug("failed to unpack %d frame BSSID: "QDF_MAC_ADDR_FMT,
bcn->frm_type, QDF_MAC_ADDR_REF(hdr->i_addr3));
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
list_count = qdf_list_size(scan_list);
for (i = 0; i < list_count; i++) {
status = qdf_list_remove_front(scan_list, &next_node);
if (QDF_IS_STATUS_ERROR(status) || !next_node) {
scm_debug("list remove failure i:%d, lsize:%d, BSSID: "QDF_MAC_ADDR_FMT,
i, list_count, QDF_MAC_ADDR_REF(hdr->i_addr3));
status = QDF_STATUS_E_INVAL;
goto free_nbuf;
}
scan_node = qdf_container_of(next_node,
struct scan_cache_node, node);
scan_entry = scan_node->entry;
if (scan_obj->drop_bcn_on_chan_mismatch &&
scan_entry->channel_mismatch) {
scm_nofl_debug("Drop frame for chan mismatch "QDF_MAC_ADDR_FMT" Seq Num: %d freq %d RSSI %d",
QDF_MAC_ADDR_REF(scan_entry->bssid.bytes),
scan_entry->seq_num,
scan_entry->channel.chan_freq,
scan_entry->rssi_raw);
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
/* Do not add invalid channel entry as kernel will reject it */
if (scan_obj->drop_bcn_on_invalid_freq &&
wlan_reg_is_disable_for_freq(pdev,
scan_entry->channel.chan_freq)) {
scm_nofl_debug("Drop frame for invalid freq %d: "QDF_MAC_ADDR_FMT" Seq Num: %d RSSI %d",
scan_entry->channel.chan_freq,
QDF_MAC_ADDR_REF(scan_entry->bssid.bytes),
scan_entry->seq_num,
scan_entry->rssi_raw);
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
if (wlan_cm_get_check_6ghz_security(psoc) &&
wlan_reg_is_6ghz_chan_freq(scan_entry->channel.chan_freq)) {
if (!util_scan_entry_rsn(scan_entry)) {
scm_info_rl(
"Drop frame from "QDF_MAC_ADDR_FMT
": No RSN IE for 6GHz AP",
QDF_MAC_ADDR_REF(
scan_entry->bssid.bytes));
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
status = wlan_crypto_rsnie_check(&sec_params,
util_scan_entry_rsn(scan_entry));
if (QDF_IS_STATUS_ERROR(status)) {
scm_info_rl(
"Drop frame from 6GHz AP "
QDF_MAC_ADDR_FMT
": RSN IE parse failed, status %d",
QDF_MAC_ADDR_REF(
scan_entry->bssid.bytes),
status);
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
if ((QDF_HAS_PARAM(sec_params.ucastcipherset,
WLAN_CRYPTO_CIPHER_NONE)) ||
(QDF_HAS_PARAM(sec_params.ucastcipherset,
WLAN_CRYPTO_CIPHER_TKIP)) ||
(QDF_HAS_PARAM(sec_params.ucastcipherset,
WLAN_CRYPTO_CIPHER_WEP_40)) ||
(QDF_HAS_PARAM(sec_params.ucastcipherset,
WLAN_CRYPTO_CIPHER_WEP_104))) {
scm_info_rl(
"Drop frame from "QDF_MAC_ADDR_FMT
": Invalid sec type %0X for 6GHz AP",
QDF_MAC_ADDR_REF(
scan_entry->bssid.bytes),
sec_params.ucastcipherset);
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
if (!wlan_cm_6ghz_allowed_for_akm(psoc,
sec_params.key_mgmt,
sec_params.rsn_caps,
util_scan_entry_rsnxe(scan_entry),
0, false)) {
scm_info_rl(
"Drop frame from "QDF_MAC_ADDR_FMT
": Invalid AKM suite %0X for 6GHz AP",
QDF_MAC_ADDR_REF(
scan_entry->bssid.bytes),
sec_params.key_mgmt);
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
}
if (scan_obj->cb.update_beacon)
scan_obj->cb.update_beacon(pdev, scan_entry);
/**
* Do not drop the frame if Wi-Fi safe mode or RF test mode is
* enabled. wlan_cm_get_check_6ghz_security API returns true if
* neither Safe mode nor RF test mode are enabled.
*/
if (!wlan_cm_get_standard_6ghz_conn_policy(psoc) &&
!scm_is_bss_allowed_for_country(psoc, scan_entry) &&
wlan_cm_get_check_6ghz_security(psoc)) {
scm_info_rl(
"Drop frame from "QDF_MAC_ADDR_FMT
": AP in VLP mode not supported for US",
QDF_MAC_ADDR_REF(scan_entry->bssid.bytes));
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
status = scm_add_update_entry(psoc, pdev, scan_entry);
if (QDF_IS_STATUS_ERROR(status)) {
scm_debug("failed to add entry for BSSID: "QDF_MAC_ADDR_FMT" Seq Num: %d",
QDF_MAC_ADDR_REF(scan_entry->bssid.bytes),
scan_entry->seq_num);
util_scan_free_cache_entry(scan_entry);
qdf_mem_free(scan_node);
continue;
}
qdf_mem_free(scan_node);
}
free_nbuf:
if (scan_list)
qdf_mem_free(scan_list);
if (bcn->psoc)
wlan_objmgr_psoc_release_ref(bcn->psoc, WLAN_SCAN_ID);
if (pdev)
wlan_objmgr_pdev_release_ref(pdev, WLAN_SCAN_ID);
if (bcn->rx_data)
qdf_mem_free(bcn->rx_data);
if (bcn->buf)
qdf_nbuf_free(bcn->buf);
qdf_mem_free(bcn);
return status;
}
QDF_STATUS scm_handle_bcn_probe(struct scheduler_msg *msg)
{
if (!msg) {
scm_err("msg is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
return __scm_handle_bcn_probe(msg->bodyptr);
}
/**
* scm_scan_apply_filter_get_entry() - apply filter and get the
* scan entry
* @psoc: psoc pointer
* @db_entry: scan entry
* @filter: filter to be applied
* @scan_list: scan list to which entry is added
*
* Return: QDF_STATUS
*/
static QDF_STATUS
scm_scan_apply_filter_get_entry(struct wlan_objmgr_psoc *psoc,
struct scan_cache_entry *db_entry,
struct scan_filter *filter,
qdf_list_t *scan_list)
{
struct scan_cache_node *scan_node = NULL;
struct security_info security = {0};
bool match;
if (!filter)
match = true;
else
match = scm_filter_match(psoc, db_entry,
filter, &security);
if (!match)
return QDF_STATUS_SUCCESS;
scan_node = qdf_mem_malloc_atomic(sizeof(*scan_node));
if (!scan_node)
return QDF_STATUS_E_NOMEM;
scan_node->entry =
util_scan_copy_cache_entry(db_entry);
if (!scan_node->entry) {
qdf_mem_free(scan_node);
return QDF_STATUS_E_NOMEM;
}
qdf_mem_copy(&scan_node->entry->neg_sec_info,
&security, sizeof(scan_node->entry->neg_sec_info));
qdf_list_insert_front(scan_list, &scan_node->node);
return QDF_STATUS_SUCCESS;
}
/**
* scm_get_results() - Iterate and get scan results
* @psoc: psoc ptr
* @scan_db: scan db
* @filter: filter to be applied
* @scan_list: scan list to which entry is added
*
* Return: void
*/
static void scm_get_results(struct wlan_objmgr_psoc *psoc,
struct scan_dbs *scan_db, struct scan_filter *filter,
qdf_list_t *scan_list)
{
int i, count;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
count = qdf_list_size(&scan_db->scan_hash_tbl[i]);
if (!count)
continue;
while (cur_node) {
scm_scan_apply_filter_get_entry(psoc,
cur_node->entry, filter, scan_list);
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], cur_node);
cur_node = next_node;
}
}
}
QDF_STATUS scm_purge_scan_results(qdf_list_t *scan_list)
{
QDF_STATUS status;
struct scan_cache_node *cur_node;
qdf_list_node_t *cur_lst = NULL, *next_lst = NULL;
if (!scan_list) {
scm_err("scan_result is NULL");
return QDF_STATUS_E_INVAL;
}
status = qdf_list_peek_front(scan_list, &cur_lst);
while (cur_lst) {
qdf_list_peek_next(
scan_list, cur_lst, &next_lst);
cur_node = qdf_container_of(cur_lst,
struct scan_cache_node, node);
status = qdf_list_remove_node(scan_list,
cur_lst);
if (QDF_IS_STATUS_SUCCESS(status)) {
util_scan_free_cache_entry(cur_node->entry);
qdf_mem_free(cur_node);
}
cur_lst = next_lst;
next_lst = NULL;
}
qdf_list_destroy(scan_list);
qdf_mem_free(scan_list);
return status;
}
qdf_list_t *scm_get_scan_result(struct wlan_objmgr_pdev *pdev,
struct scan_filter *filter)
{
struct wlan_objmgr_psoc *psoc;
struct scan_dbs *scan_db;
qdf_list_t *tmp_list;
if (!pdev) {
scm_err("pdev is NULL");
return NULL;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return NULL;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return NULL;
}
tmp_list = qdf_mem_malloc_atomic(sizeof(*tmp_list));
if (!tmp_list) {
scm_err("failed tp allocate scan_result");
return NULL;
}
qdf_list_create(tmp_list,
MAX_SCAN_CACHE_SIZE);
scm_age_out_entries(psoc, scan_db);
scm_get_results(psoc, scan_db, filter, tmp_list);
return tmp_list;
}
/**
* scm_iterate_db_and_call_func() - iterate and call the func
* @scan_db: scan db
* @func: func to be called
* @arg: func arg
*
* Return: QDF_STATUS
*/
static QDF_STATUS
scm_iterate_db_and_call_func(struct scan_dbs *scan_db,
scan_iterator_func func, void *arg)
{
int i;
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
if (!func)
return QDF_STATUS_E_INVAL;
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
while (cur_node) {
status = func(arg, cur_node->entry);
if (QDF_IS_STATUS_ERROR(status)) {
scm_scan_entry_put_ref(scan_db,
cur_node, true);
return status;
}
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], cur_node);
cur_node = next_node;
}
}
return status;
}
QDF_STATUS
scm_iterate_scan_db(struct wlan_objmgr_pdev *pdev,
scan_iterator_func func, void *arg)
{
struct wlan_objmgr_psoc *psoc;
struct scan_dbs *scan_db;
QDF_STATUS status;
if (!func) {
scm_err("func is NULL");
return QDF_STATUS_E_INVAL;
}
if (!pdev) {
scm_err("pdev is NULL");
return QDF_STATUS_E_INVAL;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return QDF_STATUS_E_INVAL;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return QDF_STATUS_E_INVAL;
}
scm_age_out_entries(psoc, scan_db);
status = scm_iterate_db_and_call_func(scan_db, func, arg);
return status;
}
/**
* scm_scan_apply_filter_flush_entry() -flush scan entries depending
* on filter
* @psoc: psoc ptr
* @scan_db: scan db
* @db_node: node on which filters are applied
* @filter: filter to be applied
*
* Return: QDF_STATUS
*/
static QDF_STATUS
scm_scan_apply_filter_flush_entry(struct wlan_objmgr_psoc *psoc,
struct scan_dbs *scan_db,
struct scan_cache_node *db_node,
struct scan_filter *filter)
{
struct security_info security = {0};
bool match;
if (!filter)
match = true;
else
match = scm_filter_match(psoc, db_node->entry,
filter, &security);
if (!match)
return QDF_STATUS_SUCCESS;
qdf_spin_lock_bh(&scan_db->scan_db_lock);
scm_scan_entry_del(scan_db, db_node);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
return QDF_STATUS_SUCCESS;
}
/**
* scm_flush_scan_entries() - API to flush scan entries depending on filters
* @psoc: psoc ptr
* @scan_db: scan db
* @filter: filter
*
* Return: void
*/
static void scm_flush_scan_entries(struct wlan_objmgr_psoc *psoc,
struct scan_dbs *scan_db,
struct scan_filter *filter)
{
int i;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
while (cur_node) {
scm_scan_apply_filter_flush_entry(psoc, scan_db,
cur_node, filter);
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], cur_node);
cur_node = next_node;
}
}
}
QDF_STATUS scm_flush_results(struct wlan_objmgr_pdev *pdev,
struct scan_filter *filter)
{
struct wlan_objmgr_psoc *psoc;
struct scan_dbs *scan_db;
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!pdev) {
scm_err("pdev is NULL");
return QDF_STATUS_E_INVAL;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return QDF_STATUS_E_INVAL;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return QDF_STATUS_E_INVAL;
}
scm_flush_scan_entries(psoc, scan_db, filter);
return status;
}
/**
* scm_filter_channels() - Remove entries not belonging to channel list
* @scan_db: scan db
* @db_node: node on which filters are applied
* @chan_freq_list: valid channel frequency (in MHz) list
* @num_chan: number of channels
*
* Return: QDF_STATUS
*/
static void scm_filter_channels(struct wlan_objmgr_pdev *pdev,
struct scan_dbs *scan_db,
struct scan_cache_node *db_node,
uint32_t *chan_freq_list, uint32_t num_chan)
{
int i;
bool match = false;
for (i = 0; i < num_chan; i++) {
if (chan_freq_list[i] == util_scan_entry_channel_frequency(
db_node->entry)) {
match = true;
break;
}
}
if (!match) {
qdf_spin_lock_bh(&scan_db->scan_db_lock);
scm_scan_entry_del(scan_db, db_node);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
}
}
void scm_filter_valid_channel(struct wlan_objmgr_pdev *pdev,
uint32_t *chan_freq_list, uint32_t num_chan)
{
int i;
struct wlan_objmgr_psoc *psoc;
struct scan_dbs *scan_db;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
scm_debug("num_chan = %d", num_chan);
if (!pdev) {
scm_err("pdev is NULL");
return;
}
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return;
}
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
while (cur_node) {
scm_filter_channels(pdev, scan_db,
cur_node, chan_freq_list, num_chan);
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], cur_node);
cur_node = next_node;
}
}
}
QDF_STATUS scm_scan_register_bcn_cb(struct wlan_objmgr_psoc *psoc,
update_beacon_cb cb, enum scan_cb_type type)
{
struct wlan_scan_obj *scan_obj;
scan_obj = wlan_psoc_get_scan_obj(psoc);
if (!scan_obj) {
scm_err("scan obj is NULL");
return QDF_STATUS_E_INVAL;
}
switch (type) {
case SCAN_CB_TYPE_INFORM_BCN:
scan_obj->cb.inform_beacon = cb;
break;
case SCAN_CB_TYPE_UPDATE_BCN:
scan_obj->cb.update_beacon = cb;
break;
case SCAN_CB_TYPE_UNLINK_BSS:
scan_obj->cb.unlink_bss = cb;
break;
default:
scm_err("invalid cb type %d", type);
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_db_init(struct wlan_objmgr_psoc *psoc)
{
int i, j;
struct scan_dbs *scan_db;
if (!psoc) {
scm_err("psoc is NULL");
return QDF_STATUS_E_INVAL;
}
/* Initialize the scan database per pdev */
for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) {
scan_db = wlan_pdevid_get_scan_db(psoc, i);
if (!scan_db) {
scm_err("scan_db is NULL %d", i);
continue;
}
scan_db->num_entries = 0;
qdf_spinlock_create(&scan_db->scan_db_lock);
for (j = 0; j < SCAN_HASH_SIZE; j++)
qdf_list_create(&scan_db->scan_hash_tbl[j],
MAX_SCAN_CACHE_SIZE);
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_db_deinit(struct wlan_objmgr_psoc *psoc)
{
int i, j;
struct scan_dbs *scan_db;
if (!psoc) {
scm_err("scan obj is NULL");
return QDF_STATUS_E_INVAL;
}
/* Initialize the scan database per pdev */
for (i = 0; i < WLAN_UMAC_MAX_PDEVS; i++) {
scan_db = wlan_pdevid_get_scan_db(psoc, i);
if (!scan_db) {
scm_err("scan_db is NULL %d", i);
continue;
}
scm_flush_scan_entries(psoc, scan_db, NULL);
for (j = 0; j < SCAN_HASH_SIZE; j++)
qdf_list_destroy(&scan_db->scan_hash_tbl[j]);
qdf_spinlock_destroy(&scan_db->scan_db_lock);
}
return QDF_STATUS_SUCCESS;
}
#ifdef FEATURE_6G_SCAN_CHAN_SORT_ALGO
QDF_STATUS scm_channel_list_db_init(struct wlan_objmgr_psoc *psoc)
{
uint32_t i, j;
uint32_t min_freq, max_freq;
struct channel_list_db *rnr_channel_db;
min_freq = wlan_reg_min_6ghz_chan_freq();
max_freq = wlan_reg_max_6ghz_chan_freq();
scm_info("min_freq %d max_freq %d", min_freq, max_freq);
i = min_freq;
rnr_channel_db = scm_get_rnr_channel_db(psoc);
if (!rnr_channel_db)
return QDF_STATUS_E_INVAL;
for (j = 0; j < QDF_ARRAY_SIZE(rnr_channel_db->channel); j++) {
if (i >= min_freq && i <= max_freq)
rnr_channel_db->channel[j].chan_freq = i;
i += 20;
/* init list for all to avoid uninitialized list */
qdf_list_create(&rnr_channel_db->channel[j].rnr_list,
WLAN_MAX_RNR_COUNT);
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_channel_list_db_deinit(struct wlan_objmgr_psoc *psoc)
{
int i;
qdf_list_node_t *cur_node, *next_node;
struct meta_rnr_channel *channel;
struct scan_rnr_node *rnr_node;
struct channel_list_db *rnr_channel_db;
rnr_channel_db = scm_get_rnr_channel_db(psoc);
if (!rnr_channel_db)
return QDF_STATUS_E_INVAL;
for (i = 0; i < QDF_ARRAY_SIZE(rnr_channel_db->channel); i++) {
channel = &rnr_channel_db->channel[i];
channel->chan_freq = 0;
channel->beacon_probe_last_time_found = 0;
channel->bss_beacon_probe_count = 0;
channel->saved_profile_count = 0;
cur_node = NULL;
qdf_list_peek_front(&channel->rnr_list, &cur_node);
while (cur_node) {
next_node = NULL;
qdf_list_peek_next(&channel->rnr_list, cur_node,
&next_node);
rnr_node = qdf_container_of(cur_node,
struct scan_rnr_node,
node);
qdf_list_remove_node(&channel->rnr_list,
&rnr_node->node);
qdf_mem_free(rnr_node);
cur_node = next_node;
next_node = NULL;
}
qdf_list_destroy(&channel->rnr_list);
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS scm_rnr_db_flush(struct wlan_objmgr_psoc *psoc)
{
int i;
qdf_list_node_t *cur_node, *next_node;
struct meta_rnr_channel *channel;
struct scan_rnr_node *rnr_node;
struct channel_list_db *rnr_channel_db;
rnr_channel_db = scm_get_rnr_channel_db(psoc);
if (!rnr_channel_db)
return QDF_STATUS_E_INVAL;
for (i = 0; i < QDF_ARRAY_SIZE(rnr_channel_db->channel); i++) {
channel = &rnr_channel_db->channel[i];
cur_node = NULL;
qdf_list_peek_front(&channel->rnr_list, &cur_node);
while (cur_node) {
next_node = NULL;
qdf_list_peek_next(&channel->rnr_list, cur_node,
&next_node);
rnr_node = qdf_container_of(cur_node,
struct scan_rnr_node,
node);
qdf_list_remove_node(&channel->rnr_list,
&rnr_node->node);
qdf_mem_free(rnr_node);
cur_node = next_node;
next_node = NULL;
}
/* Reset beacon info */
channel->beacon_probe_last_time_found = 0;
channel->bss_beacon_probe_count = 0;
}
return QDF_STATUS_SUCCESS;
}
void scm_update_rnr_from_scan_cache(struct wlan_objmgr_pdev *pdev)
{
uint8_t i;
struct scan_dbs *scan_db;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
struct wlan_objmgr_psoc *psoc;
struct scan_cache_entry *entry;
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return;
}
for (i = 0 ; i < SCAN_HASH_SIZE; i++) {
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i], NULL);
while (cur_node) {
entry = cur_node->entry;
scm_add_rnr_channel_db(psoc, entry);
next_node =
scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[i],
cur_node);
cur_node = next_node;
next_node = NULL;
}
}
}
#endif
QDF_STATUS scm_update_scan_mlme_info(struct wlan_objmgr_pdev *pdev,
struct scan_cache_entry *entry)
{
uint8_t hash_idx;
struct scan_dbs *scan_db;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
struct wlan_objmgr_psoc *psoc;
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return QDF_STATUS_E_INVAL;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return QDF_STATUS_E_INVAL;
}
hash_idx = SCAN_GET_HASH(entry->bssid.bytes);
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[hash_idx], NULL);
while (cur_node) {
if (util_is_scan_entry_match(entry,
cur_node->entry)) {
/* Acquire db lock to prevent simultaneous update */
qdf_spin_lock_bh(&scan_db->scan_db_lock);
scm_update_mlme_info(entry, cur_node->entry);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
scm_scan_entry_put_ref(scan_db,
cur_node, true);
return QDF_STATUS_SUCCESS;
}
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[hash_idx], cur_node);
cur_node = next_node;
}
return QDF_STATUS_E_INVAL;
}
QDF_STATUS scm_scan_update_mlme_by_bssinfo(struct wlan_objmgr_pdev *pdev,
struct bss_info *bss_info, struct mlme_info *mlme)
{
uint8_t hash_idx;
struct scan_dbs *scan_db;
struct scan_cache_node *cur_node;
struct scan_cache_node *next_node = NULL;
struct wlan_objmgr_psoc *psoc;
struct scan_cache_entry *entry;
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc) {
scm_err("psoc is NULL");
return QDF_STATUS_E_INVAL;
}
scan_db = wlan_pdev_get_scan_db(psoc, pdev);
if (!scan_db) {
scm_err("scan_db is NULL");
return QDF_STATUS_E_INVAL;
}
hash_idx = SCAN_GET_HASH(bss_info->bssid.bytes);
cur_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[hash_idx], NULL);
while (cur_node) {
entry = cur_node->entry;
if (qdf_is_macaddr_equal(&bss_info->bssid, &entry->bssid) &&
(util_is_ssid_match(&bss_info->ssid, &entry->ssid)) &&
(bss_info->freq == entry->channel.chan_freq)) {
/* Acquire db lock to prevent simultaneous update */
qdf_spin_lock_bh(&scan_db->scan_db_lock);
qdf_mem_copy(&entry->mlme_info, mlme,
sizeof(struct mlme_info));
scm_scan_entry_put_ref(scan_db,
cur_node, false);
qdf_spin_unlock_bh(&scan_db->scan_db_lock);
return QDF_STATUS_SUCCESS;
}
next_node = scm_get_next_node(scan_db,
&scan_db->scan_hash_tbl[hash_idx], cur_node);
cur_node = next_node;
}
return QDF_STATUS_E_INVAL;
}