162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP Wireless LAN device driver: scan ioctl and command handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011-2020 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "decl.h" 962306a36Sopenharmony_ci#include "ioctl.h" 1062306a36Sopenharmony_ci#include "util.h" 1162306a36Sopenharmony_ci#include "fw.h" 1262306a36Sopenharmony_ci#include "main.h" 1362306a36Sopenharmony_ci#include "11n.h" 1462306a36Sopenharmony_ci#include "cfg80211.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* The maximum number of channels the firmware can scan per command */ 1762306a36Sopenharmony_ci#define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD 4 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* Memory needed to store a max sized Channel List TLV for a firmware scan */ 2262306a36Sopenharmony_ci#define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ 2362306a36Sopenharmony_ci + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ 2462306a36Sopenharmony_ci *sizeof(struct mwifiex_chan_scan_param_set))) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Memory needed to store supported rate */ 2762306a36Sopenharmony_ci#define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ 2862306a36Sopenharmony_ci + HOSTCMD_SUPPORTED_RATES) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Memory needed to store a max number/size WildCard SSID TLV for a firmware 3162306a36Sopenharmony_ci scan */ 3262306a36Sopenharmony_ci#define WILDCARD_SSID_TLV_MAX_SIZE \ 3362306a36Sopenharmony_ci (MWIFIEX_MAX_SSID_LIST_LENGTH * \ 3462306a36Sopenharmony_ci (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ 3562306a36Sopenharmony_ci + IEEE80211_MAX_SSID_LEN)) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ 3862306a36Sopenharmony_ci#define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ 3962306a36Sopenharmony_ci + sizeof(struct mwifiex_ie_types_num_probes) \ 4062306a36Sopenharmony_ci + sizeof(struct mwifiex_ie_types_htcap) \ 4162306a36Sopenharmony_ci + CHAN_TLV_MAX_SIZE \ 4262306a36Sopenharmony_ci + RATE_TLV_MAX_SIZE \ 4362306a36Sopenharmony_ci + WILDCARD_SSID_TLV_MAX_SIZE) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciunion mwifiex_scan_cmd_config_tlv { 4762306a36Sopenharmony_ci /* Scan configuration (variable length) */ 4862306a36Sopenharmony_ci struct mwifiex_scan_cmd_config config; 4962306a36Sopenharmony_ci /* Max allocated block */ 5062306a36Sopenharmony_ci u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cienum cipher_suite { 5462306a36Sopenharmony_ci CIPHER_SUITE_TKIP, 5562306a36Sopenharmony_ci CIPHER_SUITE_CCMP, 5662306a36Sopenharmony_ci CIPHER_SUITE_MAX 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_cistatic u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { 5962306a36Sopenharmony_ci { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ 6062306a36Sopenharmony_ci { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_cistatic u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { 6362306a36Sopenharmony_ci { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ 6462306a36Sopenharmony_ci { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void 6862306a36Sopenharmony_ci_dbg_security_flags(int log_level, const char *func, const char *desc, 6962306a36Sopenharmony_ci struct mwifiex_private *priv, 7062306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci _mwifiex_dbg(priv->adapter, log_level, 7362306a36Sopenharmony_ci "info: %s: %s:\twpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\tEncMode=%#x privacy=%#x\n", 7462306a36Sopenharmony_ci func, desc, 7562306a36Sopenharmony_ci bss_desc->bcn_wpa_ie ? 7662306a36Sopenharmony_ci bss_desc->bcn_wpa_ie->vend_hdr.element_id : 0, 7762306a36Sopenharmony_ci bss_desc->bcn_rsn_ie ? 7862306a36Sopenharmony_ci bss_desc->bcn_rsn_ie->ieee_hdr.element_id : 0, 7962306a36Sopenharmony_ci priv->sec_info.wep_enabled ? "e" : "d", 8062306a36Sopenharmony_ci priv->sec_info.wpa_enabled ? "e" : "d", 8162306a36Sopenharmony_ci priv->sec_info.wpa2_enabled ? "e" : "d", 8262306a36Sopenharmony_ci priv->sec_info.encryption_mode, 8362306a36Sopenharmony_ci bss_desc->privacy); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci#define dbg_security_flags(mask, desc, priv, bss_desc) \ 8662306a36Sopenharmony_ci _dbg_security_flags(MWIFIEX_DBG_##mask, desc, __func__, priv, bss_desc) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool 8962306a36Sopenharmony_cihas_ieee_hdr(struct ieee_types_generic *ie, u8 key) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return (ie && ie->ieee_hdr.element_id == key); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic bool 9562306a36Sopenharmony_cihas_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return (ie && ie->vend_hdr.element_id == key); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * This function parses a given IE for a given OUI. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * This is used to parse a WPA/RSN IE to find if it has 10462306a36Sopenharmony_ci * a given oui in PTK. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistatic u8 10762306a36Sopenharmony_cimwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u8 count; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci count = iebody->ptk_cnt[0]; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* There could be multiple OUIs for PTK hence 11462306a36Sopenharmony_ci 1) Take the length. 11562306a36Sopenharmony_ci 2) Check all the OUIs for AES. 11662306a36Sopenharmony_ci 3) If one of them is AES then pass success. */ 11762306a36Sopenharmony_ci while (count) { 11862306a36Sopenharmony_ci if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) 11962306a36Sopenharmony_ci return MWIFIEX_OUI_PRESENT; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci --count; 12262306a36Sopenharmony_ci if (count) 12362306a36Sopenharmony_ci iebody = (struct ie_body *) ((u8 *) iebody + 12462306a36Sopenharmony_ci sizeof(iebody->ptk_body)); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci pr_debug("info: %s: OUI is not found in PTK\n", __func__); 12862306a36Sopenharmony_ci return MWIFIEX_OUI_NOT_PRESENT; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * This function checks if a given OUI is present in a RSN IE. 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * The function first checks if a RSN IE is present or not in the 13562306a36Sopenharmony_ci * BSS descriptor. It tries to locate the OUI only if such an IE is 13662306a36Sopenharmony_ci * present. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic u8 13962306a36Sopenharmony_cimwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci u8 *oui; 14262306a36Sopenharmony_ci struct ie_body *iebody; 14362306a36Sopenharmony_ci u8 ret = MWIFIEX_OUI_NOT_PRESENT; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) { 14662306a36Sopenharmony_ci iebody = (struct ie_body *) 14762306a36Sopenharmony_ci (((u8 *) bss_desc->bcn_rsn_ie->data) + 14862306a36Sopenharmony_ci RSN_GTK_OUI_OFFSET); 14962306a36Sopenharmony_ci oui = &mwifiex_rsn_oui[cipher][0]; 15062306a36Sopenharmony_ci ret = mwifiex_search_oui_in_ie(iebody, oui); 15162306a36Sopenharmony_ci if (ret) 15262306a36Sopenharmony_ci return ret; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * This function checks if a given OUI is present in a WPA IE. 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * The function first checks if a WPA IE is present or not in the 16162306a36Sopenharmony_ci * BSS descriptor. It tries to locate the OUI only if such an IE is 16262306a36Sopenharmony_ci * present. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic u8 16562306a36Sopenharmony_cimwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u8 *oui; 16862306a36Sopenharmony_ci struct ie_body *iebody; 16962306a36Sopenharmony_ci u8 ret = MWIFIEX_OUI_NOT_PRESENT; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) { 17262306a36Sopenharmony_ci iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data + 17362306a36Sopenharmony_ci WPA_GTK_OUI_OFFSET); 17462306a36Sopenharmony_ci oui = &mwifiex_wpa_oui[cipher][0]; 17562306a36Sopenharmony_ci ret = mwifiex_search_oui_in_ie(iebody, oui); 17662306a36Sopenharmony_ci if (ret) 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * This function compares two SSIDs and checks if they match. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cis32 18662306a36Sopenharmony_cimwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) 18962306a36Sopenharmony_ci return -1; 19062306a36Sopenharmony_ci return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * This function checks if wapi is enabled in driver and scanned network is 19562306a36Sopenharmony_ci * compatible with it. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic bool 19862306a36Sopenharmony_cimwifiex_is_bss_wapi(struct mwifiex_private *priv, 19962306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci if (priv->sec_info.wapi_enabled && 20262306a36Sopenharmony_ci has_ieee_hdr(bss_desc->bcn_wapi_ie, WLAN_EID_BSS_AC_ACCESS_DELAY)) 20362306a36Sopenharmony_ci return true; 20462306a36Sopenharmony_ci return false; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * This function checks if driver is configured with no security mode and 20962306a36Sopenharmony_ci * scanned network is compatible with it. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic bool 21262306a36Sopenharmony_cimwifiex_is_bss_no_sec(struct mwifiex_private *priv, 21362306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && 21662306a36Sopenharmony_ci !priv->sec_info.wpa2_enabled && 21762306a36Sopenharmony_ci !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) && 21862306a36Sopenharmony_ci !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) && 21962306a36Sopenharmony_ci !priv->sec_info.encryption_mode && !bss_desc->privacy) { 22062306a36Sopenharmony_ci return true; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci return false; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * This function checks if static WEP is enabled in driver and scanned network 22762306a36Sopenharmony_ci * is compatible with it. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic bool 23062306a36Sopenharmony_cimwifiex_is_bss_static_wep(struct mwifiex_private *priv, 23162306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && 23462306a36Sopenharmony_ci !priv->sec_info.wpa2_enabled && bss_desc->privacy) { 23562306a36Sopenharmony_ci return true; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci return false; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * This function checks if wpa is enabled in driver and scanned network is 24262306a36Sopenharmony_ci * compatible with it. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic bool 24562306a36Sopenharmony_cimwifiex_is_bss_wpa(struct mwifiex_private *priv, 24662306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && 24962306a36Sopenharmony_ci !priv->sec_info.wpa2_enabled && 25062306a36Sopenharmony_ci has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * Privacy bit may NOT be set in some APs like 25362306a36Sopenharmony_ci * LinkSys WRT54G && bss_desc->privacy 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci ) { 25662306a36Sopenharmony_ci dbg_security_flags(INFO, "WPA", priv, bss_desc); 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return false; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * This function checks if wpa2 is enabled in driver and scanned network is 26462306a36Sopenharmony_ci * compatible with it. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_cistatic bool 26762306a36Sopenharmony_cimwifiex_is_bss_wpa2(struct mwifiex_private *priv, 26862306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && 27162306a36Sopenharmony_ci priv->sec_info.wpa2_enabled && 27262306a36Sopenharmony_ci has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) { 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * Privacy bit may NOT be set in some APs like 27562306a36Sopenharmony_ci * LinkSys WRT54G && bss_desc->privacy 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci dbg_security_flags(INFO, "WAP2", priv, bss_desc); 27862306a36Sopenharmony_ci return true; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci return false; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * This function checks if adhoc AES is enabled in driver and scanned network is 28562306a36Sopenharmony_ci * compatible with it. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic bool 28862306a36Sopenharmony_cimwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, 28962306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && 29262306a36Sopenharmony_ci !priv->sec_info.wpa2_enabled && 29362306a36Sopenharmony_ci !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) && 29462306a36Sopenharmony_ci !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) && 29562306a36Sopenharmony_ci !priv->sec_info.encryption_mode && bss_desc->privacy) { 29662306a36Sopenharmony_ci return true; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci return false; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * This function checks if dynamic WEP is enabled in driver and scanned network 30362306a36Sopenharmony_ci * is compatible with it. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic bool 30662306a36Sopenharmony_cimwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, 30762306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && 31062306a36Sopenharmony_ci !priv->sec_info.wpa2_enabled && 31162306a36Sopenharmony_ci !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) && 31262306a36Sopenharmony_ci !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) && 31362306a36Sopenharmony_ci priv->sec_info.encryption_mode && bss_desc->privacy) { 31462306a36Sopenharmony_ci dbg_security_flags(INFO, "dynamic", priv, bss_desc); 31562306a36Sopenharmony_ci return true; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci return false; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* 32162306a36Sopenharmony_ci * This function checks if a scanned network is compatible with the driver 32262306a36Sopenharmony_ci * settings. 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * WEP WPA WPA2 ad-hoc encrypt Network 32562306a36Sopenharmony_ci * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible 32662306a36Sopenharmony_ci * 0 0 0 0 NONE 0 0 0 yes No security 32762306a36Sopenharmony_ci * 0 1 0 0 x 1x 1 x yes WPA (disable 32862306a36Sopenharmony_ci * HT if no AES) 32962306a36Sopenharmony_ci * 0 0 1 0 x 1x x 1 yes WPA2 (disable 33062306a36Sopenharmony_ci * HT if no AES) 33162306a36Sopenharmony_ci * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES 33262306a36Sopenharmony_ci * 1 0 0 0 NONE 1 0 0 yes Static WEP 33362306a36Sopenharmony_ci * (disable HT) 33462306a36Sopenharmony_ci * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP 33562306a36Sopenharmony_ci * 33662306a36Sopenharmony_ci * Compatibility is not matched while roaming, except for mode. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_cistatic s32 33962306a36Sopenharmony_cimwifiex_is_network_compatible(struct mwifiex_private *priv, 34062306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc, u32 mode) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci bss_desc->disable_11n = false; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Don't check for compatibility if roaming */ 34762306a36Sopenharmony_ci if (priv->media_connected && 34862306a36Sopenharmony_ci (priv->bss_mode == NL80211_IFTYPE_STATION) && 34962306a36Sopenharmony_ci (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (priv->wps.session_enable) { 35362306a36Sopenharmony_ci mwifiex_dbg(adapter, IOCTL, 35462306a36Sopenharmony_ci "info: return success directly in WPS period\n"); 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (bss_desc->chan_sw_ie_present) { 35962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 36062306a36Sopenharmony_ci "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); 36162306a36Sopenharmony_ci return -1; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (mwifiex_is_bss_wapi(priv, bss_desc)) { 36562306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 36662306a36Sopenharmony_ci "info: return success for WAPI AP\n"); 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (bss_desc->bss_mode == mode) { 37162306a36Sopenharmony_ci if (mwifiex_is_bss_no_sec(priv, bss_desc)) { 37262306a36Sopenharmony_ci /* No security */ 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci } else if (mwifiex_is_bss_static_wep(priv, bss_desc)) { 37562306a36Sopenharmony_ci /* Static WEP enabled */ 37662306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 37762306a36Sopenharmony_ci "info: Disable 11n in WEP mode.\n"); 37862306a36Sopenharmony_ci bss_desc->disable_11n = true; 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci } else if (mwifiex_is_bss_wpa(priv, bss_desc)) { 38162306a36Sopenharmony_ci /* WPA enabled */ 38262306a36Sopenharmony_ci if (((priv->adapter->config_bands & BAND_GN || 38362306a36Sopenharmony_ci priv->adapter->config_bands & BAND_AN) && 38462306a36Sopenharmony_ci bss_desc->bcn_ht_cap) && 38562306a36Sopenharmony_ci !mwifiex_is_wpa_oui_present(bss_desc, 38662306a36Sopenharmony_ci CIPHER_SUITE_CCMP)) { 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (mwifiex_is_wpa_oui_present 38962306a36Sopenharmony_ci (bss_desc, CIPHER_SUITE_TKIP)) { 39062306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 39162306a36Sopenharmony_ci "info: Disable 11n if AES\t" 39262306a36Sopenharmony_ci "is not supported by AP\n"); 39362306a36Sopenharmony_ci bss_desc->disable_11n = true; 39462306a36Sopenharmony_ci } else { 39562306a36Sopenharmony_ci return -1; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci } else if (mwifiex_is_bss_wpa2(priv, bss_desc)) { 40062306a36Sopenharmony_ci /* WPA2 enabled */ 40162306a36Sopenharmony_ci if (((priv->adapter->config_bands & BAND_GN || 40262306a36Sopenharmony_ci priv->adapter->config_bands & BAND_AN) && 40362306a36Sopenharmony_ci bss_desc->bcn_ht_cap) && 40462306a36Sopenharmony_ci !mwifiex_is_rsn_oui_present(bss_desc, 40562306a36Sopenharmony_ci CIPHER_SUITE_CCMP)) { 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (mwifiex_is_rsn_oui_present 40862306a36Sopenharmony_ci (bss_desc, CIPHER_SUITE_TKIP)) { 40962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 41062306a36Sopenharmony_ci "info: Disable 11n if AES\t" 41162306a36Sopenharmony_ci "is not supported by AP\n"); 41262306a36Sopenharmony_ci bss_desc->disable_11n = true; 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci return -1; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci } else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) { 41962306a36Sopenharmony_ci /* Ad-hoc AES enabled */ 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci } else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) { 42262306a36Sopenharmony_ci /* Dynamic WEP enabled */ 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Security doesn't match */ 42762306a36Sopenharmony_ci dbg_security_flags(ERROR, "failed", priv, bss_desc); 42862306a36Sopenharmony_ci return -1; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Mode doesn't match */ 43262306a36Sopenharmony_ci return -1; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* 43662306a36Sopenharmony_ci * This function creates a channel list for the driver to scan, based 43762306a36Sopenharmony_ci * on region/band information. 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * This routine is used for any scan that is not provided with a 44062306a36Sopenharmony_ci * specific channel list to scan. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_cistatic int 44362306a36Sopenharmony_cimwifiex_scan_create_channel_list(struct mwifiex_private *priv, 44462306a36Sopenharmony_ci const struct mwifiex_user_scan_cfg 44562306a36Sopenharmony_ci *user_scan_in, 44662306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set 44762306a36Sopenharmony_ci *scan_chan_list, 44862306a36Sopenharmony_ci u8 filtered_scan) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci enum nl80211_band band; 45162306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 45262306a36Sopenharmony_ci struct ieee80211_channel *ch; 45362306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 45462306a36Sopenharmony_ci int chan_idx = 0, i; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (band = 0; (band < NUM_NL80211_BANDS) ; band++) { 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!priv->wdev.wiphy->bands[band]) 45962306a36Sopenharmony_ci continue; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci sband = priv->wdev.wiphy->bands[band]; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci for (i = 0; (i < sband->n_channels) ; i++) { 46462306a36Sopenharmony_ci ch = &sband->channels[i]; 46562306a36Sopenharmony_ci if (ch->flags & IEEE80211_CHAN_DISABLED) 46662306a36Sopenharmony_ci continue; 46762306a36Sopenharmony_ci scan_chan_list[chan_idx].radio_type = band; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (user_scan_in && 47062306a36Sopenharmony_ci user_scan_in->chan_list[0].scan_time) 47162306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 47262306a36Sopenharmony_ci cpu_to_le16((u16) user_scan_in-> 47362306a36Sopenharmony_ci chan_list[0].scan_time); 47462306a36Sopenharmony_ci else if ((ch->flags & IEEE80211_CHAN_NO_IR) || 47562306a36Sopenharmony_ci (ch->flags & IEEE80211_CHAN_RADAR)) 47662306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 47762306a36Sopenharmony_ci cpu_to_le16(adapter->passive_scan_time); 47862306a36Sopenharmony_ci else 47962306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 48062306a36Sopenharmony_ci cpu_to_le16(adapter->active_scan_time); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (ch->flags & IEEE80211_CHAN_NO_IR) 48362306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 48462306a36Sopenharmony_ci |= (MWIFIEX_PASSIVE_SCAN | 48562306a36Sopenharmony_ci MWIFIEX_HIDDEN_SSID_REPORT); 48662306a36Sopenharmony_ci else 48762306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 48862306a36Sopenharmony_ci &= ~MWIFIEX_PASSIVE_SCAN; 48962306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_number = 49062306a36Sopenharmony_ci (u32) ch->hw_value; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 49362306a36Sopenharmony_ci |= MWIFIEX_DISABLE_CHAN_FILT; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (filtered_scan && 49662306a36Sopenharmony_ci !((ch->flags & IEEE80211_CHAN_NO_IR) || 49762306a36Sopenharmony_ci (ch->flags & IEEE80211_CHAN_RADAR))) 49862306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 49962306a36Sopenharmony_ci cpu_to_le16(adapter->specific_scan_time); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci chan_idx++; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci return chan_idx; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/* This function creates a channel list tlv for bgscan config, based 50962306a36Sopenharmony_ci * on region/band information. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_cistatic int 51262306a36Sopenharmony_cimwifiex_bgscan_create_channel_list(struct mwifiex_private *priv, 51362306a36Sopenharmony_ci const struct mwifiex_bg_scan_cfg 51462306a36Sopenharmony_ci *bgscan_cfg_in, 51562306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set 51662306a36Sopenharmony_ci *scan_chan_list) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci enum nl80211_band band; 51962306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 52062306a36Sopenharmony_ci struct ieee80211_channel *ch; 52162306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 52262306a36Sopenharmony_ci int chan_idx = 0, i; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for (band = 0; (band < NUM_NL80211_BANDS); band++) { 52562306a36Sopenharmony_ci if (!priv->wdev.wiphy->bands[band]) 52662306a36Sopenharmony_ci continue; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci sband = priv->wdev.wiphy->bands[band]; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci for (i = 0; (i < sband->n_channels) ; i++) { 53162306a36Sopenharmony_ci ch = &sband->channels[i]; 53262306a36Sopenharmony_ci if (ch->flags & IEEE80211_CHAN_DISABLED) 53362306a36Sopenharmony_ci continue; 53462306a36Sopenharmony_ci scan_chan_list[chan_idx].radio_type = band; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (bgscan_cfg_in->chan_list[0].scan_time) 53762306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 53862306a36Sopenharmony_ci cpu_to_le16((u16)bgscan_cfg_in-> 53962306a36Sopenharmony_ci chan_list[0].scan_time); 54062306a36Sopenharmony_ci else if (ch->flags & IEEE80211_CHAN_NO_IR) 54162306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 54262306a36Sopenharmony_ci cpu_to_le16(adapter->passive_scan_time); 54362306a36Sopenharmony_ci else 54462306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 54562306a36Sopenharmony_ci cpu_to_le16(adapter-> 54662306a36Sopenharmony_ci specific_scan_time); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (ch->flags & IEEE80211_CHAN_NO_IR) 54962306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 55062306a36Sopenharmony_ci |= MWIFIEX_PASSIVE_SCAN; 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 55362306a36Sopenharmony_ci &= ~MWIFIEX_PASSIVE_SCAN; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_number = 55662306a36Sopenharmony_ci (u32)ch->hw_value; 55762306a36Sopenharmony_ci chan_idx++; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci return chan_idx; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* This function appends rate TLV to scan config command. */ 56462306a36Sopenharmony_cistatic int 56562306a36Sopenharmony_cimwifiex_append_rate_tlv(struct mwifiex_private *priv, 56662306a36Sopenharmony_ci struct mwifiex_scan_cmd_config *scan_cfg_out, 56762306a36Sopenharmony_ci u8 radio) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct mwifiex_ie_types_rates_param_set *rates_tlv; 57062306a36Sopenharmony_ci u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos; 57162306a36Sopenharmony_ci u32 rates_size; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci memset(rates, 0, sizeof(rates)); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (priv->scan_request) 57862306a36Sopenharmony_ci rates_size = mwifiex_get_rates_from_cfg80211(priv, rates, 57962306a36Sopenharmony_ci radio); 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci rates_size = mwifiex_get_supported_rates(priv, rates); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, CMD, 58462306a36Sopenharmony_ci "info: SCAN_CMD: Rates size = %d\n", 58562306a36Sopenharmony_ci rates_size); 58662306a36Sopenharmony_ci rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos; 58762306a36Sopenharmony_ci rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); 58862306a36Sopenharmony_ci rates_tlv->header.len = cpu_to_le16((u16) rates_size); 58962306a36Sopenharmony_ci memcpy(rates_tlv->rates, rates, rates_size); 59062306a36Sopenharmony_ci scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return rates_size; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/* 59662306a36Sopenharmony_ci * This function constructs and sends multiple scan config commands to 59762306a36Sopenharmony_ci * the firmware. 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * Previous routines in the code flow have created a scan command configuration 60062306a36Sopenharmony_ci * with any requested TLVs. This function splits the channel TLV into maximum 60162306a36Sopenharmony_ci * channels supported per scan lists and sends the portion of the channel TLV, 60262306a36Sopenharmony_ci * along with the other TLVs, to the firmware. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_cistatic int 60562306a36Sopenharmony_cimwifiex_scan_channel_list(struct mwifiex_private *priv, 60662306a36Sopenharmony_ci u32 max_chan_per_scan, u8 filtered_scan, 60762306a36Sopenharmony_ci struct mwifiex_scan_cmd_config *scan_cfg_out, 60862306a36Sopenharmony_ci struct mwifiex_ie_types_chan_list_param_set 60962306a36Sopenharmony_ci *chan_tlv_out, 61062306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set *scan_chan_list) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 61362306a36Sopenharmony_ci int ret = 0; 61462306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set *tmp_chan_list; 61562306a36Sopenharmony_ci u32 tlv_idx, rates_size, cmd_no; 61662306a36Sopenharmony_ci u32 total_scan_time; 61762306a36Sopenharmony_ci u32 done_early; 61862306a36Sopenharmony_ci u8 radio_type; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { 62162306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 62262306a36Sopenharmony_ci "info: Scan: Null detect: %p, %p, %p\n", 62362306a36Sopenharmony_ci scan_cfg_out, chan_tlv_out, scan_chan_list); 62462306a36Sopenharmony_ci return -1; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Check csa channel expiry before preparing scan list */ 62862306a36Sopenharmony_ci mwifiex_11h_get_csa_closed_channel(priv); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Set the temp channel struct pointer to the start of the desired 63362306a36Sopenharmony_ci list */ 63462306a36Sopenharmony_ci tmp_chan_list = scan_chan_list; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Loop through the desired channel list, sending a new firmware scan 63762306a36Sopenharmony_ci commands for each max_chan_per_scan channels (or for 1,6,11 63862306a36Sopenharmony_ci individually if configured accordingly) */ 63962306a36Sopenharmony_ci while (tmp_chan_list->chan_number) { 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci tlv_idx = 0; 64262306a36Sopenharmony_ci total_scan_time = 0; 64362306a36Sopenharmony_ci radio_type = 0; 64462306a36Sopenharmony_ci chan_tlv_out->header.len = 0; 64562306a36Sopenharmony_ci done_early = false; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * Construct the Channel TLV for the scan command. Continue to 64962306a36Sopenharmony_ci * insert channel TLVs until: 65062306a36Sopenharmony_ci * - the tlv_idx hits the maximum configured per scan command 65162306a36Sopenharmony_ci * - the next channel to insert is 0 (end of desired channel 65262306a36Sopenharmony_ci * list) 65362306a36Sopenharmony_ci * - done_early is set (controlling individual scanning of 65462306a36Sopenharmony_ci * 1,6,11) 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ci while (tlv_idx < max_chan_per_scan && 65762306a36Sopenharmony_ci tmp_chan_list->chan_number && !done_early) { 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (tmp_chan_list->chan_number == priv->csa_chan) { 66062306a36Sopenharmony_ci tmp_chan_list++; 66162306a36Sopenharmony_ci continue; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci radio_type = tmp_chan_list->radio_type; 66562306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 66662306a36Sopenharmony_ci "info: Scan: Chan(%3d), Radio(%d),\t" 66762306a36Sopenharmony_ci "Mode(%d, %d), Dur(%d)\n", 66862306a36Sopenharmony_ci tmp_chan_list->chan_number, 66962306a36Sopenharmony_ci tmp_chan_list->radio_type, 67062306a36Sopenharmony_ci tmp_chan_list->chan_scan_mode_bitmap 67162306a36Sopenharmony_ci & MWIFIEX_PASSIVE_SCAN, 67262306a36Sopenharmony_ci (tmp_chan_list->chan_scan_mode_bitmap 67362306a36Sopenharmony_ci & MWIFIEX_DISABLE_CHAN_FILT) >> 1, 67462306a36Sopenharmony_ci le16_to_cpu(tmp_chan_list->max_scan_time)); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Copy the current channel TLV to the command being 67762306a36Sopenharmony_ci prepared */ 67862306a36Sopenharmony_ci memcpy(chan_tlv_out->chan_scan_param + tlv_idx, 67962306a36Sopenharmony_ci tmp_chan_list, 68062306a36Sopenharmony_ci sizeof(chan_tlv_out->chan_scan_param)); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Increment the TLV header length by the size 68362306a36Sopenharmony_ci appended */ 68462306a36Sopenharmony_ci le16_unaligned_add_cpu(&chan_tlv_out->header.len, 68562306a36Sopenharmony_ci sizeof( 68662306a36Sopenharmony_ci chan_tlv_out->chan_scan_param)); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * The tlv buffer length is set to the number of bytes 69062306a36Sopenharmony_ci * of the between the channel tlv pointer and the start 69162306a36Sopenharmony_ci * of the tlv buffer. This compensates for any TLVs 69262306a36Sopenharmony_ci * that were appended before the channel list. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - 69562306a36Sopenharmony_ci scan_cfg_out->tlv_buf); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* Add the size of the channel tlv header and the data 69862306a36Sopenharmony_ci length */ 69962306a36Sopenharmony_ci scan_cfg_out->tlv_buf_len += 70062306a36Sopenharmony_ci (sizeof(chan_tlv_out->header) 70162306a36Sopenharmony_ci + le16_to_cpu(chan_tlv_out->header.len)); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Increment the index to the channel tlv we are 70462306a36Sopenharmony_ci constructing */ 70562306a36Sopenharmony_ci tlv_idx++; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Count the total scan time per command */ 70862306a36Sopenharmony_ci total_scan_time += 70962306a36Sopenharmony_ci le16_to_cpu(tmp_chan_list->max_scan_time); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci done_early = false; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Stop the loop if the *current* channel is in the 71462306a36Sopenharmony_ci 1,6,11 set and we are not filtering on a BSSID 71562306a36Sopenharmony_ci or SSID. */ 71662306a36Sopenharmony_ci if (!filtered_scan && 71762306a36Sopenharmony_ci (tmp_chan_list->chan_number == 1 || 71862306a36Sopenharmony_ci tmp_chan_list->chan_number == 6 || 71962306a36Sopenharmony_ci tmp_chan_list->chan_number == 11)) 72062306a36Sopenharmony_ci done_early = true; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Increment the tmp pointer to the next channel to 72362306a36Sopenharmony_ci be scanned */ 72462306a36Sopenharmony_ci tmp_chan_list++; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Stop the loop if the *next* channel is in the 1,6,11 72762306a36Sopenharmony_ci set. This will cause it to be the only channel 72862306a36Sopenharmony_ci scanned on the next interation */ 72962306a36Sopenharmony_ci if (!filtered_scan && 73062306a36Sopenharmony_ci (tmp_chan_list->chan_number == 1 || 73162306a36Sopenharmony_ci tmp_chan_list->chan_number == 6 || 73262306a36Sopenharmony_ci tmp_chan_list->chan_number == 11)) 73362306a36Sopenharmony_ci done_early = true; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* The total scan time should be less than scan command timeout 73762306a36Sopenharmony_ci value */ 73862306a36Sopenharmony_ci if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { 73962306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 74062306a36Sopenharmony_ci "total scan time %dms\t" 74162306a36Sopenharmony_ci "is over limit (%dms), scan skipped\n", 74262306a36Sopenharmony_ci total_scan_time, 74362306a36Sopenharmony_ci MWIFIEX_MAX_TOTAL_SCAN_TIME); 74462306a36Sopenharmony_ci ret = -1; 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out, 74962306a36Sopenharmony_ci radio_type); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Send the scan command to the firmware with the specified 75262306a36Sopenharmony_ci cfg */ 75362306a36Sopenharmony_ci if (priv->adapter->ext_scan) 75462306a36Sopenharmony_ci cmd_no = HostCmd_CMD_802_11_SCAN_EXT; 75562306a36Sopenharmony_ci else 75662306a36Sopenharmony_ci cmd_no = HostCmd_CMD_802_11_SCAN; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET, 75962306a36Sopenharmony_ci 0, scan_cfg_out, false); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* rate IE is updated per scan command but same starting 76262306a36Sopenharmony_ci * pointer is used each time so that rate IE from earlier 76362306a36Sopenharmony_ci * scan_cfg_out->buf is overwritten with new one. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci scan_cfg_out->tlv_buf_len -= 76662306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header) + rates_size; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (ret) { 76962306a36Sopenharmony_ci mwifiex_cancel_pending_scan_cmd(adapter); 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (ret) 77562306a36Sopenharmony_ci return -1; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* 78162306a36Sopenharmony_ci * This function constructs a scan command configuration structure to use 78262306a36Sopenharmony_ci * in scan commands. 78362306a36Sopenharmony_ci * 78462306a36Sopenharmony_ci * Application layer or other functions can invoke network scanning 78562306a36Sopenharmony_ci * with a scan configuration supplied in a user scan configuration structure. 78662306a36Sopenharmony_ci * This structure is used as the basis of one or many scan command configuration 78762306a36Sopenharmony_ci * commands that are sent to the command processing module and eventually to the 78862306a36Sopenharmony_ci * firmware. 78962306a36Sopenharmony_ci * 79062306a36Sopenharmony_ci * This function creates a scan command configuration structure based on the 79162306a36Sopenharmony_ci * following user supplied parameters (if present): 79262306a36Sopenharmony_ci * - SSID filter 79362306a36Sopenharmony_ci * - BSSID filter 79462306a36Sopenharmony_ci * - Number of Probes to be sent 79562306a36Sopenharmony_ci * - Channel list 79662306a36Sopenharmony_ci * 79762306a36Sopenharmony_ci * If the SSID or BSSID filter is not present, the filter is disabled/cleared. 79862306a36Sopenharmony_ci * If the number of probes is not set, adapter default setting is used. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_cistatic void 80162306a36Sopenharmony_cimwifiex_config_scan(struct mwifiex_private *priv, 80262306a36Sopenharmony_ci const struct mwifiex_user_scan_cfg *user_scan_in, 80362306a36Sopenharmony_ci struct mwifiex_scan_cmd_config *scan_cfg_out, 80462306a36Sopenharmony_ci struct mwifiex_ie_types_chan_list_param_set **chan_list_out, 80562306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set *scan_chan_list, 80662306a36Sopenharmony_ci u8 *max_chan_per_scan, u8 *filtered_scan, 80762306a36Sopenharmony_ci u8 *scan_current_only) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 81062306a36Sopenharmony_ci struct mwifiex_ie_types_num_probes *num_probes_tlv; 81162306a36Sopenharmony_ci struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv; 81262306a36Sopenharmony_ci struct mwifiex_ie_types_random_mac *random_mac_tlv; 81362306a36Sopenharmony_ci struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; 81462306a36Sopenharmony_ci struct mwifiex_ie_types_bssid_list *bssid_tlv; 81562306a36Sopenharmony_ci u8 *tlv_pos; 81662306a36Sopenharmony_ci u32 num_probes; 81762306a36Sopenharmony_ci u32 ssid_len; 81862306a36Sopenharmony_ci u32 chan_idx; 81962306a36Sopenharmony_ci u32 scan_type; 82062306a36Sopenharmony_ci u16 scan_dur; 82162306a36Sopenharmony_ci u8 channel; 82262306a36Sopenharmony_ci u8 radio_type; 82362306a36Sopenharmony_ci int i; 82462306a36Sopenharmony_ci u8 ssid_filter; 82562306a36Sopenharmony_ci struct mwifiex_ie_types_htcap *ht_cap; 82662306a36Sopenharmony_ci struct mwifiex_ie_types_bss_mode *bss_mode; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* The tlv_buf_len is calculated for each scan command. The TLVs added 82962306a36Sopenharmony_ci in this routine will be preserved since the routine that sends the 83062306a36Sopenharmony_ci command will append channelTLVs at *chan_list_out. The difference 83162306a36Sopenharmony_ci between the *chan_list_out and the tlv_buf start will be used to 83262306a36Sopenharmony_ci calculate the size of anything we add in this routine. */ 83362306a36Sopenharmony_ci scan_cfg_out->tlv_buf_len = 0; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Running tlv pointer. Assigned to chan_list_out at end of function 83662306a36Sopenharmony_ci so later routines know where channels can be added to the command 83762306a36Sopenharmony_ci buf */ 83862306a36Sopenharmony_ci tlv_pos = scan_cfg_out->tlv_buf; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* Initialize the scan as un-filtered; the flag is later set to TRUE 84162306a36Sopenharmony_ci below if a SSID or BSSID filter is sent in the command */ 84262306a36Sopenharmony_ci *filtered_scan = false; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Initialize the scan as not being only on the current channel. If 84562306a36Sopenharmony_ci the channel list is customized, only contains one channel, and is 84662306a36Sopenharmony_ci the active channel, this is set true and data flow is not halted. */ 84762306a36Sopenharmony_ci *scan_current_only = false; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (user_scan_in) { 85062306a36Sopenharmony_ci u8 tmpaddr[ETH_ALEN]; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Default the ssid_filter flag to TRUE, set false under 85362306a36Sopenharmony_ci certain wildcard conditions and qualified by the existence 85462306a36Sopenharmony_ci of an SSID list before marking the scan as filtered */ 85562306a36Sopenharmony_ci ssid_filter = true; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* Set the BSS type scan filter, use Adapter setting if 85862306a36Sopenharmony_ci unset */ 85962306a36Sopenharmony_ci scan_cfg_out->bss_mode = 86062306a36Sopenharmony_ci (u8)(user_scan_in->bss_mode ?: adapter->scan_mode); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* Set the number of probes to send, use Adapter setting 86362306a36Sopenharmony_ci if unset */ 86462306a36Sopenharmony_ci num_probes = user_scan_in->num_probes ?: adapter->scan_probes; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * Set the BSSID filter to the incoming configuration, 86862306a36Sopenharmony_ci * if non-zero. If not set, it will remain disabled 86962306a36Sopenharmony_ci * (all zeros). 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_ci memcpy(scan_cfg_out->specific_bssid, 87262306a36Sopenharmony_ci user_scan_in->specific_bssid, 87362306a36Sopenharmony_ci sizeof(scan_cfg_out->specific_bssid)); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (adapter->ext_scan && 87862306a36Sopenharmony_ci !is_zero_ether_addr(tmpaddr)) { 87962306a36Sopenharmony_ci bssid_tlv = 88062306a36Sopenharmony_ci (struct mwifiex_ie_types_bssid_list *)tlv_pos; 88162306a36Sopenharmony_ci bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); 88262306a36Sopenharmony_ci bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); 88362306a36Sopenharmony_ci memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, 88462306a36Sopenharmony_ci ETH_ALEN); 88562306a36Sopenharmony_ci tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci for (i = 0; i < user_scan_in->num_ssids; i++) { 88962306a36Sopenharmony_ci ssid_len = user_scan_in->ssid_list[i].ssid_len; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci wildcard_ssid_tlv = 89262306a36Sopenharmony_ci (struct mwifiex_ie_types_wildcard_ssid_params *) 89362306a36Sopenharmony_ci tlv_pos; 89462306a36Sopenharmony_ci wildcard_ssid_tlv->header.type = 89562306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_WILDCARDSSID); 89662306a36Sopenharmony_ci wildcard_ssid_tlv->header.len = cpu_to_le16( 89762306a36Sopenharmony_ci (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> 89862306a36Sopenharmony_ci max_ssid_length))); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* 90162306a36Sopenharmony_ci * max_ssid_length = 0 tells firmware to perform 90262306a36Sopenharmony_ci * specific scan for the SSID filled, whereas 90362306a36Sopenharmony_ci * max_ssid_length = IEEE80211_MAX_SSID_LEN is for 90462306a36Sopenharmony_ci * wildcard scan. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci if (ssid_len) 90762306a36Sopenharmony_ci wildcard_ssid_tlv->max_ssid_length = 0; 90862306a36Sopenharmony_ci else 90962306a36Sopenharmony_ci wildcard_ssid_tlv->max_ssid_length = 91062306a36Sopenharmony_ci IEEE80211_MAX_SSID_LEN; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (!memcmp(user_scan_in->ssid_list[i].ssid, 91362306a36Sopenharmony_ci "DIRECT-", 7)) 91462306a36Sopenharmony_ci wildcard_ssid_tlv->max_ssid_length = 0xfe; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci memcpy(wildcard_ssid_tlv->ssid, 91762306a36Sopenharmony_ci user_scan_in->ssid_list[i].ssid, ssid_len); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci tlv_pos += (sizeof(wildcard_ssid_tlv->header) 92062306a36Sopenharmony_ci + le16_to_cpu(wildcard_ssid_tlv->header.len)); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 92362306a36Sopenharmony_ci "info: scan: ssid[%d]: %s, %d\n", 92462306a36Sopenharmony_ci i, wildcard_ssid_tlv->ssid, 92562306a36Sopenharmony_ci wildcard_ssid_tlv->max_ssid_length); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* Empty wildcard ssid with a maxlen will match many or 92862306a36Sopenharmony_ci potentially all SSIDs (maxlen == 32), therefore do 92962306a36Sopenharmony_ci not treat the scan as 93062306a36Sopenharmony_ci filtered. */ 93162306a36Sopenharmony_ci if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) 93262306a36Sopenharmony_ci ssid_filter = false; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* 93662306a36Sopenharmony_ci * The default number of channels sent in the command is low to 93762306a36Sopenharmony_ci * ensure the response buffer from the firmware does not 93862306a36Sopenharmony_ci * truncate scan results. That is not an issue with an SSID 93962306a36Sopenharmony_ci * or BSSID filter applied to the scan results in the firmware. 94062306a36Sopenharmony_ci */ 94162306a36Sopenharmony_ci memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN); 94262306a36Sopenharmony_ci if ((i && ssid_filter) || 94362306a36Sopenharmony_ci !is_zero_ether_addr(tmpaddr)) 94462306a36Sopenharmony_ci *filtered_scan = true; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (user_scan_in->scan_chan_gap) { 94762306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 94862306a36Sopenharmony_ci "info: scan: channel gap = %d\n", 94962306a36Sopenharmony_ci user_scan_in->scan_chan_gap); 95062306a36Sopenharmony_ci *max_chan_per_scan = 95162306a36Sopenharmony_ci MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci chan_gap_tlv = (void *)tlv_pos; 95462306a36Sopenharmony_ci chan_gap_tlv->header.type = 95562306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); 95662306a36Sopenharmony_ci chan_gap_tlv->header.len = 95762306a36Sopenharmony_ci cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); 95862306a36Sopenharmony_ci chan_gap_tlv->chan_gap = 95962306a36Sopenharmony_ci cpu_to_le16((user_scan_in->scan_chan_gap)); 96062306a36Sopenharmony_ci tlv_pos += 96162306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_scan_chan_gap); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (!is_zero_ether_addr(user_scan_in->random_mac)) { 96562306a36Sopenharmony_ci random_mac_tlv = (void *)tlv_pos; 96662306a36Sopenharmony_ci random_mac_tlv->header.type = 96762306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_RANDOM_MAC); 96862306a36Sopenharmony_ci random_mac_tlv->header.len = 96962306a36Sopenharmony_ci cpu_to_le16(sizeof(random_mac_tlv->mac)); 97062306a36Sopenharmony_ci ether_addr_copy(random_mac_tlv->mac, 97162306a36Sopenharmony_ci user_scan_in->random_mac); 97262306a36Sopenharmony_ci tlv_pos += 97362306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_random_mac); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci } else { 97662306a36Sopenharmony_ci scan_cfg_out->bss_mode = (u8) adapter->scan_mode; 97762306a36Sopenharmony_ci num_probes = adapter->scan_probes; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* 98162306a36Sopenharmony_ci * If a specific BSSID or SSID is used, the number of channels in the 98262306a36Sopenharmony_ci * scan command will be increased to the absolute maximum. 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ci if (*filtered_scan) { 98562306a36Sopenharmony_ci *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; 98662306a36Sopenharmony_ci } else { 98762306a36Sopenharmony_ci if (!priv->media_connected) 98862306a36Sopenharmony_ci *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; 98962306a36Sopenharmony_ci else 99062306a36Sopenharmony_ci *max_chan_per_scan = 99162306a36Sopenharmony_ci MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD / 2; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (adapter->ext_scan) { 99562306a36Sopenharmony_ci bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos; 99662306a36Sopenharmony_ci bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE); 99762306a36Sopenharmony_ci bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode)); 99862306a36Sopenharmony_ci bss_mode->bss_mode = scan_cfg_out->bss_mode; 99962306a36Sopenharmony_ci tlv_pos += sizeof(bss_mode->header) + 100062306a36Sopenharmony_ci le16_to_cpu(bss_mode->header.len); 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* If the input config or adapter has the number of Probes set, 100462306a36Sopenharmony_ci add tlv */ 100562306a36Sopenharmony_ci if (num_probes) { 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 100862306a36Sopenharmony_ci "info: scan: num_probes = %d\n", 100962306a36Sopenharmony_ci num_probes); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; 101262306a36Sopenharmony_ci num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); 101362306a36Sopenharmony_ci num_probes_tlv->header.len = 101462306a36Sopenharmony_ci cpu_to_le16(sizeof(num_probes_tlv->num_probes)); 101562306a36Sopenharmony_ci num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci tlv_pos += sizeof(num_probes_tlv->header) + 101862306a36Sopenharmony_ci le16_to_cpu(num_probes_tlv->header.len); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && 102362306a36Sopenharmony_ci (priv->adapter->config_bands & BAND_GN || 102462306a36Sopenharmony_ci priv->adapter->config_bands & BAND_AN)) { 102562306a36Sopenharmony_ci ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; 102662306a36Sopenharmony_ci memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); 102762306a36Sopenharmony_ci ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); 102862306a36Sopenharmony_ci ht_cap->header.len = 102962306a36Sopenharmony_ci cpu_to_le16(sizeof(struct ieee80211_ht_cap)); 103062306a36Sopenharmony_ci radio_type = 103162306a36Sopenharmony_ci mwifiex_band_to_radio_type(priv->adapter->config_bands); 103262306a36Sopenharmony_ci mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); 103362306a36Sopenharmony_ci tlv_pos += sizeof(struct mwifiex_ie_types_htcap); 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* Append vendor specific IE TLV */ 103762306a36Sopenharmony_ci mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci /* 104062306a36Sopenharmony_ci * Set the output for the channel TLV to the address in the tlv buffer 104162306a36Sopenharmony_ci * past any TLVs that were added in this function (SSID, num_probes). 104262306a36Sopenharmony_ci * Channel TLVs will be added past this for each scan command, 104362306a36Sopenharmony_ci * preserving the TLVs that were previously added. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci *chan_list_out = 104662306a36Sopenharmony_ci (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (user_scan_in && user_scan_in->chan_list[0].chan_number) { 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 105162306a36Sopenharmony_ci "info: Scan: Using supplied channel list\n"); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci for (chan_idx = 0; 105462306a36Sopenharmony_ci chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX && 105562306a36Sopenharmony_ci user_scan_in->chan_list[chan_idx].chan_number; 105662306a36Sopenharmony_ci chan_idx++) { 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci channel = user_scan_in->chan_list[chan_idx].chan_number; 105962306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_number = channel; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci radio_type = 106262306a36Sopenharmony_ci user_scan_in->chan_list[chan_idx].radio_type; 106362306a36Sopenharmony_ci scan_chan_list[chan_idx].radio_type = radio_type; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci scan_type = user_scan_in->chan_list[chan_idx].scan_type; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) 106862306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 106962306a36Sopenharmony_ci |= (MWIFIEX_PASSIVE_SCAN | 107062306a36Sopenharmony_ci MWIFIEX_HIDDEN_SSID_REPORT); 107162306a36Sopenharmony_ci else 107262306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 107362306a36Sopenharmony_ci &= ~MWIFIEX_PASSIVE_SCAN; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci scan_chan_list[chan_idx].chan_scan_mode_bitmap 107662306a36Sopenharmony_ci |= MWIFIEX_DISABLE_CHAN_FILT; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (user_scan_in->chan_list[chan_idx].scan_time) { 107962306a36Sopenharmony_ci scan_dur = (u16) user_scan_in-> 108062306a36Sopenharmony_ci chan_list[chan_idx].scan_time; 108162306a36Sopenharmony_ci } else { 108262306a36Sopenharmony_ci if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) 108362306a36Sopenharmony_ci scan_dur = adapter->passive_scan_time; 108462306a36Sopenharmony_ci else if (*filtered_scan) 108562306a36Sopenharmony_ci scan_dur = adapter->specific_scan_time; 108662306a36Sopenharmony_ci else 108762306a36Sopenharmony_ci scan_dur = adapter->active_scan_time; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci scan_chan_list[chan_idx].min_scan_time = 109162306a36Sopenharmony_ci cpu_to_le16(scan_dur); 109262306a36Sopenharmony_ci scan_chan_list[chan_idx].max_scan_time = 109362306a36Sopenharmony_ci cpu_to_le16(scan_dur); 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* Check if we are only scanning the current channel */ 109762306a36Sopenharmony_ci if ((chan_idx == 1) && 109862306a36Sopenharmony_ci (user_scan_in->chan_list[0].chan_number == 109962306a36Sopenharmony_ci priv->curr_bss_params.bss_descriptor.channel)) { 110062306a36Sopenharmony_ci *scan_current_only = true; 110162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 110262306a36Sopenharmony_ci "info: Scan: Scanning current channel only\n"); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci } else { 110562306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 110662306a36Sopenharmony_ci "info: Scan: Creating full region channel list\n"); 110762306a36Sopenharmony_ci mwifiex_scan_create_channel_list(priv, user_scan_in, 110862306a36Sopenharmony_ci scan_chan_list, 110962306a36Sopenharmony_ci *filtered_scan); 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci/* 111562306a36Sopenharmony_ci * This function inspects the scan response buffer for pointers to 111662306a36Sopenharmony_ci * expected TLVs. 111762306a36Sopenharmony_ci * 111862306a36Sopenharmony_ci * TLVs can be included at the end of the scan response BSS information. 111962306a36Sopenharmony_ci * 112062306a36Sopenharmony_ci * Data in the buffer is parsed pointers to TLVs that can potentially 112162306a36Sopenharmony_ci * be passed back in the response. 112262306a36Sopenharmony_ci */ 112362306a36Sopenharmony_cistatic void 112462306a36Sopenharmony_cimwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, 112562306a36Sopenharmony_ci struct mwifiex_ie_types_data *tlv, 112662306a36Sopenharmony_ci u32 tlv_buf_size, u32 req_tlv_type, 112762306a36Sopenharmony_ci struct mwifiex_ie_types_data **tlv_data) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct mwifiex_ie_types_data *current_tlv; 113062306a36Sopenharmony_ci u32 tlv_buf_left; 113162306a36Sopenharmony_ci u32 tlv_type; 113262306a36Sopenharmony_ci u32 tlv_len; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci current_tlv = tlv; 113562306a36Sopenharmony_ci tlv_buf_left = tlv_buf_size; 113662306a36Sopenharmony_ci *tlv_data = NULL; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 113962306a36Sopenharmony_ci "info: SCAN_RESP: tlv_buf_size = %d\n", 114062306a36Sopenharmony_ci tlv_buf_size); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci tlv_type = le16_to_cpu(current_tlv->header.type); 114562306a36Sopenharmony_ci tlv_len = le16_to_cpu(current_tlv->header.len); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { 114862306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 114962306a36Sopenharmony_ci "SCAN_RESP: TLV buffer corrupt\n"); 115062306a36Sopenharmony_ci break; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (req_tlv_type == tlv_type) { 115462306a36Sopenharmony_ci switch (tlv_type) { 115562306a36Sopenharmony_ci case TLV_TYPE_TSFTIMESTAMP: 115662306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 115762306a36Sopenharmony_ci "info: SCAN_RESP: TSF\t" 115862306a36Sopenharmony_ci "timestamp TLV, len = %d\n", 115962306a36Sopenharmony_ci tlv_len); 116062306a36Sopenharmony_ci *tlv_data = current_tlv; 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci case TLV_TYPE_CHANNELBANDLIST: 116362306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 116462306a36Sopenharmony_ci "info: SCAN_RESP: channel\t" 116562306a36Sopenharmony_ci "band list TLV, len = %d\n", 116662306a36Sopenharmony_ci tlv_len); 116762306a36Sopenharmony_ci *tlv_data = current_tlv; 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci default: 117062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 117162306a36Sopenharmony_ci "SCAN_RESP: unhandled TLV = %d\n", 117262306a36Sopenharmony_ci tlv_type); 117362306a36Sopenharmony_ci /* Give up, this seems corrupted */ 117462306a36Sopenharmony_ci return; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (*tlv_data) 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci tlv_buf_left -= (sizeof(tlv->header) + tlv_len); 118362306a36Sopenharmony_ci current_tlv = 118462306a36Sopenharmony_ci (struct mwifiex_ie_types_data *) (current_tlv->data + 118562306a36Sopenharmony_ci tlv_len); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci } /* while */ 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/* 119162306a36Sopenharmony_ci * This function parses provided beacon buffer and updates 119262306a36Sopenharmony_ci * respective fields in bss descriptor structure. 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ciint mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, 119562306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_entry) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci u8 element_id; 119862306a36Sopenharmony_ci struct ieee_types_fh_param_set *fh_param_set; 119962306a36Sopenharmony_ci struct ieee_types_ds_param_set *ds_param_set; 120062306a36Sopenharmony_ci struct ieee_types_cf_param_set *cf_param_set; 120162306a36Sopenharmony_ci struct ieee_types_ibss_param_set *ibss_param_set; 120262306a36Sopenharmony_ci u8 *current_ptr; 120362306a36Sopenharmony_ci u8 *rate; 120462306a36Sopenharmony_ci u8 element_len; 120562306a36Sopenharmony_ci u16 total_ie_len; 120662306a36Sopenharmony_ci u8 bytes_to_copy; 120762306a36Sopenharmony_ci u8 rate_size; 120862306a36Sopenharmony_ci u8 found_data_rate_ie; 120962306a36Sopenharmony_ci u32 bytes_left; 121062306a36Sopenharmony_ci struct ieee_types_vendor_specific *vendor_ie; 121162306a36Sopenharmony_ci const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; 121262306a36Sopenharmony_ci const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci found_data_rate_ie = false; 121562306a36Sopenharmony_ci rate_size = 0; 121662306a36Sopenharmony_ci current_ptr = bss_entry->beacon_buf; 121762306a36Sopenharmony_ci bytes_left = bss_entry->beacon_buf_size; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Process variable IE */ 122062306a36Sopenharmony_ci while (bytes_left >= 2) { 122162306a36Sopenharmony_ci element_id = *current_ptr; 122262306a36Sopenharmony_ci element_len = *(current_ptr + 1); 122362306a36Sopenharmony_ci total_ie_len = element_len + sizeof(struct ieee_types_header); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (bytes_left < total_ie_len) { 122662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 122762306a36Sopenharmony_ci "err: InterpretIE: in processing\t" 122862306a36Sopenharmony_ci "IE, bytes left < IE length\n"); 122962306a36Sopenharmony_ci return -EINVAL; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci switch (element_id) { 123262306a36Sopenharmony_ci case WLAN_EID_SSID: 123362306a36Sopenharmony_ci if (element_len > IEEE80211_MAX_SSID_LEN) 123462306a36Sopenharmony_ci return -EINVAL; 123562306a36Sopenharmony_ci bss_entry->ssid.ssid_len = element_len; 123662306a36Sopenharmony_ci memcpy(bss_entry->ssid.ssid, (current_ptr + 2), 123762306a36Sopenharmony_ci element_len); 123862306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 123962306a36Sopenharmony_ci "info: InterpretIE: ssid: %-32s\n", 124062306a36Sopenharmony_ci bss_entry->ssid.ssid); 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci case WLAN_EID_SUPP_RATES: 124462306a36Sopenharmony_ci if (element_len > MWIFIEX_SUPPORTED_RATES) 124562306a36Sopenharmony_ci return -EINVAL; 124662306a36Sopenharmony_ci memcpy(bss_entry->data_rates, current_ptr + 2, 124762306a36Sopenharmony_ci element_len); 124862306a36Sopenharmony_ci memcpy(bss_entry->supported_rates, current_ptr + 2, 124962306a36Sopenharmony_ci element_len); 125062306a36Sopenharmony_ci rate_size = element_len; 125162306a36Sopenharmony_ci found_data_rate_ie = true; 125262306a36Sopenharmony_ci break; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci case WLAN_EID_FH_PARAMS: 125562306a36Sopenharmony_ci if (total_ie_len < sizeof(*fh_param_set)) 125662306a36Sopenharmony_ci return -EINVAL; 125762306a36Sopenharmony_ci fh_param_set = 125862306a36Sopenharmony_ci (struct ieee_types_fh_param_set *) current_ptr; 125962306a36Sopenharmony_ci memcpy(&bss_entry->phy_param_set.fh_param_set, 126062306a36Sopenharmony_ci fh_param_set, 126162306a36Sopenharmony_ci sizeof(struct ieee_types_fh_param_set)); 126262306a36Sopenharmony_ci break; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci case WLAN_EID_DS_PARAMS: 126562306a36Sopenharmony_ci if (total_ie_len < sizeof(*ds_param_set)) 126662306a36Sopenharmony_ci return -EINVAL; 126762306a36Sopenharmony_ci ds_param_set = 126862306a36Sopenharmony_ci (struct ieee_types_ds_param_set *) current_ptr; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci bss_entry->channel = ds_param_set->current_chan; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci memcpy(&bss_entry->phy_param_set.ds_param_set, 127362306a36Sopenharmony_ci ds_param_set, 127462306a36Sopenharmony_ci sizeof(struct ieee_types_ds_param_set)); 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci case WLAN_EID_CF_PARAMS: 127862306a36Sopenharmony_ci if (total_ie_len < sizeof(*cf_param_set)) 127962306a36Sopenharmony_ci return -EINVAL; 128062306a36Sopenharmony_ci cf_param_set = 128162306a36Sopenharmony_ci (struct ieee_types_cf_param_set *) current_ptr; 128262306a36Sopenharmony_ci memcpy(&bss_entry->ss_param_set.cf_param_set, 128362306a36Sopenharmony_ci cf_param_set, 128462306a36Sopenharmony_ci sizeof(struct ieee_types_cf_param_set)); 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci case WLAN_EID_IBSS_PARAMS: 128862306a36Sopenharmony_ci if (total_ie_len < sizeof(*ibss_param_set)) 128962306a36Sopenharmony_ci return -EINVAL; 129062306a36Sopenharmony_ci ibss_param_set = 129162306a36Sopenharmony_ci (struct ieee_types_ibss_param_set *) 129262306a36Sopenharmony_ci current_ptr; 129362306a36Sopenharmony_ci memcpy(&bss_entry->ss_param_set.ibss_param_set, 129462306a36Sopenharmony_ci ibss_param_set, 129562306a36Sopenharmony_ci sizeof(struct ieee_types_ibss_param_set)); 129662306a36Sopenharmony_ci break; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci case WLAN_EID_ERP_INFO: 129962306a36Sopenharmony_ci if (!element_len) 130062306a36Sopenharmony_ci return -EINVAL; 130162306a36Sopenharmony_ci bss_entry->erp_flags = *(current_ptr + 2); 130262306a36Sopenharmony_ci break; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci case WLAN_EID_PWR_CONSTRAINT: 130562306a36Sopenharmony_ci if (!element_len) 130662306a36Sopenharmony_ci return -EINVAL; 130762306a36Sopenharmony_ci bss_entry->local_constraint = *(current_ptr + 2); 130862306a36Sopenharmony_ci bss_entry->sensed_11h = true; 130962306a36Sopenharmony_ci break; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH: 131262306a36Sopenharmony_ci bss_entry->chan_sw_ie_present = true; 131362306a36Sopenharmony_ci fallthrough; 131462306a36Sopenharmony_ci case WLAN_EID_PWR_CAPABILITY: 131562306a36Sopenharmony_ci case WLAN_EID_TPC_REPORT: 131662306a36Sopenharmony_ci case WLAN_EID_QUIET: 131762306a36Sopenharmony_ci bss_entry->sensed_11h = true; 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci case WLAN_EID_EXT_SUPP_RATES: 132162306a36Sopenharmony_ci /* 132262306a36Sopenharmony_ci * Only process extended supported rate 132362306a36Sopenharmony_ci * if data rate is already found. 132462306a36Sopenharmony_ci * Data rate IE should come before 132562306a36Sopenharmony_ci * extended supported rate IE 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci if (found_data_rate_ie) { 132862306a36Sopenharmony_ci if ((element_len + rate_size) > 132962306a36Sopenharmony_ci MWIFIEX_SUPPORTED_RATES) 133062306a36Sopenharmony_ci bytes_to_copy = 133162306a36Sopenharmony_ci (MWIFIEX_SUPPORTED_RATES - 133262306a36Sopenharmony_ci rate_size); 133362306a36Sopenharmony_ci else 133462306a36Sopenharmony_ci bytes_to_copy = element_len; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci rate = (u8 *) bss_entry->data_rates; 133762306a36Sopenharmony_ci rate += rate_size; 133862306a36Sopenharmony_ci memcpy(rate, current_ptr + 2, bytes_to_copy); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci rate = (u8 *) bss_entry->supported_rates; 134162306a36Sopenharmony_ci rate += rate_size; 134262306a36Sopenharmony_ci memcpy(rate, current_ptr + 2, bytes_to_copy); 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 134762306a36Sopenharmony_ci vendor_ie = (struct ieee_types_vendor_specific *) 134862306a36Sopenharmony_ci current_ptr; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci /* 802.11 requires at least 3-byte OUI. */ 135162306a36Sopenharmony_ci if (element_len < sizeof(vendor_ie->vend_hdr.oui.oui)) 135262306a36Sopenharmony_ci return -EINVAL; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* Not long enough for a match? Skip it. */ 135562306a36Sopenharmony_ci if (element_len < sizeof(wpa_oui)) 135662306a36Sopenharmony_ci break; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (!memcmp(&vendor_ie->vend_hdr.oui, wpa_oui, 135962306a36Sopenharmony_ci sizeof(wpa_oui))) { 136062306a36Sopenharmony_ci bss_entry->bcn_wpa_ie = 136162306a36Sopenharmony_ci (struct ieee_types_vendor_specific *) 136262306a36Sopenharmony_ci current_ptr; 136362306a36Sopenharmony_ci bss_entry->wpa_offset = (u16) 136462306a36Sopenharmony_ci (current_ptr - bss_entry->beacon_buf); 136562306a36Sopenharmony_ci } else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui, 136662306a36Sopenharmony_ci sizeof(wmm_oui))) { 136762306a36Sopenharmony_ci if (total_ie_len == 136862306a36Sopenharmony_ci sizeof(struct ieee_types_wmm_parameter) || 136962306a36Sopenharmony_ci total_ie_len == 137062306a36Sopenharmony_ci sizeof(struct ieee_types_wmm_info)) 137162306a36Sopenharmony_ci /* 137262306a36Sopenharmony_ci * Only accept and copy the WMM IE if 137362306a36Sopenharmony_ci * it matches the size expected for the 137462306a36Sopenharmony_ci * WMM Info IE or the WMM Parameter IE. 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_ci memcpy((u8 *) &bss_entry->wmm_ie, 137762306a36Sopenharmony_ci current_ptr, total_ie_len); 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci break; 138062306a36Sopenharmony_ci case WLAN_EID_RSN: 138162306a36Sopenharmony_ci bss_entry->bcn_rsn_ie = 138262306a36Sopenharmony_ci (struct ieee_types_generic *) current_ptr; 138362306a36Sopenharmony_ci bss_entry->rsn_offset = (u16) (current_ptr - 138462306a36Sopenharmony_ci bss_entry->beacon_buf); 138562306a36Sopenharmony_ci break; 138662306a36Sopenharmony_ci case WLAN_EID_BSS_AC_ACCESS_DELAY: 138762306a36Sopenharmony_ci bss_entry->bcn_wapi_ie = 138862306a36Sopenharmony_ci (struct ieee_types_generic *) current_ptr; 138962306a36Sopenharmony_ci bss_entry->wapi_offset = (u16) (current_ptr - 139062306a36Sopenharmony_ci bss_entry->beacon_buf); 139162306a36Sopenharmony_ci break; 139262306a36Sopenharmony_ci case WLAN_EID_HT_CAPABILITY: 139362306a36Sopenharmony_ci bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) 139462306a36Sopenharmony_ci (current_ptr + 139562306a36Sopenharmony_ci sizeof(struct ieee_types_header)); 139662306a36Sopenharmony_ci bss_entry->ht_cap_offset = (u16) (current_ptr + 139762306a36Sopenharmony_ci sizeof(struct ieee_types_header) - 139862306a36Sopenharmony_ci bss_entry->beacon_buf); 139962306a36Sopenharmony_ci break; 140062306a36Sopenharmony_ci case WLAN_EID_HT_OPERATION: 140162306a36Sopenharmony_ci bss_entry->bcn_ht_oper = 140262306a36Sopenharmony_ci (struct ieee80211_ht_operation *)(current_ptr + 140362306a36Sopenharmony_ci sizeof(struct ieee_types_header)); 140462306a36Sopenharmony_ci bss_entry->ht_info_offset = (u16) (current_ptr + 140562306a36Sopenharmony_ci sizeof(struct ieee_types_header) - 140662306a36Sopenharmony_ci bss_entry->beacon_buf); 140762306a36Sopenharmony_ci break; 140862306a36Sopenharmony_ci case WLAN_EID_VHT_CAPABILITY: 140962306a36Sopenharmony_ci bss_entry->disable_11ac = false; 141062306a36Sopenharmony_ci bss_entry->bcn_vht_cap = 141162306a36Sopenharmony_ci (void *)(current_ptr + 141262306a36Sopenharmony_ci sizeof(struct ieee_types_header)); 141362306a36Sopenharmony_ci bss_entry->vht_cap_offset = 141462306a36Sopenharmony_ci (u16)((u8 *)bss_entry->bcn_vht_cap - 141562306a36Sopenharmony_ci bss_entry->beacon_buf); 141662306a36Sopenharmony_ci break; 141762306a36Sopenharmony_ci case WLAN_EID_VHT_OPERATION: 141862306a36Sopenharmony_ci bss_entry->bcn_vht_oper = 141962306a36Sopenharmony_ci (void *)(current_ptr + 142062306a36Sopenharmony_ci sizeof(struct ieee_types_header)); 142162306a36Sopenharmony_ci bss_entry->vht_info_offset = 142262306a36Sopenharmony_ci (u16)((u8 *)bss_entry->bcn_vht_oper - 142362306a36Sopenharmony_ci bss_entry->beacon_buf); 142462306a36Sopenharmony_ci break; 142562306a36Sopenharmony_ci case WLAN_EID_BSS_COEX_2040: 142662306a36Sopenharmony_ci bss_entry->bcn_bss_co_2040 = current_ptr; 142762306a36Sopenharmony_ci bss_entry->bss_co_2040_offset = 142862306a36Sopenharmony_ci (u16) (current_ptr - bss_entry->beacon_buf); 142962306a36Sopenharmony_ci break; 143062306a36Sopenharmony_ci case WLAN_EID_EXT_CAPABILITY: 143162306a36Sopenharmony_ci bss_entry->bcn_ext_cap = current_ptr; 143262306a36Sopenharmony_ci bss_entry->ext_cap_offset = 143362306a36Sopenharmony_ci (u16) (current_ptr - bss_entry->beacon_buf); 143462306a36Sopenharmony_ci break; 143562306a36Sopenharmony_ci case WLAN_EID_OPMODE_NOTIF: 143662306a36Sopenharmony_ci bss_entry->oper_mode = (void *)current_ptr; 143762306a36Sopenharmony_ci bss_entry->oper_mode_offset = 143862306a36Sopenharmony_ci (u16)((u8 *)bss_entry->oper_mode - 143962306a36Sopenharmony_ci bss_entry->beacon_buf); 144062306a36Sopenharmony_ci break; 144162306a36Sopenharmony_ci default: 144262306a36Sopenharmony_ci break; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci current_ptr += total_ie_len; 144662306a36Sopenharmony_ci bytes_left -= total_ie_len; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci } /* while (bytes_left > 2) */ 144962306a36Sopenharmony_ci return 0; 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci/* 145362306a36Sopenharmony_ci * This function converts radio type scan parameter to a band configuration 145462306a36Sopenharmony_ci * to be used in join command. 145562306a36Sopenharmony_ci */ 145662306a36Sopenharmony_cistatic u8 145762306a36Sopenharmony_cimwifiex_radio_type_to_band(u8 radio_type) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci switch (radio_type) { 146062306a36Sopenharmony_ci case HostCmd_SCAN_RADIO_TYPE_A: 146162306a36Sopenharmony_ci return BAND_A; 146262306a36Sopenharmony_ci case HostCmd_SCAN_RADIO_TYPE_BG: 146362306a36Sopenharmony_ci default: 146462306a36Sopenharmony_ci return BAND_G; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci/* 146962306a36Sopenharmony_ci * This is an internal function used to start a scan based on an input 147062306a36Sopenharmony_ci * configuration. 147162306a36Sopenharmony_ci * 147262306a36Sopenharmony_ci * This uses the input user scan configuration information when provided in 147362306a36Sopenharmony_ci * order to send the appropriate scan commands to firmware to populate or 147462306a36Sopenharmony_ci * update the internal driver scan table. 147562306a36Sopenharmony_ci */ 147662306a36Sopenharmony_ciint mwifiex_scan_networks(struct mwifiex_private *priv, 147762306a36Sopenharmony_ci const struct mwifiex_user_scan_cfg *user_scan_in) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci int ret; 148062306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 148162306a36Sopenharmony_ci struct cmd_ctrl_node *cmd_node; 148262306a36Sopenharmony_ci union mwifiex_scan_cmd_config_tlv *scan_cfg_out; 148362306a36Sopenharmony_ci struct mwifiex_ie_types_chan_list_param_set *chan_list_out; 148462306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set *scan_chan_list; 148562306a36Sopenharmony_ci u8 filtered_scan; 148662306a36Sopenharmony_ci u8 scan_current_chan_only; 148762306a36Sopenharmony_ci u8 max_chan_per_scan; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci if (adapter->scan_processing) { 149062306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 149162306a36Sopenharmony_ci "cmd: Scan already in process...\n"); 149262306a36Sopenharmony_ci return -EBUSY; 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if (priv->scan_block) { 149662306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 149762306a36Sopenharmony_ci "cmd: Scan is blocked during association...\n"); 149862306a36Sopenharmony_ci return -EBUSY; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags) || 150262306a36Sopenharmony_ci test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags)) { 150362306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 150462306a36Sopenharmony_ci "Ignore scan. Card removed or firmware in bad state\n"); 150562306a36Sopenharmony_ci return -EFAULT; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci spin_lock_bh(&adapter->mwifiex_cmd_lock); 150962306a36Sopenharmony_ci adapter->scan_processing = true; 151062306a36Sopenharmony_ci spin_unlock_bh(&adapter->mwifiex_cmd_lock); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), 151362306a36Sopenharmony_ci GFP_KERNEL); 151462306a36Sopenharmony_ci if (!scan_cfg_out) { 151562306a36Sopenharmony_ci ret = -ENOMEM; 151662306a36Sopenharmony_ci goto done; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX, 152062306a36Sopenharmony_ci sizeof(struct mwifiex_chan_scan_param_set), 152162306a36Sopenharmony_ci GFP_KERNEL); 152262306a36Sopenharmony_ci if (!scan_chan_list) { 152362306a36Sopenharmony_ci kfree(scan_cfg_out); 152462306a36Sopenharmony_ci ret = -ENOMEM; 152562306a36Sopenharmony_ci goto done; 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, 152962306a36Sopenharmony_ci &chan_list_out, scan_chan_list, &max_chan_per_scan, 153062306a36Sopenharmony_ci &filtered_scan, &scan_current_chan_only); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, 153362306a36Sopenharmony_ci &scan_cfg_out->config, chan_list_out, 153462306a36Sopenharmony_ci scan_chan_list); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci /* Get scan command from scan_pending_q and put to cmd_pending_q */ 153762306a36Sopenharmony_ci if (!ret) { 153862306a36Sopenharmony_ci spin_lock_bh(&adapter->scan_pending_q_lock); 153962306a36Sopenharmony_ci if (!list_empty(&adapter->scan_pending_q)) { 154062306a36Sopenharmony_ci cmd_node = list_first_entry(&adapter->scan_pending_q, 154162306a36Sopenharmony_ci struct cmd_ctrl_node, list); 154262306a36Sopenharmony_ci list_del(&cmd_node->list); 154362306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 154462306a36Sopenharmony_ci mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); 154562306a36Sopenharmony_ci queue_work(adapter->workqueue, &adapter->main_work); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* Perform internal scan synchronously */ 154862306a36Sopenharmony_ci if (!priv->scan_request) { 154962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 155062306a36Sopenharmony_ci "wait internal scan\n"); 155162306a36Sopenharmony_ci mwifiex_wait_queue_complete(adapter, cmd_node); 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci } else { 155462306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci kfree(scan_cfg_out); 155962306a36Sopenharmony_ci kfree(scan_chan_list); 156062306a36Sopenharmony_cidone: 156162306a36Sopenharmony_ci if (ret) { 156262306a36Sopenharmony_ci spin_lock_bh(&adapter->mwifiex_cmd_lock); 156362306a36Sopenharmony_ci adapter->scan_processing = false; 156462306a36Sopenharmony_ci spin_unlock_bh(&adapter->mwifiex_cmd_lock); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci return ret; 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci/* 157062306a36Sopenharmony_ci * This function prepares a scan command to be sent to the firmware. 157162306a36Sopenharmony_ci * 157262306a36Sopenharmony_ci * This uses the scan command configuration sent to the command processing 157362306a36Sopenharmony_ci * module in command preparation stage to configure a scan command structure 157462306a36Sopenharmony_ci * to send to firmware. 157562306a36Sopenharmony_ci * 157662306a36Sopenharmony_ci * The fixed fields specifying the BSS type and BSSID filters as well as a 157762306a36Sopenharmony_ci * variable number/length of TLVs are sent in the command to firmware. 157862306a36Sopenharmony_ci * 157962306a36Sopenharmony_ci * Preparation also includes - 158062306a36Sopenharmony_ci * - Setting command ID, and proper size 158162306a36Sopenharmony_ci * - Ensuring correct endian-ness 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_ciint mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, 158462306a36Sopenharmony_ci struct mwifiex_scan_cmd_config *scan_cfg) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci /* Set fixed field variables in scan command */ 158962306a36Sopenharmony_ci scan_cmd->bss_mode = scan_cfg->bss_mode; 159062306a36Sopenharmony_ci memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, 159162306a36Sopenharmony_ci sizeof(scan_cmd->bssid)); 159262306a36Sopenharmony_ci memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ 159762306a36Sopenharmony_ci cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) 159862306a36Sopenharmony_ci + sizeof(scan_cmd->bssid) 159962306a36Sopenharmony_ci + scan_cfg->tlv_buf_len + S_DS_GEN)); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci return 0; 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci/* 160562306a36Sopenharmony_ci * This function checks compatibility of requested network with current 160662306a36Sopenharmony_ci * driver settings. 160762306a36Sopenharmony_ci */ 160862306a36Sopenharmony_ciint mwifiex_check_network_compatibility(struct mwifiex_private *priv, 160962306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc) 161062306a36Sopenharmony_ci{ 161162306a36Sopenharmony_ci int ret = -1; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci if (!bss_desc) 161462306a36Sopenharmony_ci return -1; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band, 161762306a36Sopenharmony_ci (u16) bss_desc->channel, 0))) { 161862306a36Sopenharmony_ci switch (priv->bss_mode) { 161962306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 162062306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 162162306a36Sopenharmony_ci ret = mwifiex_is_network_compatible(priv, bss_desc, 162262306a36Sopenharmony_ci priv->bss_mode); 162362306a36Sopenharmony_ci if (ret) 162462306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 162562306a36Sopenharmony_ci "Incompatible network settings\n"); 162662306a36Sopenharmony_ci break; 162762306a36Sopenharmony_ci default: 162862306a36Sopenharmony_ci ret = 0; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci return ret; 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci/* This function checks if SSID string contains all zeroes or length is zero */ 163662306a36Sopenharmony_cistatic bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci int idx; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci for (idx = 0; idx < ssid->ssid_len; idx++) { 164162306a36Sopenharmony_ci if (ssid->ssid[idx]) 164262306a36Sopenharmony_ci return false; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return true; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci/* This function checks if any hidden SSID found in passive scan channels 164962306a36Sopenharmony_ci * and save those channels for specific SSID active scan 165062306a36Sopenharmony_ci */ 165162306a36Sopenharmony_cistatic int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv, 165262306a36Sopenharmony_ci struct cfg80211_bss *bss) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc; 165562306a36Sopenharmony_ci int ret; 165662306a36Sopenharmony_ci int chid; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* Allocate and fill new bss descriptor */ 165962306a36Sopenharmony_ci bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); 166062306a36Sopenharmony_ci if (!bss_desc) 166162306a36Sopenharmony_ci return -ENOMEM; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); 166462306a36Sopenharmony_ci if (ret) 166562306a36Sopenharmony_ci goto done; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) { 166862306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n"); 166962306a36Sopenharmony_ci for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) { 167062306a36Sopenharmony_ci if (priv->hidden_chan[chid].chan_number == 167162306a36Sopenharmony_ci bss->channel->hw_value) 167262306a36Sopenharmony_ci break; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (!priv->hidden_chan[chid].chan_number) { 167562306a36Sopenharmony_ci priv->hidden_chan[chid].chan_number = 167662306a36Sopenharmony_ci bss->channel->hw_value; 167762306a36Sopenharmony_ci priv->hidden_chan[chid].radio_type = 167862306a36Sopenharmony_ci bss->channel->band; 167962306a36Sopenharmony_ci priv->hidden_chan[chid].scan_type = 168062306a36Sopenharmony_ci MWIFIEX_SCAN_TYPE_ACTIVE; 168162306a36Sopenharmony_ci break; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_cidone: 168762306a36Sopenharmony_ci /* beacon_ie buffer was allocated in function 168862306a36Sopenharmony_ci * mwifiex_fill_new_bss_desc(). Free it now. 168962306a36Sopenharmony_ci */ 169062306a36Sopenharmony_ci kfree(bss_desc->beacon_buf); 169162306a36Sopenharmony_ci kfree(bss_desc); 169262306a36Sopenharmony_ci return 0; 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, 169662306a36Sopenharmony_ci struct cfg80211_bss *bss) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci struct mwifiex_bssdescriptor *bss_desc; 169962306a36Sopenharmony_ci int ret; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* Allocate and fill new bss descriptor */ 170262306a36Sopenharmony_ci bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); 170362306a36Sopenharmony_ci if (!bss_desc) 170462306a36Sopenharmony_ci return -ENOMEM; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); 170762306a36Sopenharmony_ci if (ret) 170862306a36Sopenharmony_ci goto done; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci ret = mwifiex_check_network_compatibility(priv, bss_desc); 171162306a36Sopenharmony_ci if (ret) 171262306a36Sopenharmony_ci goto done; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci spin_lock_bh(&priv->curr_bcn_buf_lock); 171562306a36Sopenharmony_ci /* Make a copy of current BSSID descriptor */ 171662306a36Sopenharmony_ci memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, 171762306a36Sopenharmony_ci sizeof(priv->curr_bss_params.bss_descriptor)); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci /* The contents of beacon_ie will be copied to its own buffer 172062306a36Sopenharmony_ci * in mwifiex_save_curr_bcn() 172162306a36Sopenharmony_ci */ 172262306a36Sopenharmony_ci mwifiex_save_curr_bcn(priv); 172362306a36Sopenharmony_ci spin_unlock_bh(&priv->curr_bcn_buf_lock); 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cidone: 172662306a36Sopenharmony_ci /* beacon_ie buffer was allocated in function 172762306a36Sopenharmony_ci * mwifiex_fill_new_bss_desc(). Free it now. 172862306a36Sopenharmony_ci */ 172962306a36Sopenharmony_ci kfree(bss_desc->beacon_buf); 173062306a36Sopenharmony_ci kfree(bss_desc); 173162306a36Sopenharmony_ci return 0; 173262306a36Sopenharmony_ci} 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_cistatic int 173562306a36Sopenharmony_cimwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, 173662306a36Sopenharmony_ci u32 *bytes_left, u64 fw_tsf, u8 *radio_type, 173762306a36Sopenharmony_ci bool ext_scan, s32 rssi_val) 173862306a36Sopenharmony_ci{ 173962306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 174062306a36Sopenharmony_ci struct mwifiex_chan_freq_power *cfp; 174162306a36Sopenharmony_ci struct cfg80211_bss *bss; 174262306a36Sopenharmony_ci u8 bssid[ETH_ALEN]; 174362306a36Sopenharmony_ci s32 rssi; 174462306a36Sopenharmony_ci const u8 *ie_buf; 174562306a36Sopenharmony_ci size_t ie_len; 174662306a36Sopenharmony_ci u16 channel = 0; 174762306a36Sopenharmony_ci u16 beacon_size = 0; 174862306a36Sopenharmony_ci u32 curr_bcn_bytes; 174962306a36Sopenharmony_ci u32 freq; 175062306a36Sopenharmony_ci u16 beacon_period; 175162306a36Sopenharmony_ci u16 cap_info_bitmap; 175262306a36Sopenharmony_ci u8 *current_ptr; 175362306a36Sopenharmony_ci u64 timestamp; 175462306a36Sopenharmony_ci struct mwifiex_fixed_bcn_param *bcn_param; 175562306a36Sopenharmony_ci struct mwifiex_bss_priv *bss_priv; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (*bytes_left >= sizeof(beacon_size)) { 175862306a36Sopenharmony_ci /* Extract & convert beacon size from command buffer */ 175962306a36Sopenharmony_ci beacon_size = get_unaligned_le16((*bss_info)); 176062306a36Sopenharmony_ci *bytes_left -= sizeof(beacon_size); 176162306a36Sopenharmony_ci *bss_info += sizeof(beacon_size); 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (!beacon_size || beacon_size > *bytes_left) { 176562306a36Sopenharmony_ci *bss_info += *bytes_left; 176662306a36Sopenharmony_ci *bytes_left = 0; 176762306a36Sopenharmony_ci return -EFAULT; 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci /* Initialize the current working beacon pointer for this BSS 177162306a36Sopenharmony_ci * iteration 177262306a36Sopenharmony_ci */ 177362306a36Sopenharmony_ci current_ptr = *bss_info; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci /* Advance the return beacon pointer past the current beacon */ 177662306a36Sopenharmony_ci *bss_info += beacon_size; 177762306a36Sopenharmony_ci *bytes_left -= beacon_size; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci curr_bcn_bytes = beacon_size; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* First 5 fields are bssid, RSSI(for legacy scan only), 178262306a36Sopenharmony_ci * time stamp, beacon interval, and capability information 178362306a36Sopenharmony_ci */ 178462306a36Sopenharmony_ci if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + 178562306a36Sopenharmony_ci sizeof(struct mwifiex_fixed_bcn_param)) { 178662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 178762306a36Sopenharmony_ci "InterpretIE: not enough bytes left\n"); 178862306a36Sopenharmony_ci return -EFAULT; 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci memcpy(bssid, current_ptr, ETH_ALEN); 179262306a36Sopenharmony_ci current_ptr += ETH_ALEN; 179362306a36Sopenharmony_ci curr_bcn_bytes -= ETH_ALEN; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (!ext_scan) { 179662306a36Sopenharmony_ci rssi = (s32) *current_ptr; 179762306a36Sopenharmony_ci rssi = (-rssi) * 100; /* Convert dBm to mBm */ 179862306a36Sopenharmony_ci current_ptr += sizeof(u8); 179962306a36Sopenharmony_ci curr_bcn_bytes -= sizeof(u8); 180062306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 180162306a36Sopenharmony_ci "info: InterpretIE: RSSI=%d\n", rssi); 180262306a36Sopenharmony_ci } else { 180362306a36Sopenharmony_ci rssi = rssi_val; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; 180762306a36Sopenharmony_ci current_ptr += sizeof(*bcn_param); 180862306a36Sopenharmony_ci curr_bcn_bytes -= sizeof(*bcn_param); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci timestamp = le64_to_cpu(bcn_param->timestamp); 181162306a36Sopenharmony_ci beacon_period = le16_to_cpu(bcn_param->beacon_period); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); 181462306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 181562306a36Sopenharmony_ci "info: InterpretIE: capabilities=0x%X\n", 181662306a36Sopenharmony_ci cap_info_bitmap); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci /* Rest of the current buffer are IE's */ 181962306a36Sopenharmony_ci ie_buf = current_ptr; 182062306a36Sopenharmony_ci ie_len = curr_bcn_bytes; 182162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 182262306a36Sopenharmony_ci "info: InterpretIE: IELength for this AP = %d\n", 182362306a36Sopenharmony_ci curr_bcn_bytes); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { 182662306a36Sopenharmony_ci u8 element_id, element_len; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci element_id = *current_ptr; 182962306a36Sopenharmony_ci element_len = *(current_ptr + 1); 183062306a36Sopenharmony_ci if (curr_bcn_bytes < element_len + 183162306a36Sopenharmony_ci sizeof(struct ieee_types_header)) { 183262306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 183362306a36Sopenharmony_ci "%s: bytes left < IE length\n", __func__); 183462306a36Sopenharmony_ci return -EFAULT; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci if (element_id == WLAN_EID_DS_PARAMS) { 183762306a36Sopenharmony_ci channel = *(current_ptr + 183862306a36Sopenharmony_ci sizeof(struct ieee_types_header)); 183962306a36Sopenharmony_ci break; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci current_ptr += element_len + sizeof(struct ieee_types_header); 184362306a36Sopenharmony_ci curr_bcn_bytes -= element_len + 184462306a36Sopenharmony_ci sizeof(struct ieee_types_header); 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (channel) { 184862306a36Sopenharmony_ci struct ieee80211_channel *chan; 184962306a36Sopenharmony_ci u8 band; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* Skip entry if on csa closed channel */ 185262306a36Sopenharmony_ci if (channel == priv->csa_chan) { 185362306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 185462306a36Sopenharmony_ci "Dropping entry on csa closed channel\n"); 185562306a36Sopenharmony_ci return 0; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci band = BAND_G; 185962306a36Sopenharmony_ci if (radio_type) 186062306a36Sopenharmony_ci band = mwifiex_radio_type_to_band(*radio_type & 186162306a36Sopenharmony_ci (BIT(0) | BIT(1))); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci cfp = mwifiex_get_cfp(priv, band, channel, 0); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci freq = cfp ? cfp->freq : 0; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci chan = ieee80211_get_channel(priv->wdev.wiphy, freq); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { 187062306a36Sopenharmony_ci bss = cfg80211_inform_bss(priv->wdev.wiphy, 187162306a36Sopenharmony_ci chan, CFG80211_BSS_FTYPE_UNKNOWN, 187262306a36Sopenharmony_ci bssid, timestamp, 187362306a36Sopenharmony_ci cap_info_bitmap, beacon_period, 187462306a36Sopenharmony_ci ie_buf, ie_len, rssi, GFP_ATOMIC); 187562306a36Sopenharmony_ci if (bss) { 187662306a36Sopenharmony_ci bss_priv = (struct mwifiex_bss_priv *)bss->priv; 187762306a36Sopenharmony_ci bss_priv->band = band; 187862306a36Sopenharmony_ci bss_priv->fw_tsf = fw_tsf; 187962306a36Sopenharmony_ci if (priv->media_connected && 188062306a36Sopenharmony_ci !memcmp(bssid, priv->curr_bss_params. 188162306a36Sopenharmony_ci bss_descriptor.mac_address, 188262306a36Sopenharmony_ci ETH_ALEN)) 188362306a36Sopenharmony_ci mwifiex_update_curr_bss_params(priv, 188462306a36Sopenharmony_ci bss); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci if ((chan->flags & IEEE80211_CHAN_RADAR) || 188762306a36Sopenharmony_ci (chan->flags & IEEE80211_CHAN_NO_IR)) { 188862306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 188962306a36Sopenharmony_ci "radar or passive channel %d\n", 189062306a36Sopenharmony_ci channel); 189162306a36Sopenharmony_ci mwifiex_save_hidden_ssid_channels(priv, 189262306a36Sopenharmony_ci bss); 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci cfg80211_put_bss(priv->wdev.wiphy, bss); 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci } else { 189962306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n"); 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci return 0; 190362306a36Sopenharmony_ci} 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_cistatic void mwifiex_complete_scan(struct mwifiex_private *priv) 190662306a36Sopenharmony_ci{ 190762306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci adapter->survey_idx = 0; 191062306a36Sopenharmony_ci if (adapter->curr_cmd->wait_q_enabled) { 191162306a36Sopenharmony_ci adapter->cmd_wait_q.status = 0; 191262306a36Sopenharmony_ci if (!priv->scan_request) { 191362306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 191462306a36Sopenharmony_ci "complete internal scan\n"); 191562306a36Sopenharmony_ci mwifiex_complete_cmd(adapter, adapter->curr_cmd); 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci} 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci/* This function checks if any hidden SSID found in passive scan channels 192162306a36Sopenharmony_ci * and do specific SSID active scan for those channels 192262306a36Sopenharmony_ci */ 192362306a36Sopenharmony_cistatic int 192462306a36Sopenharmony_cimwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) 192562306a36Sopenharmony_ci{ 192662306a36Sopenharmony_ci int ret; 192762306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 192862306a36Sopenharmony_ci u8 id = 0; 192962306a36Sopenharmony_ci struct mwifiex_user_scan_cfg *user_scan_cfg; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci if (adapter->active_scan_triggered || !priv->scan_request || 193262306a36Sopenharmony_ci priv->scan_aborting) { 193362306a36Sopenharmony_ci adapter->active_scan_triggered = false; 193462306a36Sopenharmony_ci return 0; 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci if (!priv->hidden_chan[0].chan_number) { 193862306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); 193962306a36Sopenharmony_ci return 0; 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci if (!user_scan_cfg) 194462306a36Sopenharmony_ci return -ENOMEM; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) { 194762306a36Sopenharmony_ci if (!priv->hidden_chan[id].chan_number) 194862306a36Sopenharmony_ci break; 194962306a36Sopenharmony_ci memcpy(&user_scan_cfg->chan_list[id], 195062306a36Sopenharmony_ci &priv->hidden_chan[id], 195162306a36Sopenharmony_ci sizeof(struct mwifiex_user_scan_chan)); 195262306a36Sopenharmony_ci } 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci adapter->active_scan_triggered = true; 195562306a36Sopenharmony_ci if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) 195662306a36Sopenharmony_ci ether_addr_copy(user_scan_cfg->random_mac, 195762306a36Sopenharmony_ci priv->scan_request->mac_addr); 195862306a36Sopenharmony_ci user_scan_cfg->num_ssids = priv->scan_request->n_ssids; 195962306a36Sopenharmony_ci user_scan_cfg->ssid_list = priv->scan_request->ssids; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci ret = mwifiex_scan_networks(priv, user_scan_cfg); 196262306a36Sopenharmony_ci kfree(user_scan_cfg); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci if (ret) { 196762306a36Sopenharmony_ci dev_err(priv->adapter->dev, "scan failed: %d\n", ret); 196862306a36Sopenharmony_ci return ret; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci return 0; 197262306a36Sopenharmony_ci} 197362306a36Sopenharmony_cistatic void mwifiex_check_next_scan_command(struct mwifiex_private *priv) 197462306a36Sopenharmony_ci{ 197562306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 197662306a36Sopenharmony_ci struct cmd_ctrl_node *cmd_node; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci spin_lock_bh(&adapter->scan_pending_q_lock); 197962306a36Sopenharmony_ci if (list_empty(&adapter->scan_pending_q)) { 198062306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci spin_lock_bh(&adapter->mwifiex_cmd_lock); 198362306a36Sopenharmony_ci adapter->scan_processing = false; 198462306a36Sopenharmony_ci spin_unlock_bh(&adapter->mwifiex_cmd_lock); 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci mwifiex_active_scan_req_for_passive_chan(priv); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (!adapter->ext_scan) 198962306a36Sopenharmony_ci mwifiex_complete_scan(priv); 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci if (priv->scan_request) { 199262306a36Sopenharmony_ci struct cfg80211_scan_info info = { 199362306a36Sopenharmony_ci .aborted = false, 199462306a36Sopenharmony_ci }; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 199762306a36Sopenharmony_ci "info: notifying scan done\n"); 199862306a36Sopenharmony_ci cfg80211_scan_done(priv->scan_request, &info); 199962306a36Sopenharmony_ci priv->scan_request = NULL; 200062306a36Sopenharmony_ci priv->scan_aborting = false; 200162306a36Sopenharmony_ci } else { 200262306a36Sopenharmony_ci priv->scan_aborting = false; 200362306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 200462306a36Sopenharmony_ci "info: scan already aborted\n"); 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci } else if ((priv->scan_aborting && !priv->scan_request) || 200762306a36Sopenharmony_ci priv->scan_block) { 200862306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci mwifiex_cancel_pending_scan_cmd(adapter); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci spin_lock_bh(&adapter->mwifiex_cmd_lock); 201362306a36Sopenharmony_ci adapter->scan_processing = false; 201462306a36Sopenharmony_ci spin_unlock_bh(&adapter->mwifiex_cmd_lock); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (!adapter->active_scan_triggered) { 201762306a36Sopenharmony_ci if (priv->scan_request) { 201862306a36Sopenharmony_ci struct cfg80211_scan_info info = { 201962306a36Sopenharmony_ci .aborted = true, 202062306a36Sopenharmony_ci }; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 202362306a36Sopenharmony_ci "info: aborting scan\n"); 202462306a36Sopenharmony_ci cfg80211_scan_done(priv->scan_request, &info); 202562306a36Sopenharmony_ci priv->scan_request = NULL; 202662306a36Sopenharmony_ci priv->scan_aborting = false; 202762306a36Sopenharmony_ci } else { 202862306a36Sopenharmony_ci priv->scan_aborting = false; 202962306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 203062306a36Sopenharmony_ci "info: scan already aborted\n"); 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci } else { 203462306a36Sopenharmony_ci /* Get scan command from scan_pending_q and put to 203562306a36Sopenharmony_ci * cmd_pending_q 203662306a36Sopenharmony_ci */ 203762306a36Sopenharmony_ci cmd_node = list_first_entry(&adapter->scan_pending_q, 203862306a36Sopenharmony_ci struct cmd_ctrl_node, list); 203962306a36Sopenharmony_ci list_del(&cmd_node->list); 204062306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 204162306a36Sopenharmony_ci mwifiex_insert_cmd_to_pending_q(adapter, cmd_node); 204262306a36Sopenharmony_ci } 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci return; 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_civoid mwifiex_cancel_scan(struct mwifiex_adapter *adapter) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci struct mwifiex_private *priv; 205062306a36Sopenharmony_ci int i; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci mwifiex_cancel_pending_scan_cmd(adapter); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if (adapter->scan_processing) { 205562306a36Sopenharmony_ci spin_lock_bh(&adapter->mwifiex_cmd_lock); 205662306a36Sopenharmony_ci adapter->scan_processing = false; 205762306a36Sopenharmony_ci spin_unlock_bh(&adapter->mwifiex_cmd_lock); 205862306a36Sopenharmony_ci for (i = 0; i < adapter->priv_num; i++) { 205962306a36Sopenharmony_ci priv = adapter->priv[i]; 206062306a36Sopenharmony_ci if (!priv) 206162306a36Sopenharmony_ci continue; 206262306a36Sopenharmony_ci if (priv->scan_request) { 206362306a36Sopenharmony_ci struct cfg80211_scan_info info = { 206462306a36Sopenharmony_ci .aborted = true, 206562306a36Sopenharmony_ci }; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 206862306a36Sopenharmony_ci "info: aborting scan\n"); 206962306a36Sopenharmony_ci cfg80211_scan_done(priv->scan_request, &info); 207062306a36Sopenharmony_ci priv->scan_request = NULL; 207162306a36Sopenharmony_ci priv->scan_aborting = false; 207262306a36Sopenharmony_ci } 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci} 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci/* 207862306a36Sopenharmony_ci * This function handles the command response of scan. 207962306a36Sopenharmony_ci * 208062306a36Sopenharmony_ci * The response buffer for the scan command has the following 208162306a36Sopenharmony_ci * memory layout: 208262306a36Sopenharmony_ci * 208362306a36Sopenharmony_ci * .-------------------------------------------------------------. 208462306a36Sopenharmony_ci * | Header (4 * sizeof(t_u16)): Standard command response hdr | 208562306a36Sopenharmony_ci * .-------------------------------------------------------------. 208662306a36Sopenharmony_ci * | BufSize (t_u16) : sizeof the BSS Description data | 208762306a36Sopenharmony_ci * .-------------------------------------------------------------. 208862306a36Sopenharmony_ci * | NumOfSet (t_u8) : Number of BSS Descs returned | 208962306a36Sopenharmony_ci * .-------------------------------------------------------------. 209062306a36Sopenharmony_ci * | BSSDescription data (variable, size given in BufSize) | 209162306a36Sopenharmony_ci * .-------------------------------------------------------------. 209262306a36Sopenharmony_ci * | TLV data (variable, size calculated using Header->Size, | 209362306a36Sopenharmony_ci * | BufSize and sizeof the fixed fields above) | 209462306a36Sopenharmony_ci * .-------------------------------------------------------------. 209562306a36Sopenharmony_ci */ 209662306a36Sopenharmony_ciint mwifiex_ret_802_11_scan(struct mwifiex_private *priv, 209762306a36Sopenharmony_ci struct host_cmd_ds_command *resp) 209862306a36Sopenharmony_ci{ 209962306a36Sopenharmony_ci int ret = 0; 210062306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 210162306a36Sopenharmony_ci struct host_cmd_ds_802_11_scan_rsp *scan_rsp; 210262306a36Sopenharmony_ci struct mwifiex_ie_types_data *tlv_data; 210362306a36Sopenharmony_ci struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; 210462306a36Sopenharmony_ci u8 *bss_info; 210562306a36Sopenharmony_ci u32 scan_resp_size; 210662306a36Sopenharmony_ci u32 bytes_left; 210762306a36Sopenharmony_ci u32 idx; 210862306a36Sopenharmony_ci u32 tlv_buf_size; 210962306a36Sopenharmony_ci struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; 211062306a36Sopenharmony_ci struct chan_band_param_set *chan_band; 211162306a36Sopenharmony_ci u8 is_bgscan_resp; 211262306a36Sopenharmony_ci __le64 fw_tsf = 0; 211362306a36Sopenharmony_ci u8 *radio_type; 211462306a36Sopenharmony_ci struct cfg80211_wowlan_nd_match *pmatch; 211562306a36Sopenharmony_ci struct cfg80211_sched_scan_request *nd_config = NULL; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci is_bgscan_resp = (le16_to_cpu(resp->command) 211862306a36Sopenharmony_ci == HostCmd_CMD_802_11_BG_SCAN_QUERY); 211962306a36Sopenharmony_ci if (is_bgscan_resp) 212062306a36Sopenharmony_ci scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; 212162306a36Sopenharmony_ci else 212262306a36Sopenharmony_ci scan_rsp = &resp->params.scan_resp; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) { 212662306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 212762306a36Sopenharmony_ci "SCAN_RESP: too many AP returned (%d)\n", 212862306a36Sopenharmony_ci scan_rsp->number_of_sets); 212962306a36Sopenharmony_ci ret = -1; 213062306a36Sopenharmony_ci goto check_next_scan; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci /* Check csa channel expiry before parsing scan response */ 213462306a36Sopenharmony_ci mwifiex_11h_get_csa_closed_channel(priv); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); 213762306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 213862306a36Sopenharmony_ci "info: SCAN_RESP: bss_descript_size %d\n", 213962306a36Sopenharmony_ci bytes_left); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci scan_resp_size = le16_to_cpu(resp->size); 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 214462306a36Sopenharmony_ci "info: SCAN_RESP: returned %d APs before parsing\n", 214562306a36Sopenharmony_ci scan_rsp->number_of_sets); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci bss_info = scan_rsp->bss_desc_and_tlv_buffer; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci /* 215062306a36Sopenharmony_ci * The size of the TLV buffer is equal to the entire command response 215162306a36Sopenharmony_ci * size (scan_resp_size) minus the fixed fields (sizeof()'s), the 215262306a36Sopenharmony_ci * BSS Descriptions (bss_descript_size as bytesLef) and the command 215362306a36Sopenharmony_ci * response header (S_DS_GEN) 215462306a36Sopenharmony_ci */ 215562306a36Sopenharmony_ci tlv_buf_size = scan_resp_size - (bytes_left 215662306a36Sopenharmony_ci + sizeof(scan_rsp->bss_descript_size) 215762306a36Sopenharmony_ci + sizeof(scan_rsp->number_of_sets) 215862306a36Sopenharmony_ci + S_DS_GEN); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> 216162306a36Sopenharmony_ci bss_desc_and_tlv_buffer + 216262306a36Sopenharmony_ci bytes_left); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* Search the TLV buffer space in the scan response for any valid 216562306a36Sopenharmony_ci TLVs */ 216662306a36Sopenharmony_ci mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, 216762306a36Sopenharmony_ci TLV_TYPE_TSFTIMESTAMP, 216862306a36Sopenharmony_ci (struct mwifiex_ie_types_data **) 216962306a36Sopenharmony_ci &tsf_tlv); 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci /* Search the TLV buffer space in the scan response for any valid 217262306a36Sopenharmony_ci TLVs */ 217362306a36Sopenharmony_ci mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, 217462306a36Sopenharmony_ci TLV_TYPE_CHANNELBANDLIST, 217562306a36Sopenharmony_ci (struct mwifiex_ie_types_data **) 217662306a36Sopenharmony_ci &chan_band_tlv); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci#ifdef CONFIG_PM 217962306a36Sopenharmony_ci if (priv->wdev.wiphy->wowlan_config) 218062306a36Sopenharmony_ci nd_config = priv->wdev.wiphy->wowlan_config->nd_config; 218162306a36Sopenharmony_ci#endif 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci if (nd_config) { 218462306a36Sopenharmony_ci adapter->nd_info = 218562306a36Sopenharmony_ci kzalloc(struct_size(adapter->nd_info, matches, 218662306a36Sopenharmony_ci scan_rsp->number_of_sets), 218762306a36Sopenharmony_ci GFP_ATOMIC); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci if (adapter->nd_info) 219062306a36Sopenharmony_ci adapter->nd_info->n_matches = scan_rsp->number_of_sets; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { 219462306a36Sopenharmony_ci /* 219562306a36Sopenharmony_ci * If the TSF TLV was appended to the scan results, save this 219662306a36Sopenharmony_ci * entry's TSF value in the fw_tsf field. It is the firmware's 219762306a36Sopenharmony_ci * TSF value at the time the beacon or probe response was 219862306a36Sopenharmony_ci * received. 219962306a36Sopenharmony_ci */ 220062306a36Sopenharmony_ci if (tsf_tlv) 220162306a36Sopenharmony_ci memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], 220262306a36Sopenharmony_ci sizeof(fw_tsf)); 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci if (chan_band_tlv) { 220562306a36Sopenharmony_ci chan_band = &chan_band_tlv->chan_band_param[idx]; 220662306a36Sopenharmony_ci radio_type = &chan_band->radio_type; 220762306a36Sopenharmony_ci } else { 220862306a36Sopenharmony_ci radio_type = NULL; 220962306a36Sopenharmony_ci } 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci if (chan_band_tlv && adapter->nd_info) { 221262306a36Sopenharmony_ci adapter->nd_info->matches[idx] = 221362306a36Sopenharmony_ci kzalloc(sizeof(*pmatch) + sizeof(u32), 221462306a36Sopenharmony_ci GFP_ATOMIC); 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci pmatch = adapter->nd_info->matches[idx]; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci if (pmatch) { 221962306a36Sopenharmony_ci pmatch->n_channels = 1; 222062306a36Sopenharmony_ci pmatch->channels[0] = chan_band->chan_number; 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci } 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci ret = mwifiex_parse_single_response_buf(priv, &bss_info, 222562306a36Sopenharmony_ci &bytes_left, 222662306a36Sopenharmony_ci le64_to_cpu(fw_tsf), 222762306a36Sopenharmony_ci radio_type, false, 0); 222862306a36Sopenharmony_ci if (ret) 222962306a36Sopenharmony_ci goto check_next_scan; 223062306a36Sopenharmony_ci } 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_cicheck_next_scan: 223362306a36Sopenharmony_ci mwifiex_check_next_scan_command(priv); 223462306a36Sopenharmony_ci return ret; 223562306a36Sopenharmony_ci} 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci/* 223862306a36Sopenharmony_ci * This function prepares an extended scan command to be sent to the firmware 223962306a36Sopenharmony_ci * 224062306a36Sopenharmony_ci * This uses the scan command configuration sent to the command processing 224162306a36Sopenharmony_ci * module in command preparation stage to configure a extended scan command 224262306a36Sopenharmony_ci * structure to send to firmware. 224362306a36Sopenharmony_ci */ 224462306a36Sopenharmony_ciint mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, 224562306a36Sopenharmony_ci struct host_cmd_ds_command *cmd, 224662306a36Sopenharmony_ci void *data_buf) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; 224962306a36Sopenharmony_ci struct mwifiex_scan_cmd_config *scan_cfg = data_buf; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ 225662306a36Sopenharmony_ci cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) 225762306a36Sopenharmony_ci + scan_cfg->tlv_buf_len + S_DS_GEN)); 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci return 0; 226062306a36Sopenharmony_ci} 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci/* This function prepares an background scan config command to be sent 226362306a36Sopenharmony_ci * to the firmware 226462306a36Sopenharmony_ci */ 226562306a36Sopenharmony_ciint mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, 226662306a36Sopenharmony_ci struct host_cmd_ds_command *cmd, 226762306a36Sopenharmony_ci void *data_buf) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct host_cmd_ds_802_11_bg_scan_config *bgscan_config = 227062306a36Sopenharmony_ci &cmd->params.bg_scan_config; 227162306a36Sopenharmony_ci struct mwifiex_bg_scan_cfg *bgscan_cfg_in = data_buf; 227262306a36Sopenharmony_ci u8 *tlv_pos = bgscan_config->tlv; 227362306a36Sopenharmony_ci u8 num_probes; 227462306a36Sopenharmony_ci u32 ssid_len, chan_idx, scan_type, scan_dur, chan_num; 227562306a36Sopenharmony_ci int i; 227662306a36Sopenharmony_ci struct mwifiex_ie_types_num_probes *num_probes_tlv; 227762306a36Sopenharmony_ci struct mwifiex_ie_types_repeat_count *repeat_count_tlv; 227862306a36Sopenharmony_ci struct mwifiex_ie_types_min_rssi_threshold *rssi_threshold_tlv; 227962306a36Sopenharmony_ci struct mwifiex_ie_types_bgscan_start_later *start_later_tlv; 228062306a36Sopenharmony_ci struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; 228162306a36Sopenharmony_ci struct mwifiex_ie_types_chan_list_param_set *chan_list_tlv; 228262306a36Sopenharmony_ci struct mwifiex_chan_scan_param_set *temp_chan; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG); 228562306a36Sopenharmony_ci cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action); 228862306a36Sopenharmony_ci bgscan_config->enable = bgscan_cfg_in->enable; 228962306a36Sopenharmony_ci bgscan_config->bss_type = bgscan_cfg_in->bss_type; 229062306a36Sopenharmony_ci bgscan_config->scan_interval = 229162306a36Sopenharmony_ci cpu_to_le32(bgscan_cfg_in->scan_interval); 229262306a36Sopenharmony_ci bgscan_config->report_condition = 229362306a36Sopenharmony_ci cpu_to_le32(bgscan_cfg_in->report_condition); 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci /* stop sched scan */ 229662306a36Sopenharmony_ci if (!bgscan_config->enable) 229762306a36Sopenharmony_ci return 0; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci num_probes = (bgscan_cfg_in->num_probes ? bgscan_cfg_in-> 230262306a36Sopenharmony_ci num_probes : priv->adapter->scan_probes); 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci if (num_probes) { 230562306a36Sopenharmony_ci num_probes_tlv = (struct mwifiex_ie_types_num_probes *)tlv_pos; 230662306a36Sopenharmony_ci num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); 230762306a36Sopenharmony_ci num_probes_tlv->header.len = 230862306a36Sopenharmony_ci cpu_to_le16(sizeof(num_probes_tlv->num_probes)); 230962306a36Sopenharmony_ci num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci tlv_pos += sizeof(num_probes_tlv->header) + 231262306a36Sopenharmony_ci le16_to_cpu(num_probes_tlv->header.len); 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci if (bgscan_cfg_in->repeat_count) { 231662306a36Sopenharmony_ci repeat_count_tlv = 231762306a36Sopenharmony_ci (struct mwifiex_ie_types_repeat_count *)tlv_pos; 231862306a36Sopenharmony_ci repeat_count_tlv->header.type = 231962306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_REPEAT_COUNT); 232062306a36Sopenharmony_ci repeat_count_tlv->header.len = 232162306a36Sopenharmony_ci cpu_to_le16(sizeof(repeat_count_tlv->repeat_count)); 232262306a36Sopenharmony_ci repeat_count_tlv->repeat_count = 232362306a36Sopenharmony_ci cpu_to_le16(bgscan_cfg_in->repeat_count); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci tlv_pos += sizeof(repeat_count_tlv->header) + 232662306a36Sopenharmony_ci le16_to_cpu(repeat_count_tlv->header.len); 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci if (bgscan_cfg_in->rssi_threshold) { 233062306a36Sopenharmony_ci rssi_threshold_tlv = 233162306a36Sopenharmony_ci (struct mwifiex_ie_types_min_rssi_threshold *)tlv_pos; 233262306a36Sopenharmony_ci rssi_threshold_tlv->header.type = 233362306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_RSSI_LOW); 233462306a36Sopenharmony_ci rssi_threshold_tlv->header.len = 233562306a36Sopenharmony_ci cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold)); 233662306a36Sopenharmony_ci rssi_threshold_tlv->rssi_threshold = 233762306a36Sopenharmony_ci cpu_to_le16(bgscan_cfg_in->rssi_threshold); 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci tlv_pos += sizeof(rssi_threshold_tlv->header) + 234062306a36Sopenharmony_ci le16_to_cpu(rssi_threshold_tlv->header.len); 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci for (i = 0; i < bgscan_cfg_in->num_ssids; i++) { 234462306a36Sopenharmony_ci ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci wildcard_ssid_tlv = 234762306a36Sopenharmony_ci (struct mwifiex_ie_types_wildcard_ssid_params *)tlv_pos; 234862306a36Sopenharmony_ci wildcard_ssid_tlv->header.type = 234962306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_WILDCARDSSID); 235062306a36Sopenharmony_ci wildcard_ssid_tlv->header.len = cpu_to_le16( 235162306a36Sopenharmony_ci (u16)(ssid_len + sizeof(wildcard_ssid_tlv-> 235262306a36Sopenharmony_ci max_ssid_length))); 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci /* max_ssid_length = 0 tells firmware to perform 235562306a36Sopenharmony_ci * specific scan for the SSID filled, whereas 235662306a36Sopenharmony_ci * max_ssid_length = IEEE80211_MAX_SSID_LEN is for 235762306a36Sopenharmony_ci * wildcard scan. 235862306a36Sopenharmony_ci */ 235962306a36Sopenharmony_ci if (ssid_len) 236062306a36Sopenharmony_ci wildcard_ssid_tlv->max_ssid_length = 0; 236162306a36Sopenharmony_ci else 236262306a36Sopenharmony_ci wildcard_ssid_tlv->max_ssid_length = 236362306a36Sopenharmony_ci IEEE80211_MAX_SSID_LEN; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci memcpy(wildcard_ssid_tlv->ssid, 236662306a36Sopenharmony_ci bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len); 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci tlv_pos += (sizeof(wildcard_ssid_tlv->header) 236962306a36Sopenharmony_ci + le16_to_cpu(wildcard_ssid_tlv->header.len)); 237062306a36Sopenharmony_ci } 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci chan_list_tlv = (struct mwifiex_ie_types_chan_list_param_set *)tlv_pos; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci if (bgscan_cfg_in->chan_list[0].chan_number) { 237562306a36Sopenharmony_ci dev_dbg(priv->adapter->dev, "info: bgscan: Using supplied channel list\n"); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci chan_list_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci for (chan_idx = 0; 238062306a36Sopenharmony_ci chan_idx < MWIFIEX_BG_SCAN_CHAN_MAX && 238162306a36Sopenharmony_ci bgscan_cfg_in->chan_list[chan_idx].chan_number; 238262306a36Sopenharmony_ci chan_idx++) { 238362306a36Sopenharmony_ci temp_chan = chan_list_tlv->chan_scan_param + chan_idx; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci /* Increment the TLV header length by size appended */ 238662306a36Sopenharmony_ci le16_unaligned_add_cpu(&chan_list_tlv->header.len, 238762306a36Sopenharmony_ci sizeof( 238862306a36Sopenharmony_ci chan_list_tlv->chan_scan_param)); 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci temp_chan->chan_number = 239162306a36Sopenharmony_ci bgscan_cfg_in->chan_list[chan_idx].chan_number; 239262306a36Sopenharmony_ci temp_chan->radio_type = 239362306a36Sopenharmony_ci bgscan_cfg_in->chan_list[chan_idx].radio_type; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci scan_type = 239662306a36Sopenharmony_ci bgscan_cfg_in->chan_list[chan_idx].scan_type; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) 239962306a36Sopenharmony_ci temp_chan->chan_scan_mode_bitmap 240062306a36Sopenharmony_ci |= MWIFIEX_PASSIVE_SCAN; 240162306a36Sopenharmony_ci else 240262306a36Sopenharmony_ci temp_chan->chan_scan_mode_bitmap 240362306a36Sopenharmony_ci &= ~MWIFIEX_PASSIVE_SCAN; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci if (bgscan_cfg_in->chan_list[chan_idx].scan_time) { 240662306a36Sopenharmony_ci scan_dur = (u16)bgscan_cfg_in-> 240762306a36Sopenharmony_ci chan_list[chan_idx].scan_time; 240862306a36Sopenharmony_ci } else { 240962306a36Sopenharmony_ci scan_dur = (scan_type == 241062306a36Sopenharmony_ci MWIFIEX_SCAN_TYPE_PASSIVE) ? 241162306a36Sopenharmony_ci priv->adapter->passive_scan_time : 241262306a36Sopenharmony_ci priv->adapter->specific_scan_time; 241362306a36Sopenharmony_ci } 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci temp_chan->min_scan_time = cpu_to_le16(scan_dur); 241662306a36Sopenharmony_ci temp_chan->max_scan_time = cpu_to_le16(scan_dur); 241762306a36Sopenharmony_ci } 241862306a36Sopenharmony_ci } else { 241962306a36Sopenharmony_ci dev_dbg(priv->adapter->dev, 242062306a36Sopenharmony_ci "info: bgscan: Creating full region channel list\n"); 242162306a36Sopenharmony_ci chan_num = 242262306a36Sopenharmony_ci mwifiex_bgscan_create_channel_list(priv, bgscan_cfg_in, 242362306a36Sopenharmony_ci chan_list_tlv-> 242462306a36Sopenharmony_ci chan_scan_param); 242562306a36Sopenharmony_ci le16_unaligned_add_cpu(&chan_list_tlv->header.len, 242662306a36Sopenharmony_ci chan_num * 242762306a36Sopenharmony_ci sizeof(chan_list_tlv->chan_scan_param[0])); 242862306a36Sopenharmony_ci } 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci tlv_pos += (sizeof(chan_list_tlv->header) 243162306a36Sopenharmony_ci + le16_to_cpu(chan_list_tlv->header.len)); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci if (bgscan_cfg_in->start_later) { 243462306a36Sopenharmony_ci start_later_tlv = 243562306a36Sopenharmony_ci (struct mwifiex_ie_types_bgscan_start_later *)tlv_pos; 243662306a36Sopenharmony_ci start_later_tlv->header.type = 243762306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER); 243862306a36Sopenharmony_ci start_later_tlv->header.len = 243962306a36Sopenharmony_ci cpu_to_le16(sizeof(start_later_tlv->start_later)); 244062306a36Sopenharmony_ci start_later_tlv->start_later = 244162306a36Sopenharmony_ci cpu_to_le16(bgscan_cfg_in->start_later); 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci tlv_pos += sizeof(start_later_tlv->header) + 244462306a36Sopenharmony_ci le16_to_cpu(start_later_tlv->header.len); 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci /* Append vendor specific IE TLV */ 244862306a36Sopenharmony_ci mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_BGSCAN, &tlv_pos); 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv); 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci return 0; 245362306a36Sopenharmony_ci} 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ciint mwifiex_stop_bg_scan(struct mwifiex_private *priv) 245662306a36Sopenharmony_ci{ 245762306a36Sopenharmony_ci struct mwifiex_bg_scan_cfg *bgscan_cfg; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci if (!priv->sched_scanning) { 246062306a36Sopenharmony_ci dev_dbg(priv->adapter->dev, "bgscan already stopped!\n"); 246162306a36Sopenharmony_ci return 0; 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); 246562306a36Sopenharmony_ci if (!bgscan_cfg) 246662306a36Sopenharmony_ci return -ENOMEM; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci bgscan_cfg->bss_type = MWIFIEX_BSS_MODE_INFRA; 246962306a36Sopenharmony_ci bgscan_cfg->action = MWIFIEX_BGSCAN_ACT_SET; 247062306a36Sopenharmony_ci bgscan_cfg->enable = false; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_CONFIG, 247362306a36Sopenharmony_ci HostCmd_ACT_GEN_SET, 0, bgscan_cfg, true)) { 247462306a36Sopenharmony_ci kfree(bgscan_cfg); 247562306a36Sopenharmony_ci return -EFAULT; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci kfree(bgscan_cfg); 247962306a36Sopenharmony_ci priv->sched_scanning = false; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci return 0; 248262306a36Sopenharmony_ci} 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_cistatic void 248562306a36Sopenharmony_cimwifiex_update_chan_statistics(struct mwifiex_private *priv, 248662306a36Sopenharmony_ci struct mwifiex_ietypes_chanstats *tlv_stat) 248762306a36Sopenharmony_ci{ 248862306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 248962306a36Sopenharmony_ci u8 i, num_chan; 249062306a36Sopenharmony_ci struct mwifiex_fw_chan_stats *fw_chan_stats; 249162306a36Sopenharmony_ci struct mwifiex_chan_stats chan_stats; 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci fw_chan_stats = (void *)((u8 *)tlv_stat + 249462306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header)); 249562306a36Sopenharmony_ci num_chan = le16_to_cpu(tlv_stat->header.len) / 249662306a36Sopenharmony_ci sizeof(struct mwifiex_chan_stats); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci for (i = 0 ; i < num_chan; i++) { 249962306a36Sopenharmony_ci if (adapter->survey_idx >= adapter->num_in_chan_stats) { 250062306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 250162306a36Sopenharmony_ci "FW reported too many channel results (max %d)\n", 250262306a36Sopenharmony_ci adapter->num_in_chan_stats); 250362306a36Sopenharmony_ci return; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci chan_stats.chan_num = fw_chan_stats->chan_num; 250662306a36Sopenharmony_ci chan_stats.bandcfg = fw_chan_stats->bandcfg; 250762306a36Sopenharmony_ci chan_stats.flags = fw_chan_stats->flags; 250862306a36Sopenharmony_ci chan_stats.noise = fw_chan_stats->noise; 250962306a36Sopenharmony_ci chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss); 251062306a36Sopenharmony_ci chan_stats.cca_scan_dur = 251162306a36Sopenharmony_ci le16_to_cpu(fw_chan_stats->cca_scan_dur); 251262306a36Sopenharmony_ci chan_stats.cca_busy_dur = 251362306a36Sopenharmony_ci le16_to_cpu(fw_chan_stats->cca_busy_dur); 251462306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 251562306a36Sopenharmony_ci "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", 251662306a36Sopenharmony_ci chan_stats.chan_num, 251762306a36Sopenharmony_ci chan_stats.noise, 251862306a36Sopenharmony_ci chan_stats.total_bss, 251962306a36Sopenharmony_ci chan_stats.cca_scan_dur, 252062306a36Sopenharmony_ci chan_stats.cca_busy_dur); 252162306a36Sopenharmony_ci memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats, 252262306a36Sopenharmony_ci sizeof(struct mwifiex_chan_stats)); 252362306a36Sopenharmony_ci fw_chan_stats++; 252462306a36Sopenharmony_ci } 252562306a36Sopenharmony_ci} 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci/* This function handles the command response of extended scan */ 252862306a36Sopenharmony_ciint mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, 252962306a36Sopenharmony_ci struct host_cmd_ds_command *resp) 253062306a36Sopenharmony_ci{ 253162306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 253262306a36Sopenharmony_ci struct host_cmd_ds_802_11_scan_ext *ext_scan_resp; 253362306a36Sopenharmony_ci struct mwifiex_ie_types_header *tlv; 253462306a36Sopenharmony_ci struct mwifiex_ietypes_chanstats *tlv_stat; 253562306a36Sopenharmony_ci u16 buf_left, type, len; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci struct host_cmd_ds_command *cmd_ptr; 253862306a36Sopenharmony_ci struct cmd_ctrl_node *cmd_node; 253962306a36Sopenharmony_ci bool complete_scan = false; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci ext_scan_resp = &resp->params.ext_scan; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci tlv = (void *)ext_scan_resp->tlv_buffer; 254662306a36Sopenharmony_ci buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN 254762306a36Sopenharmony_ci - 1); 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci while (buf_left >= sizeof(struct mwifiex_ie_types_header)) { 255062306a36Sopenharmony_ci type = le16_to_cpu(tlv->type); 255162306a36Sopenharmony_ci len = le16_to_cpu(tlv->len); 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) { 255462306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 255562306a36Sopenharmony_ci "error processing scan response TLVs"); 255662306a36Sopenharmony_ci break; 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci switch (type) { 256062306a36Sopenharmony_ci case TLV_TYPE_CHANNEL_STATS: 256162306a36Sopenharmony_ci tlv_stat = (void *)tlv; 256262306a36Sopenharmony_ci mwifiex_update_chan_statistics(priv, tlv_stat); 256362306a36Sopenharmony_ci break; 256462306a36Sopenharmony_ci default: 256562306a36Sopenharmony_ci break; 256662306a36Sopenharmony_ci } 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci buf_left -= len + sizeof(struct mwifiex_ie_types_header); 256962306a36Sopenharmony_ci tlv = (void *)((u8 *)tlv + len + 257062306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header)); 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci spin_lock_bh(&adapter->cmd_pending_q_lock); 257462306a36Sopenharmony_ci spin_lock_bh(&adapter->scan_pending_q_lock); 257562306a36Sopenharmony_ci if (list_empty(&adapter->scan_pending_q)) { 257662306a36Sopenharmony_ci complete_scan = true; 257762306a36Sopenharmony_ci list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { 257862306a36Sopenharmony_ci cmd_ptr = (void *)cmd_node->cmd_skb->data; 257962306a36Sopenharmony_ci if (le16_to_cpu(cmd_ptr->command) == 258062306a36Sopenharmony_ci HostCmd_CMD_802_11_SCAN_EXT) { 258162306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 258262306a36Sopenharmony_ci "Scan pending in command pending list"); 258362306a36Sopenharmony_ci complete_scan = false; 258462306a36Sopenharmony_ci break; 258562306a36Sopenharmony_ci } 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 258962306a36Sopenharmony_ci spin_unlock_bh(&adapter->cmd_pending_q_lock); 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci if (complete_scan) 259262306a36Sopenharmony_ci mwifiex_complete_scan(priv); 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci return 0; 259562306a36Sopenharmony_ci} 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci/* This function This function handles the event extended scan report. It 259862306a36Sopenharmony_ci * parses extended scan results and informs to cfg80211 stack. 259962306a36Sopenharmony_ci */ 260062306a36Sopenharmony_ciint mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, 260162306a36Sopenharmony_ci void *buf) 260262306a36Sopenharmony_ci{ 260362306a36Sopenharmony_ci int ret = 0; 260462306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 260562306a36Sopenharmony_ci u8 *bss_info; 260662306a36Sopenharmony_ci u32 bytes_left, bytes_left_for_tlv, idx; 260762306a36Sopenharmony_ci u16 type, len; 260862306a36Sopenharmony_ci struct mwifiex_ie_types_data *tlv; 260962306a36Sopenharmony_ci struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; 261062306a36Sopenharmony_ci struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; 261162306a36Sopenharmony_ci u8 *radio_type; 261262306a36Sopenharmony_ci u64 fw_tsf = 0; 261362306a36Sopenharmony_ci s32 rssi = 0; 261462306a36Sopenharmony_ci struct mwifiex_event_scan_result *event_scan = buf; 261562306a36Sopenharmony_ci u8 num_of_set = event_scan->num_of_set; 261662306a36Sopenharmony_ci u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); 261762306a36Sopenharmony_ci u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci if (num_of_set > MWIFIEX_MAX_AP) { 262062306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 262162306a36Sopenharmony_ci "EXT_SCAN: Invalid number of AP returned (%d)!!\n", 262262306a36Sopenharmony_ci num_of_set); 262362306a36Sopenharmony_ci ret = -1; 262462306a36Sopenharmony_ci goto check_next_scan; 262562306a36Sopenharmony_ci } 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci bytes_left = scan_resp_size; 262862306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 262962306a36Sopenharmony_ci "EXT_SCAN: size %d, returned %d APs...", 263062306a36Sopenharmony_ci scan_resp_size, num_of_set); 263162306a36Sopenharmony_ci mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf, 263262306a36Sopenharmony_ci scan_resp_size + 263362306a36Sopenharmony_ci sizeof(struct mwifiex_event_scan_result)); 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci tlv = (struct mwifiex_ie_types_data *)scan_resp; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci for (idx = 0; idx < num_of_set && bytes_left; idx++) { 263862306a36Sopenharmony_ci type = le16_to_cpu(tlv->header.type); 263962306a36Sopenharmony_ci len = le16_to_cpu(tlv->header.len); 264062306a36Sopenharmony_ci if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { 264162306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 264262306a36Sopenharmony_ci "EXT_SCAN: Error bytes left < TLV length\n"); 264362306a36Sopenharmony_ci break; 264462306a36Sopenharmony_ci } 264562306a36Sopenharmony_ci scan_rsp_tlv = NULL; 264662306a36Sopenharmony_ci scan_info_tlv = NULL; 264762306a36Sopenharmony_ci bytes_left_for_tlv = bytes_left; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci /* BSS response TLV with beacon or probe response buffer 265062306a36Sopenharmony_ci * at the initial position of each descriptor 265162306a36Sopenharmony_ci */ 265262306a36Sopenharmony_ci if (type != TLV_TYPE_BSS_SCAN_RSP) 265362306a36Sopenharmony_ci break; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci bss_info = (u8 *)tlv; 265662306a36Sopenharmony_ci scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; 265762306a36Sopenharmony_ci tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); 265862306a36Sopenharmony_ci bytes_left_for_tlv -= 265962306a36Sopenharmony_ci (len + sizeof(struct mwifiex_ie_types_header)); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci while (bytes_left_for_tlv >= 266262306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header) && 266362306a36Sopenharmony_ci le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { 266462306a36Sopenharmony_ci type = le16_to_cpu(tlv->header.type); 266562306a36Sopenharmony_ci len = le16_to_cpu(tlv->header.len); 266662306a36Sopenharmony_ci if (bytes_left_for_tlv < 266762306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header) + len) { 266862306a36Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 266962306a36Sopenharmony_ci "EXT_SCAN: Error in processing TLV,\t" 267062306a36Sopenharmony_ci "bytes left < TLV length\n"); 267162306a36Sopenharmony_ci scan_rsp_tlv = NULL; 267262306a36Sopenharmony_ci bytes_left_for_tlv = 0; 267362306a36Sopenharmony_ci continue; 267462306a36Sopenharmony_ci } 267562306a36Sopenharmony_ci switch (type) { 267662306a36Sopenharmony_ci case TLV_TYPE_BSS_SCAN_INFO: 267762306a36Sopenharmony_ci scan_info_tlv = 267862306a36Sopenharmony_ci (struct mwifiex_ie_types_bss_scan_info *)tlv; 267962306a36Sopenharmony_ci if (len != 268062306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_bss_scan_info) - 268162306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header)) { 268262306a36Sopenharmony_ci bytes_left_for_tlv = 0; 268362306a36Sopenharmony_ci continue; 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci break; 268662306a36Sopenharmony_ci default: 268762306a36Sopenharmony_ci break; 268862306a36Sopenharmony_ci } 268962306a36Sopenharmony_ci tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); 269062306a36Sopenharmony_ci bytes_left -= 269162306a36Sopenharmony_ci (len + sizeof(struct mwifiex_ie_types_header)); 269262306a36Sopenharmony_ci bytes_left_for_tlv -= 269362306a36Sopenharmony_ci (len + sizeof(struct mwifiex_ie_types_header)); 269462306a36Sopenharmony_ci } 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci if (!scan_rsp_tlv) 269762306a36Sopenharmony_ci break; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci /* Advance pointer to the beacon buffer length and 270062306a36Sopenharmony_ci * update the bytes count so that the function 270162306a36Sopenharmony_ci * wlan_interpret_bss_desc_with_ie() can handle the 270262306a36Sopenharmony_ci * scan buffer withut any change 270362306a36Sopenharmony_ci */ 270462306a36Sopenharmony_ci bss_info += sizeof(u16); 270562306a36Sopenharmony_ci bytes_left -= sizeof(u16); 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci if (scan_info_tlv) { 270862306a36Sopenharmony_ci rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); 270962306a36Sopenharmony_ci rssi *= 100; /* Convert dBm to mBm */ 271062306a36Sopenharmony_ci mwifiex_dbg(adapter, INFO, 271162306a36Sopenharmony_ci "info: InterpretIE: RSSI=%d\n", rssi); 271262306a36Sopenharmony_ci fw_tsf = le64_to_cpu(scan_info_tlv->tsf); 271362306a36Sopenharmony_ci radio_type = &scan_info_tlv->radio_type; 271462306a36Sopenharmony_ci } else { 271562306a36Sopenharmony_ci radio_type = NULL; 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_ci ret = mwifiex_parse_single_response_buf(priv, &bss_info, 271862306a36Sopenharmony_ci &bytes_left, fw_tsf, 271962306a36Sopenharmony_ci radio_type, true, rssi); 272062306a36Sopenharmony_ci if (ret) 272162306a36Sopenharmony_ci goto check_next_scan; 272262306a36Sopenharmony_ci } 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_cicheck_next_scan: 272562306a36Sopenharmony_ci if (!event_scan->more_event) 272662306a36Sopenharmony_ci mwifiex_check_next_scan_command(priv); 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci return ret; 272962306a36Sopenharmony_ci} 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci/* 273262306a36Sopenharmony_ci * This function prepares command for background scan query. 273362306a36Sopenharmony_ci * 273462306a36Sopenharmony_ci * Preparation includes - 273562306a36Sopenharmony_ci * - Setting command ID and proper size 273662306a36Sopenharmony_ci * - Setting background scan flush parameter 273762306a36Sopenharmony_ci * - Ensuring correct endian-ness 273862306a36Sopenharmony_ci */ 273962306a36Sopenharmony_ciint mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) 274062306a36Sopenharmony_ci{ 274162306a36Sopenharmony_ci struct host_cmd_ds_802_11_bg_scan_query *bg_query = 274262306a36Sopenharmony_ci &cmd->params.bg_scan_query; 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); 274562306a36Sopenharmony_ci cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) 274662306a36Sopenharmony_ci + S_DS_GEN); 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci bg_query->flush = 1; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci return 0; 275162306a36Sopenharmony_ci} 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci/* 275462306a36Sopenharmony_ci * This function inserts scan command node to the scan pending queue. 275562306a36Sopenharmony_ci */ 275662306a36Sopenharmony_civoid 275762306a36Sopenharmony_cimwifiex_queue_scan_cmd(struct mwifiex_private *priv, 275862306a36Sopenharmony_ci struct cmd_ctrl_node *cmd_node) 275962306a36Sopenharmony_ci{ 276062306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci cmd_node->wait_q_enabled = true; 276362306a36Sopenharmony_ci cmd_node->condition = &adapter->scan_wait_q_woken; 276462306a36Sopenharmony_ci spin_lock_bh(&adapter->scan_pending_q_lock); 276562306a36Sopenharmony_ci list_add_tail(&cmd_node->list, &adapter->scan_pending_q); 276662306a36Sopenharmony_ci spin_unlock_bh(&adapter->scan_pending_q_lock); 276762306a36Sopenharmony_ci} 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci/* 277062306a36Sopenharmony_ci * This function sends a scan command for all available channels to the 277162306a36Sopenharmony_ci * firmware, filtered on a specific SSID. 277262306a36Sopenharmony_ci */ 277362306a36Sopenharmony_cistatic int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, 277462306a36Sopenharmony_ci struct cfg80211_ssid *req_ssid) 277562306a36Sopenharmony_ci{ 277662306a36Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 277762306a36Sopenharmony_ci int ret; 277862306a36Sopenharmony_ci struct mwifiex_user_scan_cfg *scan_cfg; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci if (adapter->scan_processing) { 278162306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 278262306a36Sopenharmony_ci "cmd: Scan already in process...\n"); 278362306a36Sopenharmony_ci return -EBUSY; 278462306a36Sopenharmony_ci } 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci if (priv->scan_block) { 278762306a36Sopenharmony_ci mwifiex_dbg(adapter, WARN, 278862306a36Sopenharmony_ci "cmd: Scan is blocked during association...\n"); 278962306a36Sopenharmony_ci return -EBUSY; 279062306a36Sopenharmony_ci } 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); 279362306a36Sopenharmony_ci if (!scan_cfg) 279462306a36Sopenharmony_ci return -ENOMEM; 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci scan_cfg->ssid_list = req_ssid; 279762306a36Sopenharmony_ci scan_cfg->num_ssids = 1; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci ret = mwifiex_scan_networks(priv, scan_cfg); 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci kfree(scan_cfg); 280262306a36Sopenharmony_ci return ret; 280362306a36Sopenharmony_ci} 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci/* 280662306a36Sopenharmony_ci * Sends IOCTL request to start a scan. 280762306a36Sopenharmony_ci * 280862306a36Sopenharmony_ci * This function allocates the IOCTL request buffer, fills it 280962306a36Sopenharmony_ci * with requisite parameters and calls the IOCTL handler. 281062306a36Sopenharmony_ci * 281162306a36Sopenharmony_ci * Scan command can be issued for both normal scan and specific SSID 281262306a36Sopenharmony_ci * scan, depending upon whether an SSID is provided or not. 281362306a36Sopenharmony_ci */ 281462306a36Sopenharmony_ciint mwifiex_request_scan(struct mwifiex_private *priv, 281562306a36Sopenharmony_ci struct cfg80211_ssid *req_ssid) 281662306a36Sopenharmony_ci{ 281762306a36Sopenharmony_ci int ret; 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci if (mutex_lock_interruptible(&priv->async_mutex)) { 282062306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 282162306a36Sopenharmony_ci "%s: acquire semaphore fail\n", 282262306a36Sopenharmony_ci __func__); 282362306a36Sopenharmony_ci return -1; 282462306a36Sopenharmony_ci } 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci priv->adapter->scan_wait_q_woken = false; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci if (req_ssid && req_ssid->ssid_len != 0) 282962306a36Sopenharmony_ci /* Specific SSID scan */ 283062306a36Sopenharmony_ci ret = mwifiex_scan_specific_ssid(priv, req_ssid); 283162306a36Sopenharmony_ci else 283262306a36Sopenharmony_ci /* Normal scan */ 283362306a36Sopenharmony_ci ret = mwifiex_scan_networks(priv, NULL); 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci mutex_unlock(&priv->async_mutex); 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci return ret; 283862306a36Sopenharmony_ci} 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci/* 284162306a36Sopenharmony_ci * This function appends the vendor specific IE TLV to a buffer. 284262306a36Sopenharmony_ci */ 284362306a36Sopenharmony_ciint 284462306a36Sopenharmony_cimwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, 284562306a36Sopenharmony_ci u16 vsie_mask, u8 **buffer) 284662306a36Sopenharmony_ci{ 284762306a36Sopenharmony_ci int id, ret_len = 0; 284862306a36Sopenharmony_ci struct mwifiex_ie_types_vendor_param_set *vs_param_set; 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci if (!buffer) 285162306a36Sopenharmony_ci return 0; 285262306a36Sopenharmony_ci if (!(*buffer)) 285362306a36Sopenharmony_ci return 0; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci /* 285662306a36Sopenharmony_ci * Traverse through the saved vendor specific IE array and append 285762306a36Sopenharmony_ci * the selected(scan/assoc/adhoc) IE as TLV to the command 285862306a36Sopenharmony_ci */ 285962306a36Sopenharmony_ci for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { 286062306a36Sopenharmony_ci if (priv->vs_ie[id].mask & vsie_mask) { 286162306a36Sopenharmony_ci vs_param_set = 286262306a36Sopenharmony_ci (struct mwifiex_ie_types_vendor_param_set *) 286362306a36Sopenharmony_ci *buffer; 286462306a36Sopenharmony_ci vs_param_set->header.type = 286562306a36Sopenharmony_ci cpu_to_le16(TLV_TYPE_PASSTHROUGH); 286662306a36Sopenharmony_ci vs_param_set->header.len = 286762306a36Sopenharmony_ci cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) 286862306a36Sopenharmony_ci & 0x00FF) + 2); 286962306a36Sopenharmony_ci if (le16_to_cpu(vs_param_set->header.len) > 287062306a36Sopenharmony_ci MWIFIEX_MAX_VSIE_LEN) { 287162306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, ERROR, 287262306a36Sopenharmony_ci "Invalid param length!\n"); 287362306a36Sopenharmony_ci break; 287462306a36Sopenharmony_ci } 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci memcpy(vs_param_set->ie, priv->vs_ie[id].ie, 287762306a36Sopenharmony_ci le16_to_cpu(vs_param_set->header.len)); 287862306a36Sopenharmony_ci *buffer += le16_to_cpu(vs_param_set->header.len) + 287962306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header); 288062306a36Sopenharmony_ci ret_len += le16_to_cpu(vs_param_set->header.len) + 288162306a36Sopenharmony_ci sizeof(struct mwifiex_ie_types_header); 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci } 288462306a36Sopenharmony_ci return ret_len; 288562306a36Sopenharmony_ci} 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci/* 288862306a36Sopenharmony_ci * This function saves a beacon buffer of the current BSS descriptor. 288962306a36Sopenharmony_ci * 289062306a36Sopenharmony_ci * The current beacon buffer is saved so that it can be restored in the 289162306a36Sopenharmony_ci * following cases that makes the beacon buffer not to contain the current 289262306a36Sopenharmony_ci * ssid's beacon buffer. 289362306a36Sopenharmony_ci * - The current ssid was not found somehow in the last scan. 289462306a36Sopenharmony_ci * - The current ssid was the last entry of the scan table and overloaded. 289562306a36Sopenharmony_ci */ 289662306a36Sopenharmony_civoid 289762306a36Sopenharmony_cimwifiex_save_curr_bcn(struct mwifiex_private *priv) 289862306a36Sopenharmony_ci{ 289962306a36Sopenharmony_ci struct mwifiex_bssdescriptor *curr_bss = 290062306a36Sopenharmony_ci &priv->curr_bss_params.bss_descriptor; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci if (!curr_bss->beacon_buf_size) 290362306a36Sopenharmony_ci return; 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci /* allocate beacon buffer at 1st time; or if it's size has changed */ 290662306a36Sopenharmony_ci if (!priv->curr_bcn_buf || 290762306a36Sopenharmony_ci priv->curr_bcn_size != curr_bss->beacon_buf_size) { 290862306a36Sopenharmony_ci priv->curr_bcn_size = curr_bss->beacon_buf_size; 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci kfree(priv->curr_bcn_buf); 291162306a36Sopenharmony_ci priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, 291262306a36Sopenharmony_ci GFP_ATOMIC); 291362306a36Sopenharmony_ci if (!priv->curr_bcn_buf) 291462306a36Sopenharmony_ci return; 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, 291862306a36Sopenharmony_ci curr_bss->beacon_buf_size); 291962306a36Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 292062306a36Sopenharmony_ci "info: current beacon saved %d\n", 292162306a36Sopenharmony_ci priv->curr_bcn_size); 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci curr_bss->beacon_buf = priv->curr_bcn_buf; 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci /* adjust the pointers in the current BSS descriptor */ 292662306a36Sopenharmony_ci if (curr_bss->bcn_wpa_ie) 292762306a36Sopenharmony_ci curr_bss->bcn_wpa_ie = 292862306a36Sopenharmony_ci (struct ieee_types_vendor_specific *) 292962306a36Sopenharmony_ci (curr_bss->beacon_buf + 293062306a36Sopenharmony_ci curr_bss->wpa_offset); 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci if (curr_bss->bcn_rsn_ie) 293362306a36Sopenharmony_ci curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) 293462306a36Sopenharmony_ci (curr_bss->beacon_buf + 293562306a36Sopenharmony_ci curr_bss->rsn_offset); 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci if (curr_bss->bcn_ht_cap) 293862306a36Sopenharmony_ci curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) 293962306a36Sopenharmony_ci (curr_bss->beacon_buf + 294062306a36Sopenharmony_ci curr_bss->ht_cap_offset); 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci if (curr_bss->bcn_ht_oper) 294362306a36Sopenharmony_ci curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) 294462306a36Sopenharmony_ci (curr_bss->beacon_buf + 294562306a36Sopenharmony_ci curr_bss->ht_info_offset); 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci if (curr_bss->bcn_vht_cap) 294862306a36Sopenharmony_ci curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + 294962306a36Sopenharmony_ci curr_bss->vht_cap_offset); 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci if (curr_bss->bcn_vht_oper) 295262306a36Sopenharmony_ci curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + 295362306a36Sopenharmony_ci curr_bss->vht_info_offset); 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci if (curr_bss->bcn_bss_co_2040) 295662306a36Sopenharmony_ci curr_bss->bcn_bss_co_2040 = 295762306a36Sopenharmony_ci (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci if (curr_bss->bcn_ext_cap) 296062306a36Sopenharmony_ci curr_bss->bcn_ext_cap = curr_bss->beacon_buf + 296162306a36Sopenharmony_ci curr_bss->ext_cap_offset; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci if (curr_bss->oper_mode) 296462306a36Sopenharmony_ci curr_bss->oper_mode = (void *)(curr_bss->beacon_buf + 296562306a36Sopenharmony_ci curr_bss->oper_mode_offset); 296662306a36Sopenharmony_ci} 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci/* 296962306a36Sopenharmony_ci * This function frees the current BSS descriptor beacon buffer. 297062306a36Sopenharmony_ci */ 297162306a36Sopenharmony_civoid 297262306a36Sopenharmony_cimwifiex_free_curr_bcn(struct mwifiex_private *priv) 297362306a36Sopenharmony_ci{ 297462306a36Sopenharmony_ci kfree(priv->curr_bcn_buf); 297562306a36Sopenharmony_ci priv->curr_bcn_buf = NULL; 297662306a36Sopenharmony_ci} 2977