From 37bf2e0dca5e5fa5bf117681eb8b6fa5f9f06ef6 Mon Sep 17 00:00:00 2001 From: Abhishek Singh Date: Tue, 21 Feb 2017 15:19:55 +0530 Subject: [PATCH] qcacmn: Add changes to get/flush scan results Add changes to get/flush scan results. Also add support to filter results depending on the filter passed. Change-Id: Iefb297ef19058299fa26b9bb61202e902e5fd465 CRs-Fixed: 1095299 --- umac/scan/core/src/wlan_scan_cache_db.c | 438 +++++++ umac/scan/core/src/wlan_scan_cache_db_i.h | 120 ++ umac/scan/core/src/wlan_scan_cache_db_ops.c | 1003 +++++++++++++++++ umac/scan/dispatcher/src/wlan_scan_ucfg_api.c | 29 + 4 files changed, 1590 insertions(+) diff --git a/umac/scan/core/src/wlan_scan_cache_db.c b/umac/scan/core/src/wlan_scan_cache_db.c index 1e5dc182b57d..6a7c44011dbb 100644 --- a/umac/scan/core/src/wlan_scan_cache_db.c +++ b/umac/scan/core/src/wlan_scan_cache_db.c @@ -569,6 +569,443 @@ free_nbuf: return status; } +/** + * scm_list_insert_sorted() - add the entries in scan_list in sorted way + * @filter: scan filter + * @scan_node: node entry to be inserted + * @scan_list: Temp scan list + * + * Add the entries in scan_list in sorted way considering + * cap_val and prefer val. The node is copy of original scan entry and + * thus no lock is required. + * + * Return: void + */ +static void scm_list_insert_sorted(struct scan_filter *filter, + struct scan_cache_node *scan_node, + qdf_list_t *scan_list) +{ + struct scan_cache_node *cur_node; + qdf_list_node_t *cur_lst = NULL, *next_lst = NULL; + + scan_node->entry->cap_val = + scm_get_bss_cap_value(filter, scan_node->entry); + + scan_node->entry->prefer_value = + scm_get_bss_prefer_value(filter, scan_node->entry); + + if (filter->num_of_pcl_channels) + scm_calc_pref_val_by_pcl(filter, scan_node->entry); + + if (qdf_list_empty(scan_list)) { + qdf_list_insert_front(scan_list, &scan_node->node); + return; + } + + qdf_list_peek_front(scan_list, &cur_lst); + + while (cur_lst) { + cur_node = qdf_container_of(cur_lst, + struct scan_cache_node, node); + if (scm_is_better_bss(filter, + scan_node->entry, cur_node->entry)) { + qdf_list_insert_before(scan_list, + &scan_node->node, + &cur_node->node); + break; + } + qdf_list_peek_next(scan_list, + cur_lst, &next_lst); + next_lst = next_lst; + next_lst = NULL; + } + + if (!cur_lst) + qdf_list_insert_back(scan_list, + &scan_node->node); + +} + +/** + * scm_scan_apply_filter_get_entry() - apply filter and get the + * scan entry + * @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 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(db_entry, filter, &security); + + if (!match) + return QDF_STATUS_SUCCESS; + + scan_node = qdf_mem_malloc(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)); + + if (!filter) + qdf_list_insert_front(scan_list, + &scan_node->node); + else + scm_list_insert_sorted(filter, scan_node, scan_list); + + return QDF_STATUS_SUCCESS; +} + +/** + * scm_get_results() - Iterate and get scan results + * @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 scan_dbs *scan_db, + struct scan_filter *filter, + qdf_list_t *scan_list) +{ + 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_get_entry( + 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(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(scan_db); + scm_get_results(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(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 + * @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 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(db_node->entry, filter, &security); + + if (!match) + return QDF_STATUS_SUCCESS; + + scm_scan_entry_put_ref(scan_db, db_node, true); + + return QDF_STATUS_SUCCESS; +} + +/** + * scm_flush_scan_entries() - API to flush scan entries depending on filters + * @scan_db: scan db + * @filter: filter + * + * Return: void + */ +static void scm_flush_scan_entries(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(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(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_list: valid channel list + * @num_chan: number of channels + * + * Return: QDF_STATUS + */ +static void scm_filter_channels(struct scan_dbs *scan_db, + struct scan_cache_node *db_node, + uint8_t *chan_list, uint32_t num_chan) +{ + int i; + bool match = false; + + for (i = 0; i < num_chan; i++) { + if (chan_list[i] == + util_scan_entry_channel_num(db_node->entry)) { + match = true; + break; + } + } + + if (!match) + scm_scan_entry_put_ref(scan_db, db_node, true); + +} + +void scm_filter_valid_channel(struct wlan_objmgr_pdev *pdev, + uint8_t *chan_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_info("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(scan_db, + cur_node, chan_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) { @@ -638,6 +1075,7 @@ QDF_STATUS scm_db_deinit(struct wlan_objmgr_psoc *psoc) continue; } + scm_flush_scan_entries(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); diff --git a/umac/scan/core/src/wlan_scan_cache_db_i.h b/umac/scan/core/src/wlan_scan_cache_db_i.h index 1f22e621ece5..69a1881c7816 100644 --- a/umac/scan/core/src/wlan_scan_cache_db_i.h +++ b/umac/scan/core/src/wlan_scan_cache_db_i.h @@ -23,6 +23,126 @@ #ifndef _WLAN_SCAN_CACHE_DB_I_H_ #define _WLAN_SCAN_CACHE_DB_I_H_ +/** + * scm_filter_match() - private API to check if entry is match to filter + * @db_entry: db entry + * @filter: filter + * @security: negotiated security if match is found + * + * Return: true if entry match filter + */ +bool scm_filter_match(struct scan_cache_entry *db_entry, + struct scan_filter *filter, + struct security_info *security); + +/** + * scm_get_bss_prefer_value() - Get the preference value for BSS + * @filter: scan filter + * @entry: entry + * + * Each BSS should be assigned a preference value ranging from + * 14-0, which will be used as an RSSI bucket score while sorting the + * scan results. + * + * Return: Preference value for the BSSID + */ +uint32_t scm_get_bss_prefer_value(struct scan_filter *filter, + struct scan_cache_entry *entry); + +/** + * scm_is_better_bss() - Is bss1 better than bss2 + * @filter: scan filter + * @bss1: Pointer to the first BSS. + * @bss2: Pointer to the second BSS. + * + * This routine helps in determining the preference value + * of a particular BSS in the scan result which is further + * used in the sorting logic of the final candidate AP's. + * + * Return: true, if bss1 is better than bss2 + * false, if bss2 is better than bss1. + */ +bool scm_is_better_bss(struct scan_filter *filter, + struct scan_cache_entry *bss1, + struct scan_cache_entry *bss2); + +/** + * scm_get_bss_cap_value() - get bss capability value + * @filter: filter + * @entry: scan entry entry + * + * Return: CapValue base on the capabilities of a BSS + */ +uint32_t scm_get_bss_cap_value(struct scan_filter *filter, + struct scan_cache_entry *entry); + +/** + * is_channel_found_in_pcl() - to check if channel is present in pcl + * @channel_id: channel of bss + * @filter: pointer to filter created through profile + * + * to check if provided channel is present in pcl + * + * Return: true or false + */ +static inline bool is_channel_found_in_pcl(int channel_id, + struct scan_filter *filter) +{ + int i; + bool status = false; + + if (!filter) + return status; + + for (i = 0; i < filter->num_of_pcl_channels; i++) { + if (filter->pcl_channel_list[i] == channel_id) { + status = true; + break; + } + } + + return status; +} + +/** + * scm_derive_prefer_value_from_rssi() - to derive prefer value + * @filter: filter + * @rssi: RSSI of the BSS + * + * This routine will derive preferred value from given rssi + * + * Return: value between 0 to 14 + */ +static inline int scm_derive_prefer_value_from_rssi(struct scan_filter *filter, + int rssi) +{ + int i = SCM_NUM_RSSI_CAT - 1, pref_val = 0; + + while (i >= 0) { + if (rssi >= filter->rssi_cat[i]) { + pref_val = filter->bss_prefer_val[i]; + break; + } + i--; + }; + + return pref_val; +} + +/** + * scm_calc_pref_val_by_pcl() - to calculate preferred value + * @filter: filter to find match from scan result + * @bss_descr: pointer to bss descriptor + * + * this routine calculates the new preferred value to be given to + * provided bss if its channel falls under preferred channel list. + * Thump rule is higer the RSSI better the boost. + * + * Return: success or failure + */ +QDF_STATUS scm_calc_pref_val_by_pcl(struct scan_filter *filter, + struct scan_cache_entry *entry); + /** * wlan_pdevid_get_scan_db() - private API to get scan db from pdev id * @psoc: psoc object diff --git a/umac/scan/core/src/wlan_scan_cache_db_ops.c b/umac/scan/core/src/wlan_scan_cache_db_ops.c index 0604a42cbb95..2d4c5332315f 100644 --- a/umac/scan/core/src/wlan_scan_cache_db_ops.c +++ b/umac/scan/core/src/wlan_scan_cache_db_ops.c @@ -18,3 +18,1006 @@ /* * DOC: contains scan cache operations */ + +#include +#include +#include +#include "wlan_scan_cache_db.h" +#include "wlan_scan_main.h" +#include "wlan_scan_cache_db_i.h" + +/** + * scm_get_altered_rssi() - Artificially increase/decrease RSSI + * @filter: scan filter + * @rssi: Actual RSSI of the AP. + * @channel_id: Channel on which the AP is parked. + * @bssid: BSSID of the AP to connect to. + * + * This routine will apply the boost and penalty parameters + * if the channel_id is of 5G band and it will also apply + * the preferred bssid score if there is a match between + * the bssid and the global preferred bssid list. + * + * Return: The modified RSSI Value + */ +static int scm_get_altered_rssi(struct scan_filter *filter, + int rssi, uint8_t channel_id, struct qdf_mac_addr *bssid) +{ + int modified_rssi; + int boost_factor; + int penalty_factor; + int i; + struct roam_filter_params *roam_params; + + roam_params = &filter->roam_params; + modified_rssi = rssi; + + /* + * If the 5G pref feature is enabled, apply the roaming + * parameters to boost or penalize the rssi. + * Boost Factor = boost_factor * (Actual RSSI - boost Threshold) + * Penalty Factor = penalty factor * (penalty threshold - Actual RSSI) + */ + if (roam_params->is_5g_pref_enabled && + WLAN_CHAN_IS_2GHZ(channel_id)) { + if (rssi > roam_params->raise_rssi_thresh_5g) { + /* Check and boost the threshold*/ + boost_factor = roam_params->raise_factor_5g * + (rssi - roam_params->raise_rssi_thresh_5g); + /* Check and penalize the threshold */ + modified_rssi += QDF_MIN(roam_params->max_raise_rssi_5g, + boost_factor); + } else if (rssi < roam_params->drop_rssi_thresh_5g) { + penalty_factor = roam_params->drop_factor_5g * + (roam_params->drop_rssi_thresh_5g - rssi); + modified_rssi -= QDF_MIN(roam_params->max_drop_rssi_5g, + penalty_factor); + } + } + /* + * Check if there are preferred bssid and then apply the + * preferred score + */ + if (bssid && roam_params->num_bssid_favored && + (roam_params->num_bssid_favored <= MAX_FAVORED_BSSID)) { + for (i = 0; i < roam_params->num_bssid_favored; i++) { + if (!qdf_is_macaddr_equal( + &roam_params->bssid_favored[i], bssid)) + continue; + modified_rssi += + roam_params->bssid_favored_factor[i]; + } + } + + return modified_rssi; +} + +/** + * scm_is_better_rssi() - Is bss1 better than bss2 + * @filter: scan filter + * @bss1: Pointer to the first BSS. + * @bss2: Pointer to the second BSS. + * + * This routine helps in determining the preference value + * of a particular BSS in the scan result which is further + * used in the sorting logic of the final candidate AP's. + * + * Return: true, if bss1 is better than bss2 + * false, if bss2 is better than bss1. + */ +static bool scm_is_better_rssi(struct scan_filter *filter, + struct scan_cache_entry *bss1, struct scan_cache_entry *bss2) +{ + bool ret; + int rssi1, rssi2; + struct qdf_mac_addr local_mac; + + rssi1 = bss1->rssi_raw; + rssi2 = bss2->rssi_raw; + /* + * Apply the boost and penlty logic and check + * which is the best RSSI + */ + qdf_mem_copy(local_mac.bytes, + bss1->bssid.bytes, QDF_MAC_ADDR_SIZE); + rssi1 = scm_get_altered_rssi(filter, rssi1, + bss1->channel.chan_idx, + &local_mac); + qdf_mem_copy(local_mac.bytes, + bss2->bssid.bytes, QDF_MAC_ADDR_SIZE); + rssi2 = scm_get_altered_rssi(filter, rssi2, + bss2->channel.chan_idx, + &local_mac); + if (rssi1 > rssi2) + ret = true; + else + ret = false; + + return ret; +} + +bool scm_is_better_bss(struct scan_filter *filter, + struct scan_cache_entry *bss1, + struct scan_cache_entry *bss2) +{ + bool ret; + + if (bss1->prefer_value > bss2->prefer_value) + return true; + + if (bss1->prefer_value == bss2->prefer_value) { + if (bss1->cap_val > bss2->cap_val) + ret = true; + else if (bss1->cap_val == bss2->cap_val) { + if (scm_is_better_rssi(filter, bss1, bss2)) + ret = true; + else + ret = false; + } else { + ret = false; + } + } else { + ret = false; + } + + return ret; +} + +uint32_t scm_get_bss_prefer_value(struct scan_filter *filter, + struct scan_cache_entry *entry) +{ + uint32_t ret = 0; + int modified_rssi; + + /* + * The RSSI does not get modified in case the 5G + * preference or preferred BSSID is not applicable + */ + modified_rssi = scm_get_altered_rssi(filter, + entry->rssi_raw, entry->channel.chan_idx, + &entry->bssid); + ret = scm_derive_prefer_value_from_rssi(filter, modified_rssi); + + return ret; +} + +uint32_t scm_get_bss_cap_value(struct scan_filter *filter, + struct scan_cache_entry *entry) +{ + uint32_t ret = SCM_BSS_CAP_VALUE_NONE; + + if (filter->roam_params.is_5g_pref_enabled) + if (WLAN_CHAN_IS_5GHZ(entry->channel.chan_idx)) + ret += SCM_BSS_CAP_VALUE_5GHZ; + /* + * if strict select 5GHz is set then ignore + * the capability checking + */ + if (!filter->strict_sel_5g) { + /* We only care about 11N capability */ + if (entry->ie_list.vhtcap) + ret += SCM_BSS_CAP_VALUE_VHT; + else if (entry->ie_list.htcap) + ret += SCM_BSS_CAP_VALUE_HT; + if (entry->ie_list.wmeinfo || + entry->ie_list.wmeinfo) { + ret += SCM_BSS_CAP_VALUE_WMM; + /* TO do Give advantage to UAPSD */ + } + } + + return ret; +} + +QDF_STATUS scm_calc_pref_val_by_pcl(struct scan_filter *filter, + struct scan_cache_entry *entry) +{ + int temp_rssi = 0, new_pref_val = 0; + int orig_pref_val = 0; + + if (!entry) + return QDF_STATUS_E_FAILURE; + + if (filter->num_of_bssid) { + scm_info("filter has specific bssid, no point of boosting"); + return QDF_STATUS_SUCCESS; + } + + if (is_channel_found_in_pcl(entry->channel.chan_idx, filter) && + (entry->rssi_raw > SCM_PCL_RSSI_THRESHOLD)) { + orig_pref_val = scm_derive_prefer_value_from_rssi(filter, + entry->rssi_raw); + temp_rssi = entry->rssi_raw + + (SCM_PCL_ADVANTAGE/(SCM_NUM_RSSI_CAT - + orig_pref_val)); + if (temp_rssi > 0) + temp_rssi = 0; + new_pref_val = scm_derive_prefer_value_from_rssi(filter, + temp_rssi); + + entry->prefer_value = + QDF_MAX(new_pref_val, entry->prefer_value); + } + + return QDF_STATUS_SUCCESS; +} + + +/** + * scm_is_open_security() - Check if scan entry support open security + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if open security else false + */ +static bool scm_is_open_security(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + bool match = false; + int i; + + if (db_entry->cap_info.wlan_caps.privacy) + return false; + + /* Check MC cipher and Auth type requested. */ + for (i = 0; i < filter->num_of_mc_enc_type; i++) { + if (WLAN_ENCRYPT_TYPE_NONE == + filter->mc_enc_type[i]) { + security->mc_enc = + filter->mc_enc_type[i]; + match = true; + break; + } + } + if (!match && filter->num_of_mc_enc_type) + return match; + + match = false; + /* Check Auth list. It should contain AuthOpen. */ + for (i = 0; i < filter->num_of_auth; i++) { + if ((WLAN_AUTH_TYPE_OPEN_SYSTEM == + filter->auth_type[i]) || + (WLAN_AUTH_TYPE_AUTOSWITCH == + filter->auth_type[i])) { + security->auth_type = + WLAN_AUTH_TYPE_OPEN_SYSTEM; + match = true; + break; + } + } + + return match; +} + +/** + * scm_is_cipher_match() - Check if cipher match the cipher list + * @cipher_list: cipher list to match + * @num_cipher: number of cipher in cipher list + * @cipher_to_match: cipher to found in cipher list + * + * Return: true if open security else false + */ +static bool scm_is_cipher_match( + uint32_t *cipher_list, + uint16_t num_cipher, uint32_t cipher_to_match) +{ + int i; + bool match = false; + + for (i = 0; i < num_cipher ; i++) { + match = (cipher_list[i] == cipher_to_match); + if (match) + break; + } + + return match; +} + +/** + * scm_get_cipher_suite_type() - get cypher suite type from enc type + * @enc: enc type + * + * Return: cypher suite type + */ +static uint8_t scm_get_cipher_suite_type(enum wlan_enc_type enc) +{ + uint8_t cipher_type; + + switch (enc) { + case WLAN_ENCRYPT_TYPE_WEP40: + case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: + cipher_type = WLAN_CSE_WEP40; + break; + case WLAN_ENCRYPT_TYPE_WEP104: + case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: + cipher_type = WLAN_CSE_WEP104; + break; + case WLAN_ENCRYPT_TYPE_TKIP: + cipher_type = WLAN_CSE_TKIP; + break; + case WLAN_ENCRYPT_TYPE_AES: + cipher_type = WLAN_CSE_CCMP; + break; + case WLAN_ENCRYPT_TYPE_NONE: + cipher_type = WLAN_CSE_NONE; + break; + case WLAN_ENCRYPT_TYPE_WPI: + cipher_type = WLAN_WAI_CERT_OR_SMS4; + break; + default: + cipher_type = WLAN_CSE_RESERVED; + break; + } + + return cipher_type; +} + +/** + * scm_is_wep_security() - Check if scan entry support WEP security + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if WEP security else false + */ +static bool scm_is_wep_security(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + int i; + bool match = false; + enum wlan_auth_type neg_auth = WLAN_AUTH_TYPE_OPEN_SYSTEM; + enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; + + /* If privacy bit is not set, consider no match */ + if (db_entry->cap_info.wlan_caps.privacy) + return false; + + for (i = 0; i < filter->num_of_mc_enc_type; i++) { + switch (filter->mc_enc_type[i]) { + case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: + case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: + case WLAN_ENCRYPT_TYPE_WEP40: + case WLAN_ENCRYPT_TYPE_WEP104: + /* + * Multicast list may contain WEP40/WEP104. + * Check whether it matches UC. + */ + if (security->uc_enc == + filter->mc_enc_type[i]) { + match = true; + neg_mccipher = + filter->mc_enc_type[i]; + } + break; + default: + match = false; + break; + } + if (match) + break; + } + + if (!match) + return match; + + for (i = 0; i < filter->num_of_auth; i++) { + switch (filter->auth_type[i]) { + case WLAN_AUTH_TYPE_OPEN_SYSTEM: + case WLAN_AUTH_TYPE_SHARED_KEY: + case WLAN_AUTH_TYPE_AUTOSWITCH: + match = true; + neg_auth = filter->auth_type[i]; + break; + default: + match = false; + } + if (match) + break; + } + + if (!match) + return match; + + /* + * In case of WPA / WPA2, check whether it supports WEP as well. + * Prepare the encryption type for WPA/WPA2 functions + */ + if (security->uc_enc == WLAN_ENCRYPT_TYPE_WEP40_STATICKEY) + security->uc_enc = WLAN_ENCRYPT_TYPE_WEP40; + else if (security->uc_enc == WLAN_ENCRYPT_TYPE_WEP104) + security->uc_enc = WLAN_ENCRYPT_TYPE_WEP104; + + /* else we can use the encryption type directly */ + if (util_scan_entry_wpa(db_entry)) { + struct wlan_wpa_ie wpa = {0}; + uint8_t cipher_type; + + cipher_type = + scm_get_cipher_suite_type(security->uc_enc); + wlan_parse_wpa_ie( + util_scan_entry_wpa(db_entry), &wpa); + + match = scm_is_cipher_match(&wpa.mc_cipher, + 1, WLAN_WPA_SEL(cipher_type)); + } + if (!match && util_scan_entry_rsn(db_entry)) { + struct wlan_rsn_ie rsn = {0}; + uint8_t cipher_type; + + cipher_type = + scm_get_cipher_suite_type(security->uc_enc); + + wlan_parse_rsn_ie( + util_scan_entry_rsn(db_entry), &rsn); + match = scm_is_cipher_match(&rsn.gp_cipher_suite, + 1, WLAN_RSN_SEL(cipher_type)); + } + + + if (match && security) { + security->auth_type = neg_auth; + security->mc_enc = neg_mccipher; + } + + return match; +} + +/** + * scm_check_pmf_match() - Check PMF security of entry match filter + * @filter: scan filter + * @rsn: rsn IE of the scan entry + * + * Return: true if PMF security match else false + */ +static bool +scm_check_pmf_match(struct scan_filter *filter, + struct wlan_rsn_ie *rsn) +{ + enum wlan_pmf_cap ap_pmf_cap = WLAN_PMF_DISABLED; + + if (rsn->cap & RSN_CAP_MFP_CAPABLE) + ap_pmf_cap = WLAN_PMF_CAPABLE; + if (rsn->cap & RSN_CAP_MFP_CAPABLE) + ap_pmf_cap = WLAN_PMF_REQUIRED; + + if ((filter->pmf_cap == WLAN_PMF_REQUIRED) && + (ap_pmf_cap == WLAN_PMF_DISABLED)) + return false; + else if ((filter->pmf_cap == WLAN_PMF_DISABLED) && + (ap_pmf_cap == WLAN_PMF_REQUIRED)) + return false; + + return true; +} + +/** + * scm_is_rsn_security() - Check if scan entry support RSN security + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if RSN security else false + */ +static bool scm_is_rsn_security(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + int i; + uint8_t cipher_type; + bool match = false; + enum wlan_auth_type neg_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; + enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; + struct wlan_rsn_ie rsn = {0}; + + if (!util_scan_entry_rsn(db_entry)) + return false; + + wlan_parse_rsn_ie( + util_scan_entry_rsn(db_entry), &rsn); + + cipher_type = + scm_get_cipher_suite_type(security->uc_enc); + match = scm_is_cipher_match(rsn.pwise_cipher_suites, + rsn.pwise_cipher_count, WLAN_RSN_SEL(cipher_type)); + if (!match) + return false; + + for (i = 0; i < filter->num_of_mc_enc_type; i++) { + cipher_type = + scm_get_cipher_suite_type( + filter->mc_enc_type[i]); + match = scm_is_cipher_match(&rsn.gp_cipher_suite, + 1, WLAN_RSN_SEL(cipher_type)); + if (match) + break; + } + if (!match) + return false; + + neg_mccipher = filter->mc_enc_type[i]; + + /* Initializing with false as it has true value already */ + match = false; + for (i = 0; i < filter->num_of_auth; i++) { + /* + * Ciphers are supported, Match authentication algorithm and + * pick first matching authtype. + */ + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_SEL(WLAN_AKM_FT_IEEE8021X))) { + if (WLAN_AUTH_TYPE_FT_RSN == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_FT_RSN; + match = true; + break; + } + } + + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_SEL(WLAN_AKM_FT_IEEE8021X))) { + if (WLAN_AUTH_TYPE_FT_RSN_PSK == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_FT_RSN_PSK; + match = true; + break; + } + } + /* ESE only supports 802.1X. No PSK. */ + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_CCKM_AKM)) { + if (WLAN_AUTH_TYPE_CCKM_RSN == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_CCKM_RSN; + match = true; + break; + } + } + /* RSN */ + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_SEL(WLAN_AKM_IEEE8021X))) { + if (WLAN_AUTH_TYPE_RSN == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_RSN; + match = true; + break; + } + } + /* TKIP */ + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_SEL(WLAN_AKM_PSK))) { + if (WLAN_AUTH_TYPE_RSN_PSK == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_RSN_PSK; + match = true; + break; + } + } + /* SHA256 */ + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_SEL(WLAN_AKM_SHA256_PSK))) { + if (WLAN_AUTH_TYPE_RSN_PSK_SHA256 == + filter->auth_type[i]) { + neg_auth = + WLAN_AUTH_TYPE_RSN_PSK_SHA256; + match = true; + break; + } + } + /* 8021X SHA256 */ + if (scm_is_cipher_match(rsn.akm_suites, + rsn.akm_suite_count, + WLAN_RSN_SEL(WLAN_AKM_SHA256_IEEE8021X))) { + if (WLAN_AUTH_TYPE_RSN_8021X_SHA256 == + filter->auth_type[i]) { + neg_auth = + WLAN_AUTH_TYPE_RSN_8021X_SHA256; + match = true; + break; + } + } + } + + if (!match) + return false; + match = scm_check_pmf_match(filter, &rsn); + + if (match && security) { + security->auth_type = neg_auth; + security->mc_enc = neg_mccipher; + } + + return match; +} + +/** + * scm_is_wpa_security() - Check if scan entry support WPA security + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if WPA security else false + */ +static bool scm_is_wpa_security(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + int i; + uint8_t cipher_type; + bool match = false; + enum wlan_auth_type neg_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; + enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; + struct wlan_wpa_ie wpa = {0}; + + if (!util_scan_entry_wpa(db_entry)) + return false; + + wlan_parse_wpa_ie(util_scan_entry_wpa(db_entry), &wpa); + + cipher_type = + scm_get_cipher_suite_type(security->uc_enc); + match = scm_is_cipher_match(wpa.uc_ciphers, + wpa.uc_cipher_count, WLAN_WPA_SEL(cipher_type)); + if (!match) + return false; + + for (i = 0; i < filter->num_of_mc_enc_type; i++) { + cipher_type = + scm_get_cipher_suite_type( + filter->mc_enc_type[i]); + match = scm_is_cipher_match(&wpa.mc_cipher, + 1, WLAN_WPA_SEL(cipher_type)); + if (match) + break; + } + if (!match) + return false; + neg_mccipher = filter->mc_enc_type[i]; + + /* Initializing with false as it has true value already */ + match = false; + for (i = 0; i < filter->num_of_auth; i++) { + /* + * Ciphers are supported, Match authentication algorithm and + * pick first matching authtype. + */ + /**/ + if (scm_is_cipher_match(wpa.auth_suites, + wpa.auth_suite_count, + WLAN_WPA_SEL(WLAN_AKM_IEEE8021X))) { + if (WLAN_AUTH_TYPE_WPA == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_WPA; + match = true; + break; + } + } + if (scm_is_cipher_match(wpa.auth_suites, + wpa.auth_suite_count, + WLAN_WPA_SEL(WLAN_AKM_PSK))) { + if (WLAN_AUTH_TYPE_WPA_PSK == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_WPA_PSK; + match = true; + break; + } + } + if (scm_is_cipher_match(wpa.auth_suites, + wpa.auth_suite_count, + WLAN_WPA_CCKM_AKM)) { + if (WLAN_AUTH_TYPE_CCKM_WPA == + filter->auth_type[i]) { + neg_auth = WLAN_AUTH_TYPE_CCKM_WPA; + match = true; + break; + } + } + } + + if (match && security) { + security->auth_type = neg_auth; + security->mc_enc = neg_mccipher; + } + + return match; +} + +/** + * scm_is_wapi_security() - Check if scan entry support WAPI security + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if WAPI security else false + */ +static bool scm_is_wapi_security(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + int i; + uint8_t cipher_type; + bool match = false; + enum wlan_auth_type neg_auth = WLAN_NUM_OF_SUPPORT_AUTH_TYPE; + enum wlan_enc_type neg_mccipher = WLAN_ENCRYPT_TYPE_NONE; + struct wlan_wapi_ie wapi = {0}; + + if (!util_scan_entry_wapi(db_entry)) + return false; + + wlan_parse_wapi_ie( + util_scan_entry_wapi(db_entry), &wapi); + + cipher_type = + scm_get_cipher_suite_type(security->uc_enc); + match = scm_is_cipher_match(wapi.uc_cipher_suites, + wapi.uc_cipher_count, WLAN_WAPI_SEL(cipher_type)); + if (!match) + return false; + + for (i = 0; i < filter->num_of_mc_enc_type; i++) { + cipher_type = + scm_get_cipher_suite_type( + filter->mc_enc_type[i]); + match = scm_is_cipher_match(&wapi.mc_cipher_suite, + 1, WLAN_WAPI_SEL(cipher_type)); + if (match) + break; + } + if (!match) + return false; + neg_mccipher = filter->mc_enc_type[i]; + + if (scm_is_cipher_match(wapi.akm_suites, + wapi.akm_suite_count, + WLAN_WAPI_SEL(WLAN_WAI_CERT_OR_SMS4))) + neg_auth = + WLAN_AUTH_TYPE_WAPI_WAI_CERTIFICATE; + else if (scm_is_cipher_match(wapi.akm_suites, + wapi.akm_suite_count, WLAN_WAPI_SEL(WLAN_WAI_PSK))) + neg_auth = WLAN_AUTH_TYPE_WAPI_WAI_PSK; + else + return false; + + match = false; + for (i = 0; i < filter->num_of_auth; i++) { + if (filter->auth_type[i] == neg_auth) { + match = true; + break; + } + } + + if (match && security) { + security->auth_type = neg_auth; + security->mc_enc = neg_mccipher; + } + + return match; +} + +/** + * scm_is_def_security() - Check if any security in filter match + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if any security else false + */ +static bool scm_is_def_security(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + bool match_any = false; + bool match = true; + + /* It is allowed to match anything. Try the more secured ones first. */ + /* Check AES first */ + security->uc_enc = WLAN_ENCRYPT_TYPE_AES; + match_any = scm_is_rsn_security(filter, + db_entry, security); + if (!match_any) { + /* Check TKIP */ + security->uc_enc = WLAN_ENCRYPT_TYPE_TKIP; + match_any = scm_is_rsn_security(filter, + db_entry, security); + } + + if (!match_any) { + /* Check WAPI */ + security->uc_enc = WLAN_ENCRYPT_TYPE_WPI; + match_any = scm_is_wapi_security(filter, + db_entry, security); + } + + if (match_any) + return match; + + security->uc_enc = WLAN_ENCRYPT_TYPE_WEP104; + if (scm_is_wep_security(filter, + db_entry, security)) + return true; + security->uc_enc = WLAN_ENCRYPT_TYPE_WEP40; + if (scm_is_wep_security(filter, + db_entry, security)) + return true; + security->uc_enc = WLAN_ENCRYPT_TYPE_WEP104_STATICKEY; + if (scm_is_wep_security(filter, + db_entry, security)) + return true; + security->uc_enc = WLAN_ENCRYPT_TYPE_WEP40_STATICKEY; + if (scm_is_wep_security(filter, + db_entry, security)) + return true; + + /* It must be open and no enc */ + if (db_entry->cap_info.wlan_caps.privacy) + return false; + + security->auth_type = WLAN_AUTH_TYPE_OPEN_SYSTEM; + security->mc_enc = WLAN_ENCRYPT_TYPE_NONE; + security->uc_enc = WLAN_ENCRYPT_TYPE_NONE; + + return match; +} + +/** + * scm_is_security_match() - Check if security in filter match + * @filter: scan filter + * @db_entry: db entry + * @security: matched security. + * + * Return: true if security match else false + */ +static bool scm_is_security_match(struct scan_filter *filter, + struct scan_cache_entry *db_entry, + struct security_info *security) +{ + int i; + bool match = false; + struct security_info local_security = {0}; + + if (!filter->num_of_enc_type) + return true; + + for (i = 0; (i < filter->num_of_enc_type) && + !match; i++) { + + local_security.uc_enc = + filter->enc_type[i]; + + switch (filter->enc_type[i]) { + case WLAN_ENCRYPT_TYPE_NONE: + match = scm_is_open_security(filter, + db_entry, &local_security); + break; + case WLAN_ENCRYPT_TYPE_WEP40_STATICKEY: + case WLAN_ENCRYPT_TYPE_WEP104_STATICKEY: + case WLAN_ENCRYPT_TYPE_WEP40: + case WLAN_ENCRYPT_TYPE_WEP104: + match = scm_is_wep_security(filter, + db_entry, &local_security); + break; + case WLAN_ENCRYPT_TYPE_TKIP: + case WLAN_ENCRYPT_TYPE_AES: + /* First check if there is a RSN match */ + match = scm_is_rsn_security(filter, + db_entry, &local_security); + /* If not RSN, then check WPA match */ + if (!match) + match = scm_is_wpa_security(filter, + db_entry, &local_security); + break; + case WLAN_ENCRYPT_TYPE_WPI:/* WAPI */ + match = scm_is_wapi_security(filter, + db_entry, &local_security); + break; + case WLAN_ENCRYPT_TYPE_ANY: + default: + match = scm_is_def_security(filter, + db_entry, &local_security); + break; + } + } + + if (match && security) + qdf_mem_copy(security, + &local_security, sizeof(*security)); + + return match; +} + +bool scm_filter_match(struct scan_cache_entry *db_entry, + struct scan_filter *filter, + struct security_info *security) +{ + int i; + bool match = false; + struct roam_filter_params *roam_params; + + roam_params = &filter->roam_params; + + if (filter->p2p_results && !db_entry->is_p2p) + return false; + + for (i = 0; i < roam_params->num_bssid_avoid_list; i++) + if (qdf_is_macaddr_equal(&roam_params->bssid_avoid_list[i], + &db_entry->bssid)) + return false; + + match = false; + if (db_entry->ssid.length) { + for (i = 0; i < filter->num_of_ssid; i++) { + if (util_is_ssid_match(&filter->ssid_list[i], + &db_entry->ssid)) { + match = true; + break; + } + } + } + if (!match && filter->num_of_ssid) + return false; + + match = false; + /* TO do Fill p2p MAC*/ + for (i = 0; i < filter->num_of_bssid; i++) { + if (util_is_bssid_match(&filter->bssid_list[i], + &db_entry->bssid)) { + match = true; + break; + } + /* TODO match p2p mac */ + } + if (!match && filter->num_of_bssid) + return false; + + match = false; + for (i = 0; i < filter->num_of_channels; i++) { + if (!filter->channel_list[i] || ( + (filter->channel_list[i] == + db_entry->channel.chan_idx))) { + match = true; + break; + } + } + + if (!match && filter->num_of_channels) + return false; + + if (filter->rrm_measurement_filter) + return true; + + /* TODO match phyMode */ + + if (!filter->ignore_auth_enc_type && + !scm_is_security_match(filter, + db_entry, security)) + return false; + + if (!util_is_bss_type_match(filter->bss_type, + db_entry->cap_info)) + return false; + + /* TODO match rate set */ + + if (filter->only_wmm_ap && + !db_entry->ie_list.wmeinfo && + !db_entry->ie_list.wmeparam) + return false; + + if (!util_country_code_match(filter->country, + db_entry->ie_list.country)) + return false; + + if (!util_mdie_match(filter->mobility_domain, + (struct rsn_mdie *)db_entry->ie_list.mdie)) + return false; + + return true; +} diff --git a/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c b/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c index 95f0e0a9dcd7..1d2bfef126ed 100644 --- a/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c +++ b/umac/scan/dispatcher/src/wlan_scan_ucfg_api.c @@ -36,6 +36,35 @@ QDF_STATUS ucfg_scan_register_bcn_cb(struct wlan_objmgr_psoc *psoc, return scm_scan_register_bcn_cb(psoc, cb, type); } +qdf_list_t *ucfg_scan_get_result(struct wlan_objmgr_pdev *pdev, + struct scan_filter *filter) +{ + return scm_get_scan_result(pdev, filter); +} + +QDF_STATUS ucfg_scan_db_iterate(struct wlan_objmgr_pdev *pdev, + scan_iterator_func func, void *arg) +{ + return scm_iterate_scan_db(pdev, func, arg); +} + +QDF_STATUS ucfg_scan_purge_results(qdf_list_t *scan_list) +{ + return scm_purge_scan_results(scan_list); +} + +QDF_STATUS ucfg_scan_flush_results(struct wlan_objmgr_pdev *pdev, + struct scan_filter *filter) +{ + return scm_flush_results(pdev, filter); +} + +void ucfg_scan_filter_valid_channel(struct wlan_objmgr_pdev *pdev, + uint8_t *chan_list, uint32_t num_chan) +{ + scm_filter_valid_channel(pdev, chan_list, num_chan); +} + QDF_STATUS ucfg_scan_init(void) { QDF_STATUS status;