18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: scan ioctl and command handling
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2011-2020 NXP
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by NXP
78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License Version 2, June 1991
88c2ecf20Sopenharmony_ci * (the "License").  You may use, redistribute and/or modify this File in
98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which
108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc.,
118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
158c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
168c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
178c2ecf20Sopenharmony_ci * this warranty disclaimer.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "decl.h"
218c2ecf20Sopenharmony_ci#include "ioctl.h"
228c2ecf20Sopenharmony_ci#include "util.h"
238c2ecf20Sopenharmony_ci#include "fw.h"
248c2ecf20Sopenharmony_ci#include "main.h"
258c2ecf20Sopenharmony_ci#include "11n.h"
268c2ecf20Sopenharmony_ci#include "cfg80211.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* The maximum number of channels the firmware can scan per command */
298c2ecf20Sopenharmony_ci#define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN   14
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD	4
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Memory needed to store a max sized Channel List TLV for a firmware scan */
348c2ecf20Sopenharmony_ci#define CHAN_TLV_MAX_SIZE  (sizeof(struct mwifiex_ie_types_header)         \
358c2ecf20Sopenharmony_ci				+ (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN     \
368c2ecf20Sopenharmony_ci				*sizeof(struct mwifiex_chan_scan_param_set)))
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Memory needed to store supported rate */
398c2ecf20Sopenharmony_ci#define RATE_TLV_MAX_SIZE   (sizeof(struct mwifiex_ie_types_rates_param_set) \
408c2ecf20Sopenharmony_ci				+ HOSTCMD_SUPPORTED_RATES)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Memory needed to store a max number/size WildCard SSID TLV for a firmware
438c2ecf20Sopenharmony_ci	scan */
448c2ecf20Sopenharmony_ci#define WILDCARD_SSID_TLV_MAX_SIZE  \
458c2ecf20Sopenharmony_ci	(MWIFIEX_MAX_SSID_LIST_LENGTH *					\
468c2ecf20Sopenharmony_ci		(sizeof(struct mwifiex_ie_types_wildcard_ssid_params)	\
478c2ecf20Sopenharmony_ci			+ IEEE80211_MAX_SSID_LEN))
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */
508c2ecf20Sopenharmony_ci#define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config)        \
518c2ecf20Sopenharmony_ci				+ sizeof(struct mwifiex_ie_types_num_probes)   \
528c2ecf20Sopenharmony_ci				+ sizeof(struct mwifiex_ie_types_htcap)       \
538c2ecf20Sopenharmony_ci				+ CHAN_TLV_MAX_SIZE                 \
548c2ecf20Sopenharmony_ci				+ RATE_TLV_MAX_SIZE                 \
558c2ecf20Sopenharmony_ci				+ WILDCARD_SSID_TLV_MAX_SIZE)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciunion mwifiex_scan_cmd_config_tlv {
598c2ecf20Sopenharmony_ci	/* Scan configuration (variable length) */
608c2ecf20Sopenharmony_ci	struct mwifiex_scan_cmd_config config;
618c2ecf20Sopenharmony_ci	/* Max allocated block */
628c2ecf20Sopenharmony_ci	u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC];
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cienum cipher_suite {
668c2ecf20Sopenharmony_ci	CIPHER_SUITE_TKIP,
678c2ecf20Sopenharmony_ci	CIPHER_SUITE_CCMP,
688c2ecf20Sopenharmony_ci	CIPHER_SUITE_MAX
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_cistatic u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = {
718c2ecf20Sopenharmony_ci	{ 0x00, 0x50, 0xf2, 0x02 },	/* TKIP */
728c2ecf20Sopenharmony_ci	{ 0x00, 0x50, 0xf2, 0x04 },	/* AES  */
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_cistatic u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = {
758c2ecf20Sopenharmony_ci	{ 0x00, 0x0f, 0xac, 0x02 },	/* TKIP */
768c2ecf20Sopenharmony_ci	{ 0x00, 0x0f, 0xac, 0x04 },	/* AES  */
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void
808c2ecf20Sopenharmony_ci_dbg_security_flags(int log_level, const char *func, const char *desc,
818c2ecf20Sopenharmony_ci		    struct mwifiex_private *priv,
828c2ecf20Sopenharmony_ci		    struct mwifiex_bssdescriptor *bss_desc)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	_mwifiex_dbg(priv->adapter, log_level,
858c2ecf20Sopenharmony_ci		     "info: %s: %s:\twpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\tEncMode=%#x privacy=%#x\n",
868c2ecf20Sopenharmony_ci		     func, desc,
878c2ecf20Sopenharmony_ci		     bss_desc->bcn_wpa_ie ?
888c2ecf20Sopenharmony_ci		     bss_desc->bcn_wpa_ie->vend_hdr.element_id : 0,
898c2ecf20Sopenharmony_ci		     bss_desc->bcn_rsn_ie ?
908c2ecf20Sopenharmony_ci		     bss_desc->bcn_rsn_ie->ieee_hdr.element_id : 0,
918c2ecf20Sopenharmony_ci		     priv->sec_info.wep_enabled ? "e" : "d",
928c2ecf20Sopenharmony_ci		     priv->sec_info.wpa_enabled ? "e" : "d",
938c2ecf20Sopenharmony_ci		     priv->sec_info.wpa2_enabled ? "e" : "d",
948c2ecf20Sopenharmony_ci		     priv->sec_info.encryption_mode,
958c2ecf20Sopenharmony_ci		     bss_desc->privacy);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci#define dbg_security_flags(mask, desc, priv, bss_desc) \
988c2ecf20Sopenharmony_ci	_dbg_security_flags(MWIFIEX_DBG_##mask, desc, __func__, priv, bss_desc)
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic bool
1018c2ecf20Sopenharmony_cihas_ieee_hdr(struct ieee_types_generic *ie, u8 key)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	return (ie && ie->ieee_hdr.element_id == key);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic bool
1078c2ecf20Sopenharmony_cihas_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	return (ie && ie->vend_hdr.element_id == key);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * This function parses a given IE for a given OUI.
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci * This is used to parse a WPA/RSN IE to find if it has
1168c2ecf20Sopenharmony_ci * a given oui in PTK.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_cistatic u8
1198c2ecf20Sopenharmony_cimwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u8 count;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	count = iebody->ptk_cnt[0];
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* There could be multiple OUIs for PTK hence
1268c2ecf20Sopenharmony_ci	   1) Take the length.
1278c2ecf20Sopenharmony_ci	   2) Check all the OUIs for AES.
1288c2ecf20Sopenharmony_ci	   3) If one of them is AES then pass success. */
1298c2ecf20Sopenharmony_ci	while (count) {
1308c2ecf20Sopenharmony_ci		if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body)))
1318c2ecf20Sopenharmony_ci			return MWIFIEX_OUI_PRESENT;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		--count;
1348c2ecf20Sopenharmony_ci		if (count)
1358c2ecf20Sopenharmony_ci			iebody = (struct ie_body *) ((u8 *) iebody +
1368c2ecf20Sopenharmony_ci						sizeof(iebody->ptk_body));
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	pr_debug("info: %s: OUI is not found in PTK\n", __func__);
1408c2ecf20Sopenharmony_ci	return MWIFIEX_OUI_NOT_PRESENT;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/*
1448c2ecf20Sopenharmony_ci * This function checks if a given OUI is present in a RSN IE.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * The function first checks if a RSN IE is present or not in the
1478c2ecf20Sopenharmony_ci * BSS descriptor. It tries to locate the OUI only if such an IE is
1488c2ecf20Sopenharmony_ci * present.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_cistatic u8
1518c2ecf20Sopenharmony_cimwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	u8 *oui;
1548c2ecf20Sopenharmony_ci	struct ie_body *iebody;
1558c2ecf20Sopenharmony_ci	u8 ret = MWIFIEX_OUI_NOT_PRESENT;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
1588c2ecf20Sopenharmony_ci		iebody = (struct ie_body *)
1598c2ecf20Sopenharmony_ci			 (((u8 *) bss_desc->bcn_rsn_ie->data) +
1608c2ecf20Sopenharmony_ci			  RSN_GTK_OUI_OFFSET);
1618c2ecf20Sopenharmony_ci		oui = &mwifiex_rsn_oui[cipher][0];
1628c2ecf20Sopenharmony_ci		ret = mwifiex_search_oui_in_ie(iebody, oui);
1638c2ecf20Sopenharmony_ci		if (ret)
1648c2ecf20Sopenharmony_ci			return ret;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci	return ret;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/*
1708c2ecf20Sopenharmony_ci * This function checks if a given OUI is present in a WPA IE.
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * The function first checks if a WPA IE is present or not in the
1738c2ecf20Sopenharmony_ci * BSS descriptor. It tries to locate the OUI only if such an IE is
1748c2ecf20Sopenharmony_ci * present.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic u8
1778c2ecf20Sopenharmony_cimwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	u8 *oui;
1808c2ecf20Sopenharmony_ci	struct ie_body *iebody;
1818c2ecf20Sopenharmony_ci	u8 ret = MWIFIEX_OUI_NOT_PRESENT;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) {
1848c2ecf20Sopenharmony_ci		iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
1858c2ecf20Sopenharmony_ci					    WPA_GTK_OUI_OFFSET);
1868c2ecf20Sopenharmony_ci		oui = &mwifiex_wpa_oui[cipher][0];
1878c2ecf20Sopenharmony_ci		ret = mwifiex_search_oui_in_ie(iebody, oui);
1888c2ecf20Sopenharmony_ci		if (ret)
1898c2ecf20Sopenharmony_ci			return ret;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	return ret;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci * This function compares two SSIDs and checks if they match.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_cis32
1988c2ecf20Sopenharmony_cimwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len))
2018c2ecf20Sopenharmony_ci		return -1;
2028c2ecf20Sopenharmony_ci	return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci * This function checks if wapi is enabled in driver and scanned network is
2078c2ecf20Sopenharmony_ci * compatible with it.
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic bool
2108c2ecf20Sopenharmony_cimwifiex_is_bss_wapi(struct mwifiex_private *priv,
2118c2ecf20Sopenharmony_ci		    struct mwifiex_bssdescriptor *bss_desc)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (priv->sec_info.wapi_enabled &&
2148c2ecf20Sopenharmony_ci	    has_ieee_hdr(bss_desc->bcn_wapi_ie, WLAN_EID_BSS_AC_ACCESS_DELAY))
2158c2ecf20Sopenharmony_ci		return true;
2168c2ecf20Sopenharmony_ci	return false;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * This function checks if driver is configured with no security mode and
2218c2ecf20Sopenharmony_ci * scanned network is compatible with it.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistatic bool
2248c2ecf20Sopenharmony_cimwifiex_is_bss_no_sec(struct mwifiex_private *priv,
2258c2ecf20Sopenharmony_ci		      struct mwifiex_bssdescriptor *bss_desc)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
2288c2ecf20Sopenharmony_ci	    !priv->sec_info.wpa2_enabled &&
2298c2ecf20Sopenharmony_ci	    !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
2308c2ecf20Sopenharmony_ci	    !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
2318c2ecf20Sopenharmony_ci	    !priv->sec_info.encryption_mode && !bss_desc->privacy) {
2328c2ecf20Sopenharmony_ci		return true;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	return false;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/*
2388c2ecf20Sopenharmony_ci * This function checks if static WEP is enabled in driver and scanned network
2398c2ecf20Sopenharmony_ci * is compatible with it.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_cistatic bool
2428c2ecf20Sopenharmony_cimwifiex_is_bss_static_wep(struct mwifiex_private *priv,
2438c2ecf20Sopenharmony_ci			  struct mwifiex_bssdescriptor *bss_desc)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
2468c2ecf20Sopenharmony_ci	    !priv->sec_info.wpa2_enabled && bss_desc->privacy) {
2478c2ecf20Sopenharmony_ci		return true;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return false;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/*
2538c2ecf20Sopenharmony_ci * This function checks if wpa is enabled in driver and scanned network is
2548c2ecf20Sopenharmony_ci * compatible with it.
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_cistatic bool
2578c2ecf20Sopenharmony_cimwifiex_is_bss_wpa(struct mwifiex_private *priv,
2588c2ecf20Sopenharmony_ci		   struct mwifiex_bssdescriptor *bss_desc)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled &&
2618c2ecf20Sopenharmony_ci	    !priv->sec_info.wpa2_enabled &&
2628c2ecf20Sopenharmony_ci	    has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)
2638c2ecf20Sopenharmony_ci	   /*
2648c2ecf20Sopenharmony_ci	    * Privacy bit may NOT be set in some APs like
2658c2ecf20Sopenharmony_ci	    * LinkSys WRT54G && bss_desc->privacy
2668c2ecf20Sopenharmony_ci	    */
2678c2ecf20Sopenharmony_ci	 ) {
2688c2ecf20Sopenharmony_ci		dbg_security_flags(INFO, "WPA", priv, bss_desc);
2698c2ecf20Sopenharmony_ci		return true;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	return false;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/*
2758c2ecf20Sopenharmony_ci * This function checks if wpa2 is enabled in driver and scanned network is
2768c2ecf20Sopenharmony_ci * compatible with it.
2778c2ecf20Sopenharmony_ci */
2788c2ecf20Sopenharmony_cistatic bool
2798c2ecf20Sopenharmony_cimwifiex_is_bss_wpa2(struct mwifiex_private *priv,
2808c2ecf20Sopenharmony_ci		    struct mwifiex_bssdescriptor *bss_desc)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
2838c2ecf20Sopenharmony_ci	    priv->sec_info.wpa2_enabled &&
2848c2ecf20Sopenharmony_ci	    has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
2858c2ecf20Sopenharmony_ci		/*
2868c2ecf20Sopenharmony_ci		 * Privacy bit may NOT be set in some APs like
2878c2ecf20Sopenharmony_ci		 * LinkSys WRT54G && bss_desc->privacy
2888c2ecf20Sopenharmony_ci		 */
2898c2ecf20Sopenharmony_ci		dbg_security_flags(INFO, "WAP2", priv, bss_desc);
2908c2ecf20Sopenharmony_ci		return true;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci	return false;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/*
2968c2ecf20Sopenharmony_ci * This function checks if adhoc AES is enabled in driver and scanned network is
2978c2ecf20Sopenharmony_ci * compatible with it.
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_cistatic bool
3008c2ecf20Sopenharmony_cimwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv,
3018c2ecf20Sopenharmony_ci			 struct mwifiex_bssdescriptor *bss_desc)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
3048c2ecf20Sopenharmony_ci	    !priv->sec_info.wpa2_enabled &&
3058c2ecf20Sopenharmony_ci	    !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
3068c2ecf20Sopenharmony_ci	    !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
3078c2ecf20Sopenharmony_ci	    !priv->sec_info.encryption_mode && bss_desc->privacy) {
3088c2ecf20Sopenharmony_ci		return true;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	return false;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/*
3148c2ecf20Sopenharmony_ci * This function checks if dynamic WEP is enabled in driver and scanned network
3158c2ecf20Sopenharmony_ci * is compatible with it.
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_cistatic bool
3188c2ecf20Sopenharmony_cimwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv,
3198c2ecf20Sopenharmony_ci			   struct mwifiex_bssdescriptor *bss_desc)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
3228c2ecf20Sopenharmony_ci	    !priv->sec_info.wpa2_enabled &&
3238c2ecf20Sopenharmony_ci	    !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
3248c2ecf20Sopenharmony_ci	    !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
3258c2ecf20Sopenharmony_ci	    priv->sec_info.encryption_mode && bss_desc->privacy) {
3268c2ecf20Sopenharmony_ci		dbg_security_flags(INFO, "dynamic", priv, bss_desc);
3278c2ecf20Sopenharmony_ci		return true;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	return false;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/*
3338c2ecf20Sopenharmony_ci * This function checks if a scanned network is compatible with the driver
3348c2ecf20Sopenharmony_ci * settings.
3358c2ecf20Sopenharmony_ci *
3368c2ecf20Sopenharmony_ci *   WEP     WPA    WPA2   ad-hoc encrypt                  Network
3378c2ecf20Sopenharmony_ci * enabled enabled enabled  AES    mode   Privacy WPA WPA2 Compatible
3388c2ecf20Sopenharmony_ci *    0       0       0      0     NONE      0     0   0   yes No security
3398c2ecf20Sopenharmony_ci *    0       1       0      0      x        1x    1   x   yes WPA (disable
3408c2ecf20Sopenharmony_ci *                                                         HT if no AES)
3418c2ecf20Sopenharmony_ci *    0       0       1      0      x        1x    x   1   yes WPA2 (disable
3428c2ecf20Sopenharmony_ci *                                                         HT if no AES)
3438c2ecf20Sopenharmony_ci *    0       0       0      1     NONE      1     0   0   yes Ad-hoc AES
3448c2ecf20Sopenharmony_ci *    1       0       0      0     NONE      1     0   0   yes Static WEP
3458c2ecf20Sopenharmony_ci *                                                         (disable HT)
3468c2ecf20Sopenharmony_ci *    0       0       0      0    !=NONE     1     0   0   yes Dynamic WEP
3478c2ecf20Sopenharmony_ci *
3488c2ecf20Sopenharmony_ci * Compatibility is not matched while roaming, except for mode.
3498c2ecf20Sopenharmony_ci */
3508c2ecf20Sopenharmony_cistatic s32
3518c2ecf20Sopenharmony_cimwifiex_is_network_compatible(struct mwifiex_private *priv,
3528c2ecf20Sopenharmony_ci			      struct mwifiex_bssdescriptor *bss_desc, u32 mode)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	bss_desc->disable_11n = false;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Don't check for compatibility if roaming */
3598c2ecf20Sopenharmony_ci	if (priv->media_connected &&
3608c2ecf20Sopenharmony_ci	    (priv->bss_mode == NL80211_IFTYPE_STATION) &&
3618c2ecf20Sopenharmony_ci	    (bss_desc->bss_mode == NL80211_IFTYPE_STATION))
3628c2ecf20Sopenharmony_ci		return 0;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (priv->wps.session_enable) {
3658c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, IOCTL,
3668c2ecf20Sopenharmony_ci			    "info: return success directly in WPS period\n");
3678c2ecf20Sopenharmony_ci		return 0;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (bss_desc->chan_sw_ie_present) {
3718c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
3728c2ecf20Sopenharmony_ci			    "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
3738c2ecf20Sopenharmony_ci		return -1;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (mwifiex_is_bss_wapi(priv, bss_desc)) {
3778c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
3788c2ecf20Sopenharmony_ci			    "info: return success for WAPI AP\n");
3798c2ecf20Sopenharmony_ci		return 0;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (bss_desc->bss_mode == mode) {
3838c2ecf20Sopenharmony_ci		if (mwifiex_is_bss_no_sec(priv, bss_desc)) {
3848c2ecf20Sopenharmony_ci			/* No security */
3858c2ecf20Sopenharmony_ci			return 0;
3868c2ecf20Sopenharmony_ci		} else if (mwifiex_is_bss_static_wep(priv, bss_desc)) {
3878c2ecf20Sopenharmony_ci			/* Static WEP enabled */
3888c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
3898c2ecf20Sopenharmony_ci				    "info: Disable 11n in WEP mode.\n");
3908c2ecf20Sopenharmony_ci			bss_desc->disable_11n = true;
3918c2ecf20Sopenharmony_ci			return 0;
3928c2ecf20Sopenharmony_ci		} else if (mwifiex_is_bss_wpa(priv, bss_desc)) {
3938c2ecf20Sopenharmony_ci			/* WPA enabled */
3948c2ecf20Sopenharmony_ci			if (((priv->adapter->config_bands & BAND_GN ||
3958c2ecf20Sopenharmony_ci			      priv->adapter->config_bands & BAND_AN) &&
3968c2ecf20Sopenharmony_ci			     bss_desc->bcn_ht_cap) &&
3978c2ecf20Sopenharmony_ci			    !mwifiex_is_wpa_oui_present(bss_desc,
3988c2ecf20Sopenharmony_ci							 CIPHER_SUITE_CCMP)) {
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci				if (mwifiex_is_wpa_oui_present
4018c2ecf20Sopenharmony_ci						(bss_desc, CIPHER_SUITE_TKIP)) {
4028c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
4038c2ecf20Sopenharmony_ci						    "info: Disable 11n if AES\t"
4048c2ecf20Sopenharmony_ci						    "is not supported by AP\n");
4058c2ecf20Sopenharmony_ci					bss_desc->disable_11n = true;
4068c2ecf20Sopenharmony_ci				} else {
4078c2ecf20Sopenharmony_ci					return -1;
4088c2ecf20Sopenharmony_ci				}
4098c2ecf20Sopenharmony_ci			}
4108c2ecf20Sopenharmony_ci			return 0;
4118c2ecf20Sopenharmony_ci		} else if (mwifiex_is_bss_wpa2(priv, bss_desc)) {
4128c2ecf20Sopenharmony_ci			/* WPA2 enabled */
4138c2ecf20Sopenharmony_ci			if (((priv->adapter->config_bands & BAND_GN ||
4148c2ecf20Sopenharmony_ci			      priv->adapter->config_bands & BAND_AN) &&
4158c2ecf20Sopenharmony_ci			     bss_desc->bcn_ht_cap) &&
4168c2ecf20Sopenharmony_ci			    !mwifiex_is_rsn_oui_present(bss_desc,
4178c2ecf20Sopenharmony_ci							CIPHER_SUITE_CCMP)) {
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci				if (mwifiex_is_rsn_oui_present
4208c2ecf20Sopenharmony_ci						(bss_desc, CIPHER_SUITE_TKIP)) {
4218c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
4228c2ecf20Sopenharmony_ci						    "info: Disable 11n if AES\t"
4238c2ecf20Sopenharmony_ci						    "is not supported by AP\n");
4248c2ecf20Sopenharmony_ci					bss_desc->disable_11n = true;
4258c2ecf20Sopenharmony_ci				} else {
4268c2ecf20Sopenharmony_ci					return -1;
4278c2ecf20Sopenharmony_ci				}
4288c2ecf20Sopenharmony_ci			}
4298c2ecf20Sopenharmony_ci			return 0;
4308c2ecf20Sopenharmony_ci		} else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) {
4318c2ecf20Sopenharmony_ci			/* Ad-hoc AES enabled */
4328c2ecf20Sopenharmony_ci			return 0;
4338c2ecf20Sopenharmony_ci		} else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) {
4348c2ecf20Sopenharmony_ci			/* Dynamic WEP enabled */
4358c2ecf20Sopenharmony_ci			return 0;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		/* Security doesn't match */
4398c2ecf20Sopenharmony_ci		dbg_security_flags(ERROR, "failed", priv, bss_desc);
4408c2ecf20Sopenharmony_ci		return -1;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* Mode doesn't match */
4448c2ecf20Sopenharmony_ci	return -1;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci/*
4488c2ecf20Sopenharmony_ci * This function creates a channel list for the driver to scan, based
4498c2ecf20Sopenharmony_ci * on region/band information.
4508c2ecf20Sopenharmony_ci *
4518c2ecf20Sopenharmony_ci * This routine is used for any scan that is not provided with a
4528c2ecf20Sopenharmony_ci * specific channel list to scan.
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_cistatic int
4558c2ecf20Sopenharmony_cimwifiex_scan_create_channel_list(struct mwifiex_private *priv,
4568c2ecf20Sopenharmony_ci				 const struct mwifiex_user_scan_cfg
4578c2ecf20Sopenharmony_ci							*user_scan_in,
4588c2ecf20Sopenharmony_ci				 struct mwifiex_chan_scan_param_set
4598c2ecf20Sopenharmony_ci							*scan_chan_list,
4608c2ecf20Sopenharmony_ci				 u8 filtered_scan)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	enum nl80211_band band;
4638c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *sband;
4648c2ecf20Sopenharmony_ci	struct ieee80211_channel *ch;
4658c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
4668c2ecf20Sopenharmony_ci	int chan_idx = 0, i;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	for (band = 0; (band < NUM_NL80211_BANDS) ; band++) {
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		if (!priv->wdev.wiphy->bands[band])
4718c2ecf20Sopenharmony_ci			continue;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		sband = priv->wdev.wiphy->bands[band];
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		for (i = 0; (i < sband->n_channels) ; i++) {
4768c2ecf20Sopenharmony_ci			ch = &sband->channels[i];
4778c2ecf20Sopenharmony_ci			if (ch->flags & IEEE80211_CHAN_DISABLED)
4788c2ecf20Sopenharmony_ci				continue;
4798c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].radio_type = band;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci			if (user_scan_in &&
4828c2ecf20Sopenharmony_ci			    user_scan_in->chan_list[0].scan_time)
4838c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
4848c2ecf20Sopenharmony_ci					cpu_to_le16((u16) user_scan_in->
4858c2ecf20Sopenharmony_ci					chan_list[0].scan_time);
4868c2ecf20Sopenharmony_ci			else if ((ch->flags & IEEE80211_CHAN_NO_IR) ||
4878c2ecf20Sopenharmony_ci				 (ch->flags & IEEE80211_CHAN_RADAR))
4888c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
4898c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->passive_scan_time);
4908c2ecf20Sopenharmony_ci			else
4918c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
4928c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->active_scan_time);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci			if (ch->flags & IEEE80211_CHAN_NO_IR)
4958c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].chan_scan_mode_bitmap
4968c2ecf20Sopenharmony_ci					|= (MWIFIEX_PASSIVE_SCAN |
4978c2ecf20Sopenharmony_ci					    MWIFIEX_HIDDEN_SSID_REPORT);
4988c2ecf20Sopenharmony_ci			else
4998c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].chan_scan_mode_bitmap
5008c2ecf20Sopenharmony_ci					&= ~MWIFIEX_PASSIVE_SCAN;
5018c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].chan_number =
5028c2ecf20Sopenharmony_ci							(u32) ch->hw_value;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].chan_scan_mode_bitmap
5058c2ecf20Sopenharmony_ci					|= MWIFIEX_DISABLE_CHAN_FILT;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			if (filtered_scan &&
5088c2ecf20Sopenharmony_ci			    !((ch->flags & IEEE80211_CHAN_NO_IR) ||
5098c2ecf20Sopenharmony_ci			      (ch->flags & IEEE80211_CHAN_RADAR)))
5108c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
5118c2ecf20Sopenharmony_ci				cpu_to_le16(adapter->specific_scan_time);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci			chan_idx++;
5148c2ecf20Sopenharmony_ci		}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	return chan_idx;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/* This function creates a channel list tlv for bgscan config, based
5218c2ecf20Sopenharmony_ci * on region/band information.
5228c2ecf20Sopenharmony_ci */
5238c2ecf20Sopenharmony_cistatic int
5248c2ecf20Sopenharmony_cimwifiex_bgscan_create_channel_list(struct mwifiex_private *priv,
5258c2ecf20Sopenharmony_ci				   const struct mwifiex_bg_scan_cfg
5268c2ecf20Sopenharmony_ci						*bgscan_cfg_in,
5278c2ecf20Sopenharmony_ci				   struct mwifiex_chan_scan_param_set
5288c2ecf20Sopenharmony_ci						*scan_chan_list)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	enum nl80211_band band;
5318c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *sband;
5328c2ecf20Sopenharmony_ci	struct ieee80211_channel *ch;
5338c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
5348c2ecf20Sopenharmony_ci	int chan_idx = 0, i;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	for (band = 0; (band < NUM_NL80211_BANDS); band++) {
5378c2ecf20Sopenharmony_ci		if (!priv->wdev.wiphy->bands[band])
5388c2ecf20Sopenharmony_ci			continue;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		sband = priv->wdev.wiphy->bands[band];
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		for (i = 0; (i < sband->n_channels) ; i++) {
5438c2ecf20Sopenharmony_ci			ch = &sband->channels[i];
5448c2ecf20Sopenharmony_ci			if (ch->flags & IEEE80211_CHAN_DISABLED)
5458c2ecf20Sopenharmony_ci				continue;
5468c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].radio_type = band;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci			if (bgscan_cfg_in->chan_list[0].scan_time)
5498c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
5508c2ecf20Sopenharmony_ci					cpu_to_le16((u16)bgscan_cfg_in->
5518c2ecf20Sopenharmony_ci					chan_list[0].scan_time);
5528c2ecf20Sopenharmony_ci			else if (ch->flags & IEEE80211_CHAN_NO_IR)
5538c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
5548c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->passive_scan_time);
5558c2ecf20Sopenharmony_ci			else
5568c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].max_scan_time =
5578c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->
5588c2ecf20Sopenharmony_ci						    specific_scan_time);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci			if (ch->flags & IEEE80211_CHAN_NO_IR)
5618c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].chan_scan_mode_bitmap
5628c2ecf20Sopenharmony_ci					|= MWIFIEX_PASSIVE_SCAN;
5638c2ecf20Sopenharmony_ci			else
5648c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].chan_scan_mode_bitmap
5658c2ecf20Sopenharmony_ci					&= ~MWIFIEX_PASSIVE_SCAN;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].chan_number =
5688c2ecf20Sopenharmony_ci							(u32)ch->hw_value;
5698c2ecf20Sopenharmony_ci			chan_idx++;
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	return chan_idx;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci/* This function appends rate TLV to scan config command. */
5768c2ecf20Sopenharmony_cistatic int
5778c2ecf20Sopenharmony_cimwifiex_append_rate_tlv(struct mwifiex_private *priv,
5788c2ecf20Sopenharmony_ci			struct mwifiex_scan_cmd_config *scan_cfg_out,
5798c2ecf20Sopenharmony_ci			u8 radio)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_rates_param_set *rates_tlv;
5828c2ecf20Sopenharmony_ci	u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos;
5838c2ecf20Sopenharmony_ci	u32 rates_size;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	memset(rates, 0, sizeof(rates));
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (priv->scan_request)
5908c2ecf20Sopenharmony_ci		rates_size = mwifiex_get_rates_from_cfg80211(priv, rates,
5918c2ecf20Sopenharmony_ci							     radio);
5928c2ecf20Sopenharmony_ci	else
5938c2ecf20Sopenharmony_ci		rates_size = mwifiex_get_supported_rates(priv, rates);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	mwifiex_dbg(priv->adapter, CMD,
5968c2ecf20Sopenharmony_ci		    "info: SCAN_CMD: Rates size = %d\n",
5978c2ecf20Sopenharmony_ci		rates_size);
5988c2ecf20Sopenharmony_ci	rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos;
5998c2ecf20Sopenharmony_ci	rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
6008c2ecf20Sopenharmony_ci	rates_tlv->header.len = cpu_to_le16((u16) rates_size);
6018c2ecf20Sopenharmony_ci	memcpy(rates_tlv->rates, rates, rates_size);
6028c2ecf20Sopenharmony_ci	scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return rates_size;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/*
6088c2ecf20Sopenharmony_ci * This function constructs and sends multiple scan config commands to
6098c2ecf20Sopenharmony_ci * the firmware.
6108c2ecf20Sopenharmony_ci *
6118c2ecf20Sopenharmony_ci * Previous routines in the code flow have created a scan command configuration
6128c2ecf20Sopenharmony_ci * with any requested TLVs.  This function splits the channel TLV into maximum
6138c2ecf20Sopenharmony_ci * channels supported per scan lists and sends the portion of the channel TLV,
6148c2ecf20Sopenharmony_ci * along with the other TLVs, to the firmware.
6158c2ecf20Sopenharmony_ci */
6168c2ecf20Sopenharmony_cistatic int
6178c2ecf20Sopenharmony_cimwifiex_scan_channel_list(struct mwifiex_private *priv,
6188c2ecf20Sopenharmony_ci			  u32 max_chan_per_scan, u8 filtered_scan,
6198c2ecf20Sopenharmony_ci			  struct mwifiex_scan_cmd_config *scan_cfg_out,
6208c2ecf20Sopenharmony_ci			  struct mwifiex_ie_types_chan_list_param_set
6218c2ecf20Sopenharmony_ci			  *chan_tlv_out,
6228c2ecf20Sopenharmony_ci			  struct mwifiex_chan_scan_param_set *scan_chan_list)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
6258c2ecf20Sopenharmony_ci	int ret = 0;
6268c2ecf20Sopenharmony_ci	struct mwifiex_chan_scan_param_set *tmp_chan_list;
6278c2ecf20Sopenharmony_ci	struct mwifiex_chan_scan_param_set *start_chan;
6288c2ecf20Sopenharmony_ci	u32 tlv_idx, rates_size, cmd_no;
6298c2ecf20Sopenharmony_ci	u32 total_scan_time;
6308c2ecf20Sopenharmony_ci	u32 done_early;
6318c2ecf20Sopenharmony_ci	u8 radio_type;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) {
6348c2ecf20Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR,
6358c2ecf20Sopenharmony_ci			    "info: Scan: Null detect: %p, %p, %p\n",
6368c2ecf20Sopenharmony_ci			    scan_cfg_out, chan_tlv_out, scan_chan_list);
6378c2ecf20Sopenharmony_ci		return -1;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* Check csa channel expiry before preparing scan list */
6418c2ecf20Sopenharmony_ci	mwifiex_11h_get_csa_closed_channel(priv);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	/* Set the temp channel struct pointer to the start of the desired
6468c2ecf20Sopenharmony_ci	   list */
6478c2ecf20Sopenharmony_ci	tmp_chan_list = scan_chan_list;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* Loop through the desired channel list, sending a new firmware scan
6508c2ecf20Sopenharmony_ci	   commands for each max_chan_per_scan channels (or for 1,6,11
6518c2ecf20Sopenharmony_ci	   individually if configured accordingly) */
6528c2ecf20Sopenharmony_ci	while (tmp_chan_list->chan_number) {
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci		tlv_idx = 0;
6558c2ecf20Sopenharmony_ci		total_scan_time = 0;
6568c2ecf20Sopenharmony_ci		radio_type = 0;
6578c2ecf20Sopenharmony_ci		chan_tlv_out->header.len = 0;
6588c2ecf20Sopenharmony_ci		start_chan = tmp_chan_list;
6598c2ecf20Sopenharmony_ci		done_early = false;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		/*
6628c2ecf20Sopenharmony_ci		 * Construct the Channel TLV for the scan command.  Continue to
6638c2ecf20Sopenharmony_ci		 * insert channel TLVs until:
6648c2ecf20Sopenharmony_ci		 *   - the tlv_idx hits the maximum configured per scan command
6658c2ecf20Sopenharmony_ci		 *   - the next channel to insert is 0 (end of desired channel
6668c2ecf20Sopenharmony_ci		 *     list)
6678c2ecf20Sopenharmony_ci		 *   - done_early is set (controlling individual scanning of
6688c2ecf20Sopenharmony_ci		 *     1,6,11)
6698c2ecf20Sopenharmony_ci		 */
6708c2ecf20Sopenharmony_ci		while (tlv_idx < max_chan_per_scan &&
6718c2ecf20Sopenharmony_ci		       tmp_chan_list->chan_number && !done_early) {
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci			if (tmp_chan_list->chan_number == priv->csa_chan) {
6748c2ecf20Sopenharmony_ci				tmp_chan_list++;
6758c2ecf20Sopenharmony_ci				continue;
6768c2ecf20Sopenharmony_ci			}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci			radio_type = tmp_chan_list->radio_type;
6798c2ecf20Sopenharmony_ci			mwifiex_dbg(priv->adapter, INFO,
6808c2ecf20Sopenharmony_ci				    "info: Scan: Chan(%3d), Radio(%d),\t"
6818c2ecf20Sopenharmony_ci				    "Mode(%d, %d), Dur(%d)\n",
6828c2ecf20Sopenharmony_ci				    tmp_chan_list->chan_number,
6838c2ecf20Sopenharmony_ci				    tmp_chan_list->radio_type,
6848c2ecf20Sopenharmony_ci				    tmp_chan_list->chan_scan_mode_bitmap
6858c2ecf20Sopenharmony_ci				    & MWIFIEX_PASSIVE_SCAN,
6868c2ecf20Sopenharmony_ci				    (tmp_chan_list->chan_scan_mode_bitmap
6878c2ecf20Sopenharmony_ci				    & MWIFIEX_DISABLE_CHAN_FILT) >> 1,
6888c2ecf20Sopenharmony_ci				    le16_to_cpu(tmp_chan_list->max_scan_time));
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci			/* Copy the current channel TLV to the command being
6918c2ecf20Sopenharmony_ci			   prepared */
6928c2ecf20Sopenharmony_ci			memcpy(chan_tlv_out->chan_scan_param + tlv_idx,
6938c2ecf20Sopenharmony_ci			       tmp_chan_list,
6948c2ecf20Sopenharmony_ci			       sizeof(chan_tlv_out->chan_scan_param));
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci			/* Increment the TLV header length by the size
6978c2ecf20Sopenharmony_ci			   appended */
6988c2ecf20Sopenharmony_ci			le16_unaligned_add_cpu(&chan_tlv_out->header.len,
6998c2ecf20Sopenharmony_ci					       sizeof(
7008c2ecf20Sopenharmony_ci						chan_tlv_out->chan_scan_param));
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci			/*
7038c2ecf20Sopenharmony_ci			 * The tlv buffer length is set to the number of bytes
7048c2ecf20Sopenharmony_ci			 * of the between the channel tlv pointer and the start
7058c2ecf20Sopenharmony_ci			 * of the tlv buffer.  This compensates for any TLVs
7068c2ecf20Sopenharmony_ci			 * that were appended before the channel list.
7078c2ecf20Sopenharmony_ci			 */
7088c2ecf20Sopenharmony_ci			scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out -
7098c2ecf20Sopenharmony_ci							scan_cfg_out->tlv_buf);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci			/* Add the size of the channel tlv header and the data
7128c2ecf20Sopenharmony_ci			   length */
7138c2ecf20Sopenharmony_ci			scan_cfg_out->tlv_buf_len +=
7148c2ecf20Sopenharmony_ci				(sizeof(chan_tlv_out->header)
7158c2ecf20Sopenharmony_ci				 + le16_to_cpu(chan_tlv_out->header.len));
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci			/* Increment the index to the channel tlv we are
7188c2ecf20Sopenharmony_ci			   constructing */
7198c2ecf20Sopenharmony_ci			tlv_idx++;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci			/* Count the total scan time per command */
7228c2ecf20Sopenharmony_ci			total_scan_time +=
7238c2ecf20Sopenharmony_ci				le16_to_cpu(tmp_chan_list->max_scan_time);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci			done_early = false;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci			/* Stop the loop if the *current* channel is in the
7288c2ecf20Sopenharmony_ci			   1,6,11 set and we are not filtering on a BSSID
7298c2ecf20Sopenharmony_ci			   or SSID. */
7308c2ecf20Sopenharmony_ci			if (!filtered_scan &&
7318c2ecf20Sopenharmony_ci			    (tmp_chan_list->chan_number == 1 ||
7328c2ecf20Sopenharmony_ci			     tmp_chan_list->chan_number == 6 ||
7338c2ecf20Sopenharmony_ci			     tmp_chan_list->chan_number == 11))
7348c2ecf20Sopenharmony_ci				done_early = true;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci			/* Increment the tmp pointer to the next channel to
7378c2ecf20Sopenharmony_ci			   be scanned */
7388c2ecf20Sopenharmony_ci			tmp_chan_list++;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci			/* Stop the loop if the *next* channel is in the 1,6,11
7418c2ecf20Sopenharmony_ci			   set.  This will cause it to be the only channel
7428c2ecf20Sopenharmony_ci			   scanned on the next interation */
7438c2ecf20Sopenharmony_ci			if (!filtered_scan &&
7448c2ecf20Sopenharmony_ci			    (tmp_chan_list->chan_number == 1 ||
7458c2ecf20Sopenharmony_ci			     tmp_chan_list->chan_number == 6 ||
7468c2ecf20Sopenharmony_ci			     tmp_chan_list->chan_number == 11))
7478c2ecf20Sopenharmony_ci				done_early = true;
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		/* The total scan time should be less than scan command timeout
7518c2ecf20Sopenharmony_ci		   value */
7528c2ecf20Sopenharmony_ci		if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) {
7538c2ecf20Sopenharmony_ci			mwifiex_dbg(priv->adapter, ERROR,
7548c2ecf20Sopenharmony_ci				    "total scan time %dms\t"
7558c2ecf20Sopenharmony_ci				    "is over limit (%dms), scan skipped\n",
7568c2ecf20Sopenharmony_ci				    total_scan_time,
7578c2ecf20Sopenharmony_ci				    MWIFIEX_MAX_TOTAL_SCAN_TIME);
7588c2ecf20Sopenharmony_ci			ret = -1;
7598c2ecf20Sopenharmony_ci			break;
7608c2ecf20Sopenharmony_ci		}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci		rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out,
7638c2ecf20Sopenharmony_ci						     radio_type);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		priv->adapter->scan_channels = start_chan;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		/* Send the scan command to the firmware with the specified
7688c2ecf20Sopenharmony_ci		   cfg */
7698c2ecf20Sopenharmony_ci		if (priv->adapter->ext_scan)
7708c2ecf20Sopenharmony_ci			cmd_no = HostCmd_CMD_802_11_SCAN_EXT;
7718c2ecf20Sopenharmony_ci		else
7728c2ecf20Sopenharmony_ci			cmd_no = HostCmd_CMD_802_11_SCAN;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET,
7758c2ecf20Sopenharmony_ci				       0, scan_cfg_out, false);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci		/* rate IE is updated per scan command but same starting
7788c2ecf20Sopenharmony_ci		 * pointer is used each time so that rate IE from earlier
7798c2ecf20Sopenharmony_ci		 * scan_cfg_out->buf is overwritten with new one.
7808c2ecf20Sopenharmony_ci		 */
7818c2ecf20Sopenharmony_ci		scan_cfg_out->tlv_buf_len -=
7828c2ecf20Sopenharmony_ci			    sizeof(struct mwifiex_ie_types_header) + rates_size;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		if (ret) {
7858c2ecf20Sopenharmony_ci			mwifiex_cancel_pending_scan_cmd(adapter);
7868c2ecf20Sopenharmony_ci			break;
7878c2ecf20Sopenharmony_ci		}
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	if (ret)
7918c2ecf20Sopenharmony_ci		return -1;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	return 0;
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/*
7978c2ecf20Sopenharmony_ci * This function constructs a scan command configuration structure to use
7988c2ecf20Sopenharmony_ci * in scan commands.
7998c2ecf20Sopenharmony_ci *
8008c2ecf20Sopenharmony_ci * Application layer or other functions can invoke network scanning
8018c2ecf20Sopenharmony_ci * with a scan configuration supplied in a user scan configuration structure.
8028c2ecf20Sopenharmony_ci * This structure is used as the basis of one or many scan command configuration
8038c2ecf20Sopenharmony_ci * commands that are sent to the command processing module and eventually to the
8048c2ecf20Sopenharmony_ci * firmware.
8058c2ecf20Sopenharmony_ci *
8068c2ecf20Sopenharmony_ci * This function creates a scan command configuration structure  based on the
8078c2ecf20Sopenharmony_ci * following user supplied parameters (if present):
8088c2ecf20Sopenharmony_ci *      - SSID filter
8098c2ecf20Sopenharmony_ci *      - BSSID filter
8108c2ecf20Sopenharmony_ci *      - Number of Probes to be sent
8118c2ecf20Sopenharmony_ci *      - Channel list
8128c2ecf20Sopenharmony_ci *
8138c2ecf20Sopenharmony_ci * If the SSID or BSSID filter is not present, the filter is disabled/cleared.
8148c2ecf20Sopenharmony_ci * If the number of probes is not set, adapter default setting is used.
8158c2ecf20Sopenharmony_ci */
8168c2ecf20Sopenharmony_cistatic void
8178c2ecf20Sopenharmony_cimwifiex_config_scan(struct mwifiex_private *priv,
8188c2ecf20Sopenharmony_ci		    const struct mwifiex_user_scan_cfg *user_scan_in,
8198c2ecf20Sopenharmony_ci		    struct mwifiex_scan_cmd_config *scan_cfg_out,
8208c2ecf20Sopenharmony_ci		    struct mwifiex_ie_types_chan_list_param_set **chan_list_out,
8218c2ecf20Sopenharmony_ci		    struct mwifiex_chan_scan_param_set *scan_chan_list,
8228c2ecf20Sopenharmony_ci		    u8 *max_chan_per_scan, u8 *filtered_scan,
8238c2ecf20Sopenharmony_ci		    u8 *scan_current_only)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
8268c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_num_probes *num_probes_tlv;
8278c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv;
8288c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_random_mac *random_mac_tlv;
8298c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
8308c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_bssid_list *bssid_tlv;
8318c2ecf20Sopenharmony_ci	u8 *tlv_pos;
8328c2ecf20Sopenharmony_ci	u32 num_probes;
8338c2ecf20Sopenharmony_ci	u32 ssid_len;
8348c2ecf20Sopenharmony_ci	u32 chan_idx;
8358c2ecf20Sopenharmony_ci	u32 scan_type;
8368c2ecf20Sopenharmony_ci	u16 scan_dur;
8378c2ecf20Sopenharmony_ci	u8 channel;
8388c2ecf20Sopenharmony_ci	u8 radio_type;
8398c2ecf20Sopenharmony_ci	int i;
8408c2ecf20Sopenharmony_ci	u8 ssid_filter;
8418c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_htcap *ht_cap;
8428c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_bss_mode *bss_mode;
8438c2ecf20Sopenharmony_ci	const u8 zero_mac[6] = {0, 0, 0, 0, 0, 0};
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* The tlv_buf_len is calculated for each scan command.  The TLVs added
8468c2ecf20Sopenharmony_ci	   in this routine will be preserved since the routine that sends the
8478c2ecf20Sopenharmony_ci	   command will append channelTLVs at *chan_list_out.  The difference
8488c2ecf20Sopenharmony_ci	   between the *chan_list_out and the tlv_buf start will be used to
8498c2ecf20Sopenharmony_ci	   calculate the size of anything we add in this routine. */
8508c2ecf20Sopenharmony_ci	scan_cfg_out->tlv_buf_len = 0;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* Running tlv pointer.  Assigned to chan_list_out at end of function
8538c2ecf20Sopenharmony_ci	   so later routines know where channels can be added to the command
8548c2ecf20Sopenharmony_ci	   buf */
8558c2ecf20Sopenharmony_ci	tlv_pos = scan_cfg_out->tlv_buf;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	/* Initialize the scan as un-filtered; the flag is later set to TRUE
8588c2ecf20Sopenharmony_ci	   below if a SSID or BSSID filter is sent in the command */
8598c2ecf20Sopenharmony_ci	*filtered_scan = false;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/* Initialize the scan as not being only on the current channel.  If
8628c2ecf20Sopenharmony_ci	   the channel list is customized, only contains one channel, and is
8638c2ecf20Sopenharmony_ci	   the active channel, this is set true and data flow is not halted. */
8648c2ecf20Sopenharmony_ci	*scan_current_only = false;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (user_scan_in) {
8678c2ecf20Sopenharmony_ci		u8 tmpaddr[ETH_ALEN];
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		/* Default the ssid_filter flag to TRUE, set false under
8708c2ecf20Sopenharmony_ci		   certain wildcard conditions and qualified by the existence
8718c2ecf20Sopenharmony_ci		   of an SSID list before marking the scan as filtered */
8728c2ecf20Sopenharmony_ci		ssid_filter = true;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		/* Set the BSS type scan filter, use Adapter setting if
8758c2ecf20Sopenharmony_ci		   unset */
8768c2ecf20Sopenharmony_ci		scan_cfg_out->bss_mode =
8778c2ecf20Sopenharmony_ci			(u8)(user_scan_in->bss_mode ?: adapter->scan_mode);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		/* Set the number of probes to send, use Adapter setting
8808c2ecf20Sopenharmony_ci		   if unset */
8818c2ecf20Sopenharmony_ci		num_probes = user_scan_in->num_probes ?: adapter->scan_probes;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci		/*
8848c2ecf20Sopenharmony_ci		 * Set the BSSID filter to the incoming configuration,
8858c2ecf20Sopenharmony_ci		 * if non-zero.  If not set, it will remain disabled
8868c2ecf20Sopenharmony_ci		 * (all zeros).
8878c2ecf20Sopenharmony_ci		 */
8888c2ecf20Sopenharmony_ci		memcpy(scan_cfg_out->specific_bssid,
8898c2ecf20Sopenharmony_ci		       user_scan_in->specific_bssid,
8908c2ecf20Sopenharmony_ci		       sizeof(scan_cfg_out->specific_bssid));
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci		if (adapter->ext_scan &&
8958c2ecf20Sopenharmony_ci		    !is_zero_ether_addr(tmpaddr)) {
8968c2ecf20Sopenharmony_ci			bssid_tlv =
8978c2ecf20Sopenharmony_ci				(struct mwifiex_ie_types_bssid_list *)tlv_pos;
8988c2ecf20Sopenharmony_ci			bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID);
8998c2ecf20Sopenharmony_ci			bssid_tlv->header.len = cpu_to_le16(ETH_ALEN);
9008c2ecf20Sopenharmony_ci			memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid,
9018c2ecf20Sopenharmony_ci			       ETH_ALEN);
9028c2ecf20Sopenharmony_ci			tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list);
9038c2ecf20Sopenharmony_ci		}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci		for (i = 0; i < user_scan_in->num_ssids; i++) {
9068c2ecf20Sopenharmony_ci			ssid_len = user_scan_in->ssid_list[i].ssid_len;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci			wildcard_ssid_tlv =
9098c2ecf20Sopenharmony_ci				(struct mwifiex_ie_types_wildcard_ssid_params *)
9108c2ecf20Sopenharmony_ci				tlv_pos;
9118c2ecf20Sopenharmony_ci			wildcard_ssid_tlv->header.type =
9128c2ecf20Sopenharmony_ci				cpu_to_le16(TLV_TYPE_WILDCARDSSID);
9138c2ecf20Sopenharmony_ci			wildcard_ssid_tlv->header.len = cpu_to_le16(
9148c2ecf20Sopenharmony_ci				(u16) (ssid_len + sizeof(wildcard_ssid_tlv->
9158c2ecf20Sopenharmony_ci							 max_ssid_length)));
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci			/*
9188c2ecf20Sopenharmony_ci			 * max_ssid_length = 0 tells firmware to perform
9198c2ecf20Sopenharmony_ci			 * specific scan for the SSID filled, whereas
9208c2ecf20Sopenharmony_ci			 * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
9218c2ecf20Sopenharmony_ci			 * wildcard scan.
9228c2ecf20Sopenharmony_ci			 */
9238c2ecf20Sopenharmony_ci			if (ssid_len)
9248c2ecf20Sopenharmony_ci				wildcard_ssid_tlv->max_ssid_length = 0;
9258c2ecf20Sopenharmony_ci			else
9268c2ecf20Sopenharmony_ci				wildcard_ssid_tlv->max_ssid_length =
9278c2ecf20Sopenharmony_ci							IEEE80211_MAX_SSID_LEN;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci			if (!memcmp(user_scan_in->ssid_list[i].ssid,
9308c2ecf20Sopenharmony_ci				    "DIRECT-", 7))
9318c2ecf20Sopenharmony_ci				wildcard_ssid_tlv->max_ssid_length = 0xfe;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci			memcpy(wildcard_ssid_tlv->ssid,
9348c2ecf20Sopenharmony_ci			       user_scan_in->ssid_list[i].ssid, ssid_len);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci			tlv_pos += (sizeof(wildcard_ssid_tlv->header)
9378c2ecf20Sopenharmony_ci				+ le16_to_cpu(wildcard_ssid_tlv->header.len));
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
9408c2ecf20Sopenharmony_ci				    "info: scan: ssid[%d]: %s, %d\n",
9418c2ecf20Sopenharmony_ci				    i, wildcard_ssid_tlv->ssid,
9428c2ecf20Sopenharmony_ci				    wildcard_ssid_tlv->max_ssid_length);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci			/* Empty wildcard ssid with a maxlen will match many or
9458c2ecf20Sopenharmony_ci			   potentially all SSIDs (maxlen == 32), therefore do
9468c2ecf20Sopenharmony_ci			   not treat the scan as
9478c2ecf20Sopenharmony_ci			   filtered. */
9488c2ecf20Sopenharmony_ci			if (!ssid_len && wildcard_ssid_tlv->max_ssid_length)
9498c2ecf20Sopenharmony_ci				ssid_filter = false;
9508c2ecf20Sopenharmony_ci		}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci		/*
9538c2ecf20Sopenharmony_ci		 *  The default number of channels sent in the command is low to
9548c2ecf20Sopenharmony_ci		 *  ensure the response buffer from the firmware does not
9558c2ecf20Sopenharmony_ci		 *  truncate scan results.  That is not an issue with an SSID
9568c2ecf20Sopenharmony_ci		 *  or BSSID filter applied to the scan results in the firmware.
9578c2ecf20Sopenharmony_ci		 */
9588c2ecf20Sopenharmony_ci		memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
9598c2ecf20Sopenharmony_ci		if ((i && ssid_filter) ||
9608c2ecf20Sopenharmony_ci		    !is_zero_ether_addr(tmpaddr))
9618c2ecf20Sopenharmony_ci			*filtered_scan = true;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci		if (user_scan_in->scan_chan_gap) {
9648c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
9658c2ecf20Sopenharmony_ci				    "info: scan: channel gap = %d\n",
9668c2ecf20Sopenharmony_ci				    user_scan_in->scan_chan_gap);
9678c2ecf20Sopenharmony_ci			*max_chan_per_scan =
9688c2ecf20Sopenharmony_ci					MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci			chan_gap_tlv = (void *)tlv_pos;
9718c2ecf20Sopenharmony_ci			chan_gap_tlv->header.type =
9728c2ecf20Sopenharmony_ci					 cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP);
9738c2ecf20Sopenharmony_ci			chan_gap_tlv->header.len =
9748c2ecf20Sopenharmony_ci				    cpu_to_le16(sizeof(chan_gap_tlv->chan_gap));
9758c2ecf20Sopenharmony_ci			chan_gap_tlv->chan_gap =
9768c2ecf20Sopenharmony_ci				     cpu_to_le16((user_scan_in->scan_chan_gap));
9778c2ecf20Sopenharmony_ci			tlv_pos +=
9788c2ecf20Sopenharmony_ci				  sizeof(struct mwifiex_ie_types_scan_chan_gap);
9798c2ecf20Sopenharmony_ci		}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci		if (!ether_addr_equal(user_scan_in->random_mac, zero_mac)) {
9828c2ecf20Sopenharmony_ci			random_mac_tlv = (void *)tlv_pos;
9838c2ecf20Sopenharmony_ci			random_mac_tlv->header.type =
9848c2ecf20Sopenharmony_ci					 cpu_to_le16(TLV_TYPE_RANDOM_MAC);
9858c2ecf20Sopenharmony_ci			random_mac_tlv->header.len =
9868c2ecf20Sopenharmony_ci				    cpu_to_le16(sizeof(random_mac_tlv->mac));
9878c2ecf20Sopenharmony_ci			ether_addr_copy(random_mac_tlv->mac,
9888c2ecf20Sopenharmony_ci					user_scan_in->random_mac);
9898c2ecf20Sopenharmony_ci			tlv_pos +=
9908c2ecf20Sopenharmony_ci				  sizeof(struct mwifiex_ie_types_random_mac);
9918c2ecf20Sopenharmony_ci		}
9928c2ecf20Sopenharmony_ci	} else {
9938c2ecf20Sopenharmony_ci		scan_cfg_out->bss_mode = (u8) adapter->scan_mode;
9948c2ecf20Sopenharmony_ci		num_probes = adapter->scan_probes;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/*
9988c2ecf20Sopenharmony_ci	 *  If a specific BSSID or SSID is used, the number of channels in the
9998c2ecf20Sopenharmony_ci	 *  scan command will be increased to the absolute maximum.
10008c2ecf20Sopenharmony_ci	 */
10018c2ecf20Sopenharmony_ci	if (*filtered_scan) {
10028c2ecf20Sopenharmony_ci		*max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN;
10038c2ecf20Sopenharmony_ci	} else {
10048c2ecf20Sopenharmony_ci		if (!priv->media_connected)
10058c2ecf20Sopenharmony_ci			*max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD;
10068c2ecf20Sopenharmony_ci		else
10078c2ecf20Sopenharmony_ci			*max_chan_per_scan =
10088c2ecf20Sopenharmony_ci					MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD / 2;
10098c2ecf20Sopenharmony_ci	}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if (adapter->ext_scan) {
10128c2ecf20Sopenharmony_ci		bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos;
10138c2ecf20Sopenharmony_ci		bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE);
10148c2ecf20Sopenharmony_ci		bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode));
10158c2ecf20Sopenharmony_ci		bss_mode->bss_mode = scan_cfg_out->bss_mode;
10168c2ecf20Sopenharmony_ci		tlv_pos += sizeof(bss_mode->header) +
10178c2ecf20Sopenharmony_ci			   le16_to_cpu(bss_mode->header.len);
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	/* If the input config or adapter has the number of Probes set,
10218c2ecf20Sopenharmony_ci	   add tlv */
10228c2ecf20Sopenharmony_ci	if (num_probes) {
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
10258c2ecf20Sopenharmony_ci			    "info: scan: num_probes = %d\n",
10268c2ecf20Sopenharmony_ci			    num_probes);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos;
10298c2ecf20Sopenharmony_ci		num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
10308c2ecf20Sopenharmony_ci		num_probes_tlv->header.len =
10318c2ecf20Sopenharmony_ci			cpu_to_le16(sizeof(num_probes_tlv->num_probes));
10328c2ecf20Sopenharmony_ci		num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		tlv_pos += sizeof(num_probes_tlv->header) +
10358c2ecf20Sopenharmony_ci			le16_to_cpu(num_probes_tlv->header.len);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
10408c2ecf20Sopenharmony_ci	    (priv->adapter->config_bands & BAND_GN ||
10418c2ecf20Sopenharmony_ci	     priv->adapter->config_bands & BAND_AN)) {
10428c2ecf20Sopenharmony_ci		ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos;
10438c2ecf20Sopenharmony_ci		memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));
10448c2ecf20Sopenharmony_ci		ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
10458c2ecf20Sopenharmony_ci		ht_cap->header.len =
10468c2ecf20Sopenharmony_ci				cpu_to_le16(sizeof(struct ieee80211_ht_cap));
10478c2ecf20Sopenharmony_ci		radio_type =
10488c2ecf20Sopenharmony_ci			mwifiex_band_to_radio_type(priv->adapter->config_bands);
10498c2ecf20Sopenharmony_ci		mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
10508c2ecf20Sopenharmony_ci		tlv_pos += sizeof(struct mwifiex_ie_types_htcap);
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	/* Append vendor specific IE TLV */
10548c2ecf20Sopenharmony_ci	mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	/*
10578c2ecf20Sopenharmony_ci	 * Set the output for the channel TLV to the address in the tlv buffer
10588c2ecf20Sopenharmony_ci	 *   past any TLVs that were added in this function (SSID, num_probes).
10598c2ecf20Sopenharmony_ci	 *   Channel TLVs will be added past this for each scan command,
10608c2ecf20Sopenharmony_ci	 *   preserving the TLVs that were previously added.
10618c2ecf20Sopenharmony_ci	 */
10628c2ecf20Sopenharmony_ci	*chan_list_out =
10638c2ecf20Sopenharmony_ci		(struct mwifiex_ie_types_chan_list_param_set *) tlv_pos;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (user_scan_in && user_scan_in->chan_list[0].chan_number) {
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
10688c2ecf20Sopenharmony_ci			    "info: Scan: Using supplied channel list\n");
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		for (chan_idx = 0;
10718c2ecf20Sopenharmony_ci		     chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX &&
10728c2ecf20Sopenharmony_ci		     user_scan_in->chan_list[chan_idx].chan_number;
10738c2ecf20Sopenharmony_ci		     chan_idx++) {
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci			channel = user_scan_in->chan_list[chan_idx].chan_number;
10768c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].chan_number = channel;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci			radio_type =
10798c2ecf20Sopenharmony_ci				user_scan_in->chan_list[chan_idx].radio_type;
10808c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].radio_type = radio_type;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci			scan_type = user_scan_in->chan_list[chan_idx].scan_type;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci			if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
10858c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].chan_scan_mode_bitmap
10868c2ecf20Sopenharmony_ci					|= (MWIFIEX_PASSIVE_SCAN |
10878c2ecf20Sopenharmony_ci					    MWIFIEX_HIDDEN_SSID_REPORT);
10888c2ecf20Sopenharmony_ci			else
10898c2ecf20Sopenharmony_ci				scan_chan_list[chan_idx].chan_scan_mode_bitmap
10908c2ecf20Sopenharmony_ci					&= ~MWIFIEX_PASSIVE_SCAN;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].chan_scan_mode_bitmap
10938c2ecf20Sopenharmony_ci				|= MWIFIEX_DISABLE_CHAN_FILT;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci			if (user_scan_in->chan_list[chan_idx].scan_time) {
10968c2ecf20Sopenharmony_ci				scan_dur = (u16) user_scan_in->
10978c2ecf20Sopenharmony_ci					chan_list[chan_idx].scan_time;
10988c2ecf20Sopenharmony_ci			} else {
10998c2ecf20Sopenharmony_ci				if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
11008c2ecf20Sopenharmony_ci					scan_dur = adapter->passive_scan_time;
11018c2ecf20Sopenharmony_ci				else if (*filtered_scan)
11028c2ecf20Sopenharmony_ci					scan_dur = adapter->specific_scan_time;
11038c2ecf20Sopenharmony_ci				else
11048c2ecf20Sopenharmony_ci					scan_dur = adapter->active_scan_time;
11058c2ecf20Sopenharmony_ci			}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].min_scan_time =
11088c2ecf20Sopenharmony_ci				cpu_to_le16(scan_dur);
11098c2ecf20Sopenharmony_ci			scan_chan_list[chan_idx].max_scan_time =
11108c2ecf20Sopenharmony_ci				cpu_to_le16(scan_dur);
11118c2ecf20Sopenharmony_ci		}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci		/* Check if we are only scanning the current channel */
11148c2ecf20Sopenharmony_ci		if ((chan_idx == 1) &&
11158c2ecf20Sopenharmony_ci		    (user_scan_in->chan_list[0].chan_number ==
11168c2ecf20Sopenharmony_ci		     priv->curr_bss_params.bss_descriptor.channel)) {
11178c2ecf20Sopenharmony_ci			*scan_current_only = true;
11188c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
11198c2ecf20Sopenharmony_ci				    "info: Scan: Scanning current channel only\n");
11208c2ecf20Sopenharmony_ci		}
11218c2ecf20Sopenharmony_ci	} else {
11228c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
11238c2ecf20Sopenharmony_ci			    "info: Scan: Creating full region channel list\n");
11248c2ecf20Sopenharmony_ci		mwifiex_scan_create_channel_list(priv, user_scan_in,
11258c2ecf20Sopenharmony_ci						 scan_chan_list,
11268c2ecf20Sopenharmony_ci						 *filtered_scan);
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci/*
11328c2ecf20Sopenharmony_ci * This function inspects the scan response buffer for pointers to
11338c2ecf20Sopenharmony_ci * expected TLVs.
11348c2ecf20Sopenharmony_ci *
11358c2ecf20Sopenharmony_ci * TLVs can be included at the end of the scan response BSS information.
11368c2ecf20Sopenharmony_ci *
11378c2ecf20Sopenharmony_ci * Data in the buffer is parsed pointers to TLVs that can potentially
11388c2ecf20Sopenharmony_ci * be passed back in the response.
11398c2ecf20Sopenharmony_ci */
11408c2ecf20Sopenharmony_cistatic void
11418c2ecf20Sopenharmony_cimwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter,
11428c2ecf20Sopenharmony_ci				     struct mwifiex_ie_types_data *tlv,
11438c2ecf20Sopenharmony_ci				     u32 tlv_buf_size, u32 req_tlv_type,
11448c2ecf20Sopenharmony_ci				     struct mwifiex_ie_types_data **tlv_data)
11458c2ecf20Sopenharmony_ci{
11468c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_data *current_tlv;
11478c2ecf20Sopenharmony_ci	u32 tlv_buf_left;
11488c2ecf20Sopenharmony_ci	u32 tlv_type;
11498c2ecf20Sopenharmony_ci	u32 tlv_len;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	current_tlv = tlv;
11528c2ecf20Sopenharmony_ci	tlv_buf_left = tlv_buf_size;
11538c2ecf20Sopenharmony_ci	*tlv_data = NULL;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
11568c2ecf20Sopenharmony_ci		    "info: SCAN_RESP: tlv_buf_size = %d\n",
11578c2ecf20Sopenharmony_ci		    tlv_buf_size);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) {
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci		tlv_type = le16_to_cpu(current_tlv->header.type);
11628c2ecf20Sopenharmony_ci		tlv_len = le16_to_cpu(current_tlv->header.len);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci		if (sizeof(tlv->header) + tlv_len > tlv_buf_left) {
11658c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
11668c2ecf20Sopenharmony_ci				    "SCAN_RESP: TLV buffer corrupt\n");
11678c2ecf20Sopenharmony_ci			break;
11688c2ecf20Sopenharmony_ci		}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		if (req_tlv_type == tlv_type) {
11718c2ecf20Sopenharmony_ci			switch (tlv_type) {
11728c2ecf20Sopenharmony_ci			case TLV_TYPE_TSFTIMESTAMP:
11738c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
11748c2ecf20Sopenharmony_ci					    "info: SCAN_RESP: TSF\t"
11758c2ecf20Sopenharmony_ci					    "timestamp TLV, len = %d\n",
11768c2ecf20Sopenharmony_ci					    tlv_len);
11778c2ecf20Sopenharmony_ci				*tlv_data = current_tlv;
11788c2ecf20Sopenharmony_ci				break;
11798c2ecf20Sopenharmony_ci			case TLV_TYPE_CHANNELBANDLIST:
11808c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
11818c2ecf20Sopenharmony_ci					    "info: SCAN_RESP: channel\t"
11828c2ecf20Sopenharmony_ci					    "band list TLV, len = %d\n",
11838c2ecf20Sopenharmony_ci					    tlv_len);
11848c2ecf20Sopenharmony_ci				*tlv_data = current_tlv;
11858c2ecf20Sopenharmony_ci				break;
11868c2ecf20Sopenharmony_ci			default:
11878c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, ERROR,
11888c2ecf20Sopenharmony_ci					    "SCAN_RESP: unhandled TLV = %d\n",
11898c2ecf20Sopenharmony_ci					    tlv_type);
11908c2ecf20Sopenharmony_ci				/* Give up, this seems corrupted */
11918c2ecf20Sopenharmony_ci				return;
11928c2ecf20Sopenharmony_ci			}
11938c2ecf20Sopenharmony_ci		}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		if (*tlv_data)
11968c2ecf20Sopenharmony_ci			break;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci		tlv_buf_left -= (sizeof(tlv->header) + tlv_len);
12008c2ecf20Sopenharmony_ci		current_tlv =
12018c2ecf20Sopenharmony_ci			(struct mwifiex_ie_types_data *) (current_tlv->data +
12028c2ecf20Sopenharmony_ci							  tlv_len);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	}			/* while */
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci/*
12088c2ecf20Sopenharmony_ci * This function parses provided beacon buffer and updates
12098c2ecf20Sopenharmony_ci * respective fields in bss descriptor structure.
12108c2ecf20Sopenharmony_ci */
12118c2ecf20Sopenharmony_ciint mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
12128c2ecf20Sopenharmony_ci				    struct mwifiex_bssdescriptor *bss_entry)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	int ret = 0;
12158c2ecf20Sopenharmony_ci	u8 element_id;
12168c2ecf20Sopenharmony_ci	struct ieee_types_fh_param_set *fh_param_set;
12178c2ecf20Sopenharmony_ci	struct ieee_types_ds_param_set *ds_param_set;
12188c2ecf20Sopenharmony_ci	struct ieee_types_cf_param_set *cf_param_set;
12198c2ecf20Sopenharmony_ci	struct ieee_types_ibss_param_set *ibss_param_set;
12208c2ecf20Sopenharmony_ci	u8 *current_ptr;
12218c2ecf20Sopenharmony_ci	u8 *rate;
12228c2ecf20Sopenharmony_ci	u8 element_len;
12238c2ecf20Sopenharmony_ci	u16 total_ie_len;
12248c2ecf20Sopenharmony_ci	u8 bytes_to_copy;
12258c2ecf20Sopenharmony_ci	u8 rate_size;
12268c2ecf20Sopenharmony_ci	u8 found_data_rate_ie;
12278c2ecf20Sopenharmony_ci	u32 bytes_left;
12288c2ecf20Sopenharmony_ci	struct ieee_types_vendor_specific *vendor_ie;
12298c2ecf20Sopenharmony_ci	const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
12308c2ecf20Sopenharmony_ci	const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	found_data_rate_ie = false;
12338c2ecf20Sopenharmony_ci	rate_size = 0;
12348c2ecf20Sopenharmony_ci	current_ptr = bss_entry->beacon_buf;
12358c2ecf20Sopenharmony_ci	bytes_left = bss_entry->beacon_buf_size;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* Process variable IE */
12388c2ecf20Sopenharmony_ci	while (bytes_left >= 2) {
12398c2ecf20Sopenharmony_ci		element_id = *current_ptr;
12408c2ecf20Sopenharmony_ci		element_len = *(current_ptr + 1);
12418c2ecf20Sopenharmony_ci		total_ie_len = element_len + sizeof(struct ieee_types_header);
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci		if (bytes_left < total_ie_len) {
12448c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
12458c2ecf20Sopenharmony_ci				    "err: InterpretIE: in processing\t"
12468c2ecf20Sopenharmony_ci				    "IE, bytes left < IE length\n");
12478c2ecf20Sopenharmony_ci			return -EINVAL;
12488c2ecf20Sopenharmony_ci		}
12498c2ecf20Sopenharmony_ci		switch (element_id) {
12508c2ecf20Sopenharmony_ci		case WLAN_EID_SSID:
12518c2ecf20Sopenharmony_ci			if (element_len > IEEE80211_MAX_SSID_LEN)
12528c2ecf20Sopenharmony_ci				return -EINVAL;
12538c2ecf20Sopenharmony_ci			bss_entry->ssid.ssid_len = element_len;
12548c2ecf20Sopenharmony_ci			memcpy(bss_entry->ssid.ssid, (current_ptr + 2),
12558c2ecf20Sopenharmony_ci			       element_len);
12568c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
12578c2ecf20Sopenharmony_ci				    "info: InterpretIE: ssid: %-32s\n",
12588c2ecf20Sopenharmony_ci				    bss_entry->ssid.ssid);
12598c2ecf20Sopenharmony_ci			break;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci		case WLAN_EID_SUPP_RATES:
12628c2ecf20Sopenharmony_ci			if (element_len > MWIFIEX_SUPPORTED_RATES)
12638c2ecf20Sopenharmony_ci				return -EINVAL;
12648c2ecf20Sopenharmony_ci			memcpy(bss_entry->data_rates, current_ptr + 2,
12658c2ecf20Sopenharmony_ci			       element_len);
12668c2ecf20Sopenharmony_ci			memcpy(bss_entry->supported_rates, current_ptr + 2,
12678c2ecf20Sopenharmony_ci			       element_len);
12688c2ecf20Sopenharmony_ci			rate_size = element_len;
12698c2ecf20Sopenharmony_ci			found_data_rate_ie = true;
12708c2ecf20Sopenharmony_ci			break;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci		case WLAN_EID_FH_PARAMS:
12738c2ecf20Sopenharmony_ci			if (total_ie_len < sizeof(*fh_param_set))
12748c2ecf20Sopenharmony_ci				return -EINVAL;
12758c2ecf20Sopenharmony_ci			fh_param_set =
12768c2ecf20Sopenharmony_ci				(struct ieee_types_fh_param_set *) current_ptr;
12778c2ecf20Sopenharmony_ci			memcpy(&bss_entry->phy_param_set.fh_param_set,
12788c2ecf20Sopenharmony_ci			       fh_param_set,
12798c2ecf20Sopenharmony_ci			       sizeof(struct ieee_types_fh_param_set));
12808c2ecf20Sopenharmony_ci			break;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci		case WLAN_EID_DS_PARAMS:
12838c2ecf20Sopenharmony_ci			if (total_ie_len < sizeof(*ds_param_set))
12848c2ecf20Sopenharmony_ci				return -EINVAL;
12858c2ecf20Sopenharmony_ci			ds_param_set =
12868c2ecf20Sopenharmony_ci				(struct ieee_types_ds_param_set *) current_ptr;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci			bss_entry->channel = ds_param_set->current_chan;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci			memcpy(&bss_entry->phy_param_set.ds_param_set,
12918c2ecf20Sopenharmony_ci			       ds_param_set,
12928c2ecf20Sopenharmony_ci			       sizeof(struct ieee_types_ds_param_set));
12938c2ecf20Sopenharmony_ci			break;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci		case WLAN_EID_CF_PARAMS:
12968c2ecf20Sopenharmony_ci			if (total_ie_len < sizeof(*cf_param_set))
12978c2ecf20Sopenharmony_ci				return -EINVAL;
12988c2ecf20Sopenharmony_ci			cf_param_set =
12998c2ecf20Sopenharmony_ci				(struct ieee_types_cf_param_set *) current_ptr;
13008c2ecf20Sopenharmony_ci			memcpy(&bss_entry->ss_param_set.cf_param_set,
13018c2ecf20Sopenharmony_ci			       cf_param_set,
13028c2ecf20Sopenharmony_ci			       sizeof(struct ieee_types_cf_param_set));
13038c2ecf20Sopenharmony_ci			break;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci		case WLAN_EID_IBSS_PARAMS:
13068c2ecf20Sopenharmony_ci			if (total_ie_len < sizeof(*ibss_param_set))
13078c2ecf20Sopenharmony_ci				return -EINVAL;
13088c2ecf20Sopenharmony_ci			ibss_param_set =
13098c2ecf20Sopenharmony_ci				(struct ieee_types_ibss_param_set *)
13108c2ecf20Sopenharmony_ci				current_ptr;
13118c2ecf20Sopenharmony_ci			memcpy(&bss_entry->ss_param_set.ibss_param_set,
13128c2ecf20Sopenharmony_ci			       ibss_param_set,
13138c2ecf20Sopenharmony_ci			       sizeof(struct ieee_types_ibss_param_set));
13148c2ecf20Sopenharmony_ci			break;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci		case WLAN_EID_ERP_INFO:
13178c2ecf20Sopenharmony_ci			if (!element_len)
13188c2ecf20Sopenharmony_ci				return -EINVAL;
13198c2ecf20Sopenharmony_ci			bss_entry->erp_flags = *(current_ptr + 2);
13208c2ecf20Sopenharmony_ci			break;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci		case WLAN_EID_PWR_CONSTRAINT:
13238c2ecf20Sopenharmony_ci			if (!element_len)
13248c2ecf20Sopenharmony_ci				return -EINVAL;
13258c2ecf20Sopenharmony_ci			bss_entry->local_constraint = *(current_ptr + 2);
13268c2ecf20Sopenharmony_ci			bss_entry->sensed_11h = true;
13278c2ecf20Sopenharmony_ci			break;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci		case WLAN_EID_CHANNEL_SWITCH:
13308c2ecf20Sopenharmony_ci			bss_entry->chan_sw_ie_present = true;
13318c2ecf20Sopenharmony_ci			fallthrough;
13328c2ecf20Sopenharmony_ci		case WLAN_EID_PWR_CAPABILITY:
13338c2ecf20Sopenharmony_ci		case WLAN_EID_TPC_REPORT:
13348c2ecf20Sopenharmony_ci		case WLAN_EID_QUIET:
13358c2ecf20Sopenharmony_ci			bss_entry->sensed_11h = true;
13368c2ecf20Sopenharmony_ci		    break;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci		case WLAN_EID_EXT_SUPP_RATES:
13398c2ecf20Sopenharmony_ci			/*
13408c2ecf20Sopenharmony_ci			 * Only process extended supported rate
13418c2ecf20Sopenharmony_ci			 * if data rate is already found.
13428c2ecf20Sopenharmony_ci			 * Data rate IE should come before
13438c2ecf20Sopenharmony_ci			 * extended supported rate IE
13448c2ecf20Sopenharmony_ci			 */
13458c2ecf20Sopenharmony_ci			if (found_data_rate_ie) {
13468c2ecf20Sopenharmony_ci				if ((element_len + rate_size) >
13478c2ecf20Sopenharmony_ci				    MWIFIEX_SUPPORTED_RATES)
13488c2ecf20Sopenharmony_ci					bytes_to_copy =
13498c2ecf20Sopenharmony_ci						(MWIFIEX_SUPPORTED_RATES -
13508c2ecf20Sopenharmony_ci						 rate_size);
13518c2ecf20Sopenharmony_ci				else
13528c2ecf20Sopenharmony_ci					bytes_to_copy = element_len;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci				rate = (u8 *) bss_entry->data_rates;
13558c2ecf20Sopenharmony_ci				rate += rate_size;
13568c2ecf20Sopenharmony_ci				memcpy(rate, current_ptr + 2, bytes_to_copy);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci				rate = (u8 *) bss_entry->supported_rates;
13598c2ecf20Sopenharmony_ci				rate += rate_size;
13608c2ecf20Sopenharmony_ci				memcpy(rate, current_ptr + 2, bytes_to_copy);
13618c2ecf20Sopenharmony_ci			}
13628c2ecf20Sopenharmony_ci			break;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci		case WLAN_EID_VENDOR_SPECIFIC:
13658c2ecf20Sopenharmony_ci			vendor_ie = (struct ieee_types_vendor_specific *)
13668c2ecf20Sopenharmony_ci					current_ptr;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci			/* 802.11 requires at least 3-byte OUI. */
13698c2ecf20Sopenharmony_ci			if (element_len < sizeof(vendor_ie->vend_hdr.oui.oui))
13708c2ecf20Sopenharmony_ci				return -EINVAL;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci			/* Not long enough for a match? Skip it. */
13738c2ecf20Sopenharmony_ci			if (element_len < sizeof(wpa_oui))
13748c2ecf20Sopenharmony_ci				break;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci			if (!memcmp(&vendor_ie->vend_hdr.oui, wpa_oui,
13778c2ecf20Sopenharmony_ci				    sizeof(wpa_oui))) {
13788c2ecf20Sopenharmony_ci				bss_entry->bcn_wpa_ie =
13798c2ecf20Sopenharmony_ci					(struct ieee_types_vendor_specific *)
13808c2ecf20Sopenharmony_ci					current_ptr;
13818c2ecf20Sopenharmony_ci				bss_entry->wpa_offset = (u16)
13828c2ecf20Sopenharmony_ci					(current_ptr - bss_entry->beacon_buf);
13838c2ecf20Sopenharmony_ci			} else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui,
13848c2ecf20Sopenharmony_ci				    sizeof(wmm_oui))) {
13858c2ecf20Sopenharmony_ci				if (total_ie_len ==
13868c2ecf20Sopenharmony_ci				    sizeof(struct ieee_types_wmm_parameter) ||
13878c2ecf20Sopenharmony_ci				    total_ie_len ==
13888c2ecf20Sopenharmony_ci				    sizeof(struct ieee_types_wmm_info))
13898c2ecf20Sopenharmony_ci					/*
13908c2ecf20Sopenharmony_ci					 * Only accept and copy the WMM IE if
13918c2ecf20Sopenharmony_ci					 * it matches the size expected for the
13928c2ecf20Sopenharmony_ci					 * WMM Info IE or the WMM Parameter IE.
13938c2ecf20Sopenharmony_ci					 */
13948c2ecf20Sopenharmony_ci					memcpy((u8 *) &bss_entry->wmm_ie,
13958c2ecf20Sopenharmony_ci					       current_ptr, total_ie_len);
13968c2ecf20Sopenharmony_ci			}
13978c2ecf20Sopenharmony_ci			break;
13988c2ecf20Sopenharmony_ci		case WLAN_EID_RSN:
13998c2ecf20Sopenharmony_ci			bss_entry->bcn_rsn_ie =
14008c2ecf20Sopenharmony_ci				(struct ieee_types_generic *) current_ptr;
14018c2ecf20Sopenharmony_ci			bss_entry->rsn_offset = (u16) (current_ptr -
14028c2ecf20Sopenharmony_ci							bss_entry->beacon_buf);
14038c2ecf20Sopenharmony_ci			break;
14048c2ecf20Sopenharmony_ci		case WLAN_EID_BSS_AC_ACCESS_DELAY:
14058c2ecf20Sopenharmony_ci			bss_entry->bcn_wapi_ie =
14068c2ecf20Sopenharmony_ci				(struct ieee_types_generic *) current_ptr;
14078c2ecf20Sopenharmony_ci			bss_entry->wapi_offset = (u16) (current_ptr -
14088c2ecf20Sopenharmony_ci							bss_entry->beacon_buf);
14098c2ecf20Sopenharmony_ci			break;
14108c2ecf20Sopenharmony_ci		case WLAN_EID_HT_CAPABILITY:
14118c2ecf20Sopenharmony_ci			bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *)
14128c2ecf20Sopenharmony_ci					(current_ptr +
14138c2ecf20Sopenharmony_ci					sizeof(struct ieee_types_header));
14148c2ecf20Sopenharmony_ci			bss_entry->ht_cap_offset = (u16) (current_ptr +
14158c2ecf20Sopenharmony_ci					sizeof(struct ieee_types_header) -
14168c2ecf20Sopenharmony_ci					bss_entry->beacon_buf);
14178c2ecf20Sopenharmony_ci			break;
14188c2ecf20Sopenharmony_ci		case WLAN_EID_HT_OPERATION:
14198c2ecf20Sopenharmony_ci			bss_entry->bcn_ht_oper =
14208c2ecf20Sopenharmony_ci				(struct ieee80211_ht_operation *)(current_ptr +
14218c2ecf20Sopenharmony_ci					sizeof(struct ieee_types_header));
14228c2ecf20Sopenharmony_ci			bss_entry->ht_info_offset = (u16) (current_ptr +
14238c2ecf20Sopenharmony_ci					sizeof(struct ieee_types_header) -
14248c2ecf20Sopenharmony_ci					bss_entry->beacon_buf);
14258c2ecf20Sopenharmony_ci			break;
14268c2ecf20Sopenharmony_ci		case WLAN_EID_VHT_CAPABILITY:
14278c2ecf20Sopenharmony_ci			bss_entry->disable_11ac = false;
14288c2ecf20Sopenharmony_ci			bss_entry->bcn_vht_cap =
14298c2ecf20Sopenharmony_ci				(void *)(current_ptr +
14308c2ecf20Sopenharmony_ci					 sizeof(struct ieee_types_header));
14318c2ecf20Sopenharmony_ci			bss_entry->vht_cap_offset =
14328c2ecf20Sopenharmony_ci					(u16)((u8 *)bss_entry->bcn_vht_cap -
14338c2ecf20Sopenharmony_ci					      bss_entry->beacon_buf);
14348c2ecf20Sopenharmony_ci			break;
14358c2ecf20Sopenharmony_ci		case WLAN_EID_VHT_OPERATION:
14368c2ecf20Sopenharmony_ci			bss_entry->bcn_vht_oper =
14378c2ecf20Sopenharmony_ci				(void *)(current_ptr +
14388c2ecf20Sopenharmony_ci					 sizeof(struct ieee_types_header));
14398c2ecf20Sopenharmony_ci			bss_entry->vht_info_offset =
14408c2ecf20Sopenharmony_ci					(u16)((u8 *)bss_entry->bcn_vht_oper -
14418c2ecf20Sopenharmony_ci					      bss_entry->beacon_buf);
14428c2ecf20Sopenharmony_ci			break;
14438c2ecf20Sopenharmony_ci		case WLAN_EID_BSS_COEX_2040:
14448c2ecf20Sopenharmony_ci			bss_entry->bcn_bss_co_2040 = current_ptr;
14458c2ecf20Sopenharmony_ci			bss_entry->bss_co_2040_offset =
14468c2ecf20Sopenharmony_ci				(u16) (current_ptr - bss_entry->beacon_buf);
14478c2ecf20Sopenharmony_ci			break;
14488c2ecf20Sopenharmony_ci		case WLAN_EID_EXT_CAPABILITY:
14498c2ecf20Sopenharmony_ci			bss_entry->bcn_ext_cap = current_ptr;
14508c2ecf20Sopenharmony_ci			bss_entry->ext_cap_offset =
14518c2ecf20Sopenharmony_ci				(u16) (current_ptr - bss_entry->beacon_buf);
14528c2ecf20Sopenharmony_ci			break;
14538c2ecf20Sopenharmony_ci		case WLAN_EID_OPMODE_NOTIF:
14548c2ecf20Sopenharmony_ci			bss_entry->oper_mode = (void *)current_ptr;
14558c2ecf20Sopenharmony_ci			bss_entry->oper_mode_offset =
14568c2ecf20Sopenharmony_ci					(u16)((u8 *)bss_entry->oper_mode -
14578c2ecf20Sopenharmony_ci					      bss_entry->beacon_buf);
14588c2ecf20Sopenharmony_ci			break;
14598c2ecf20Sopenharmony_ci		default:
14608c2ecf20Sopenharmony_ci			break;
14618c2ecf20Sopenharmony_ci		}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci		current_ptr += total_ie_len;
14648c2ecf20Sopenharmony_ci		bytes_left -= total_ie_len;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	}	/* while (bytes_left > 2) */
14678c2ecf20Sopenharmony_ci	return ret;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/*
14718c2ecf20Sopenharmony_ci * This function converts radio type scan parameter to a band configuration
14728c2ecf20Sopenharmony_ci * to be used in join command.
14738c2ecf20Sopenharmony_ci */
14748c2ecf20Sopenharmony_cistatic u8
14758c2ecf20Sopenharmony_cimwifiex_radio_type_to_band(u8 radio_type)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	switch (radio_type) {
14788c2ecf20Sopenharmony_ci	case HostCmd_SCAN_RADIO_TYPE_A:
14798c2ecf20Sopenharmony_ci		return BAND_A;
14808c2ecf20Sopenharmony_ci	case HostCmd_SCAN_RADIO_TYPE_BG:
14818c2ecf20Sopenharmony_ci	default:
14828c2ecf20Sopenharmony_ci		return BAND_G;
14838c2ecf20Sopenharmony_ci	}
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci/*
14878c2ecf20Sopenharmony_ci * This is an internal function used to start a scan based on an input
14888c2ecf20Sopenharmony_ci * configuration.
14898c2ecf20Sopenharmony_ci *
14908c2ecf20Sopenharmony_ci * This uses the input user scan configuration information when provided in
14918c2ecf20Sopenharmony_ci * order to send the appropriate scan commands to firmware to populate or
14928c2ecf20Sopenharmony_ci * update the internal driver scan table.
14938c2ecf20Sopenharmony_ci */
14948c2ecf20Sopenharmony_ciint mwifiex_scan_networks(struct mwifiex_private *priv,
14958c2ecf20Sopenharmony_ci			  const struct mwifiex_user_scan_cfg *user_scan_in)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	int ret;
14988c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
14998c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
15008c2ecf20Sopenharmony_ci	union mwifiex_scan_cmd_config_tlv *scan_cfg_out;
15018c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_chan_list_param_set *chan_list_out;
15028c2ecf20Sopenharmony_ci	struct mwifiex_chan_scan_param_set *scan_chan_list;
15038c2ecf20Sopenharmony_ci	u8 filtered_scan;
15048c2ecf20Sopenharmony_ci	u8 scan_current_chan_only;
15058c2ecf20Sopenharmony_ci	u8 max_chan_per_scan;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	if (adapter->scan_processing) {
15088c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, WARN,
15098c2ecf20Sopenharmony_ci			    "cmd: Scan already in process...\n");
15108c2ecf20Sopenharmony_ci		return -EBUSY;
15118c2ecf20Sopenharmony_ci	}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	if (priv->scan_block) {
15148c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, WARN,
15158c2ecf20Sopenharmony_ci			    "cmd: Scan is blocked during association...\n");
15168c2ecf20Sopenharmony_ci		return -EBUSY;
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags) ||
15208c2ecf20Sopenharmony_ci	    test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
15218c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
15228c2ecf20Sopenharmony_ci			    "Ignore scan. Card removed or firmware in bad state\n");
15238c2ecf20Sopenharmony_ci		return -EFAULT;
15248c2ecf20Sopenharmony_ci	}
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->mwifiex_cmd_lock);
15278c2ecf20Sopenharmony_ci	adapter->scan_processing = true;
15288c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->mwifiex_cmd_lock);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv),
15318c2ecf20Sopenharmony_ci			       GFP_KERNEL);
15328c2ecf20Sopenharmony_ci	if (!scan_cfg_out) {
15338c2ecf20Sopenharmony_ci		ret = -ENOMEM;
15348c2ecf20Sopenharmony_ci		goto done;
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX,
15388c2ecf20Sopenharmony_ci				 sizeof(struct mwifiex_chan_scan_param_set),
15398c2ecf20Sopenharmony_ci				 GFP_KERNEL);
15408c2ecf20Sopenharmony_ci	if (!scan_chan_list) {
15418c2ecf20Sopenharmony_ci		kfree(scan_cfg_out);
15428c2ecf20Sopenharmony_ci		ret = -ENOMEM;
15438c2ecf20Sopenharmony_ci		goto done;
15448c2ecf20Sopenharmony_ci	}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config,
15478c2ecf20Sopenharmony_ci			    &chan_list_out, scan_chan_list, &max_chan_per_scan,
15488c2ecf20Sopenharmony_ci			    &filtered_scan, &scan_current_chan_only);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan,
15518c2ecf20Sopenharmony_ci					&scan_cfg_out->config, chan_list_out,
15528c2ecf20Sopenharmony_ci					scan_chan_list);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/* Get scan command from scan_pending_q and put to cmd_pending_q */
15558c2ecf20Sopenharmony_ci	if (!ret) {
15568c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->scan_pending_q_lock);
15578c2ecf20Sopenharmony_ci		if (!list_empty(&adapter->scan_pending_q)) {
15588c2ecf20Sopenharmony_ci			cmd_node = list_first_entry(&adapter->scan_pending_q,
15598c2ecf20Sopenharmony_ci						    struct cmd_ctrl_node, list);
15608c2ecf20Sopenharmony_ci			list_del(&cmd_node->list);
15618c2ecf20Sopenharmony_ci			spin_unlock_bh(&adapter->scan_pending_q_lock);
15628c2ecf20Sopenharmony_ci			mwifiex_insert_cmd_to_pending_q(adapter, cmd_node);
15638c2ecf20Sopenharmony_ci			queue_work(adapter->workqueue, &adapter->main_work);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci			/* Perform internal scan synchronously */
15668c2ecf20Sopenharmony_ci			if (!priv->scan_request) {
15678c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
15688c2ecf20Sopenharmony_ci					    "wait internal scan\n");
15698c2ecf20Sopenharmony_ci				mwifiex_wait_queue_complete(adapter, cmd_node);
15708c2ecf20Sopenharmony_ci			}
15718c2ecf20Sopenharmony_ci		} else {
15728c2ecf20Sopenharmony_ci			spin_unlock_bh(&adapter->scan_pending_q_lock);
15738c2ecf20Sopenharmony_ci		}
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	kfree(scan_cfg_out);
15778c2ecf20Sopenharmony_ci	kfree(scan_chan_list);
15788c2ecf20Sopenharmony_cidone:
15798c2ecf20Sopenharmony_ci	if (ret) {
15808c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
15818c2ecf20Sopenharmony_ci		adapter->scan_processing = false;
15828c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
15838c2ecf20Sopenharmony_ci	}
15848c2ecf20Sopenharmony_ci	return ret;
15858c2ecf20Sopenharmony_ci}
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci/*
15888c2ecf20Sopenharmony_ci * This function prepares a scan command to be sent to the firmware.
15898c2ecf20Sopenharmony_ci *
15908c2ecf20Sopenharmony_ci * This uses the scan command configuration sent to the command processing
15918c2ecf20Sopenharmony_ci * module in command preparation stage to configure a scan command structure
15928c2ecf20Sopenharmony_ci * to send to firmware.
15938c2ecf20Sopenharmony_ci *
15948c2ecf20Sopenharmony_ci * The fixed fields specifying the BSS type and BSSID filters as well as a
15958c2ecf20Sopenharmony_ci * variable number/length of TLVs are sent in the command to firmware.
15968c2ecf20Sopenharmony_ci *
15978c2ecf20Sopenharmony_ci * Preparation also includes -
15988c2ecf20Sopenharmony_ci *      - Setting command ID, and proper size
15998c2ecf20Sopenharmony_ci *      - Ensuring correct endian-ness
16008c2ecf20Sopenharmony_ci */
16018c2ecf20Sopenharmony_ciint mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
16028c2ecf20Sopenharmony_ci			    struct mwifiex_scan_cmd_config *scan_cfg)
16038c2ecf20Sopenharmony_ci{
16048c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	/* Set fixed field variables in scan command */
16078c2ecf20Sopenharmony_ci	scan_cmd->bss_mode = scan_cfg->bss_mode;
16088c2ecf20Sopenharmony_ci	memcpy(scan_cmd->bssid, scan_cfg->specific_bssid,
16098c2ecf20Sopenharmony_ci	       sizeof(scan_cmd->bssid));
16108c2ecf20Sopenharmony_ci	memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
16158c2ecf20Sopenharmony_ci	cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode)
16168c2ecf20Sopenharmony_ci					  + sizeof(scan_cmd->bssid)
16178c2ecf20Sopenharmony_ci					  + scan_cfg->tlv_buf_len + S_DS_GEN));
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	return 0;
16208c2ecf20Sopenharmony_ci}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci/*
16238c2ecf20Sopenharmony_ci * This function checks compatibility of requested network with current
16248c2ecf20Sopenharmony_ci * driver settings.
16258c2ecf20Sopenharmony_ci */
16268c2ecf20Sopenharmony_ciint mwifiex_check_network_compatibility(struct mwifiex_private *priv,
16278c2ecf20Sopenharmony_ci					struct mwifiex_bssdescriptor *bss_desc)
16288c2ecf20Sopenharmony_ci{
16298c2ecf20Sopenharmony_ci	int ret = -1;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	if (!bss_desc)
16328c2ecf20Sopenharmony_ci		return -1;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band,
16358c2ecf20Sopenharmony_ci			     (u16) bss_desc->channel, 0))) {
16368c2ecf20Sopenharmony_ci		switch (priv->bss_mode) {
16378c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_STATION:
16388c2ecf20Sopenharmony_ci		case NL80211_IFTYPE_ADHOC:
16398c2ecf20Sopenharmony_ci			ret = mwifiex_is_network_compatible(priv, bss_desc,
16408c2ecf20Sopenharmony_ci							    priv->bss_mode);
16418c2ecf20Sopenharmony_ci			if (ret)
16428c2ecf20Sopenharmony_ci				mwifiex_dbg(priv->adapter, ERROR,
16438c2ecf20Sopenharmony_ci					    "Incompatible network settings\n");
16448c2ecf20Sopenharmony_ci			break;
16458c2ecf20Sopenharmony_ci		default:
16468c2ecf20Sopenharmony_ci			ret = 0;
16478c2ecf20Sopenharmony_ci		}
16488c2ecf20Sopenharmony_ci	}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	return ret;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci/* This function checks if SSID string contains all zeroes or length is zero */
16548c2ecf20Sopenharmony_cistatic bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid)
16558c2ecf20Sopenharmony_ci{
16568c2ecf20Sopenharmony_ci	int idx;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	for (idx = 0; idx < ssid->ssid_len; idx++) {
16598c2ecf20Sopenharmony_ci		if (ssid->ssid[idx])
16608c2ecf20Sopenharmony_ci			return false;
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	return true;
16648c2ecf20Sopenharmony_ci}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci/* This function checks if any hidden SSID found in passive scan channels
16678c2ecf20Sopenharmony_ci * and save those channels for specific SSID active scan
16688c2ecf20Sopenharmony_ci */
16698c2ecf20Sopenharmony_cistatic int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv,
16708c2ecf20Sopenharmony_ci					     struct cfg80211_bss *bss)
16718c2ecf20Sopenharmony_ci{
16728c2ecf20Sopenharmony_ci	struct mwifiex_bssdescriptor *bss_desc;
16738c2ecf20Sopenharmony_ci	int ret;
16748c2ecf20Sopenharmony_ci	int chid;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	/* Allocate and fill new bss descriptor */
16778c2ecf20Sopenharmony_ci	bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL);
16788c2ecf20Sopenharmony_ci	if (!bss_desc)
16798c2ecf20Sopenharmony_ci		return -ENOMEM;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
16828c2ecf20Sopenharmony_ci	if (ret)
16838c2ecf20Sopenharmony_ci		goto done;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) {
16868c2ecf20Sopenharmony_ci		mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n");
16878c2ecf20Sopenharmony_ci		for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) {
16888c2ecf20Sopenharmony_ci			if (priv->hidden_chan[chid].chan_number ==
16898c2ecf20Sopenharmony_ci			    bss->channel->hw_value)
16908c2ecf20Sopenharmony_ci				break;
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci			if (!priv->hidden_chan[chid].chan_number) {
16938c2ecf20Sopenharmony_ci				priv->hidden_chan[chid].chan_number =
16948c2ecf20Sopenharmony_ci					bss->channel->hw_value;
16958c2ecf20Sopenharmony_ci				priv->hidden_chan[chid].radio_type =
16968c2ecf20Sopenharmony_ci					bss->channel->band;
16978c2ecf20Sopenharmony_ci				priv->hidden_chan[chid].scan_type =
16988c2ecf20Sopenharmony_ci					MWIFIEX_SCAN_TYPE_ACTIVE;
16998c2ecf20Sopenharmony_ci				break;
17008c2ecf20Sopenharmony_ci			}
17018c2ecf20Sopenharmony_ci		}
17028c2ecf20Sopenharmony_ci	}
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_cidone:
17058c2ecf20Sopenharmony_ci	/* beacon_ie buffer was allocated in function
17068c2ecf20Sopenharmony_ci	 * mwifiex_fill_new_bss_desc(). Free it now.
17078c2ecf20Sopenharmony_ci	 */
17088c2ecf20Sopenharmony_ci	kfree(bss_desc->beacon_buf);
17098c2ecf20Sopenharmony_ci	kfree(bss_desc);
17108c2ecf20Sopenharmony_ci	return 0;
17118c2ecf20Sopenharmony_ci}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic int mwifiex_update_curr_bss_params(struct mwifiex_private *priv,
17148c2ecf20Sopenharmony_ci					  struct cfg80211_bss *bss)
17158c2ecf20Sopenharmony_ci{
17168c2ecf20Sopenharmony_ci	struct mwifiex_bssdescriptor *bss_desc;
17178c2ecf20Sopenharmony_ci	int ret;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	/* Allocate and fill new bss descriptor */
17208c2ecf20Sopenharmony_ci	bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL);
17218c2ecf20Sopenharmony_ci	if (!bss_desc)
17228c2ecf20Sopenharmony_ci		return -ENOMEM;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
17258c2ecf20Sopenharmony_ci	if (ret)
17268c2ecf20Sopenharmony_ci		goto done;
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	ret = mwifiex_check_network_compatibility(priv, bss_desc);
17298c2ecf20Sopenharmony_ci	if (ret)
17308c2ecf20Sopenharmony_ci		goto done;
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	spin_lock_bh(&priv->curr_bcn_buf_lock);
17338c2ecf20Sopenharmony_ci	/* Make a copy of current BSSID descriptor */
17348c2ecf20Sopenharmony_ci	memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc,
17358c2ecf20Sopenharmony_ci	       sizeof(priv->curr_bss_params.bss_descriptor));
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	/* The contents of beacon_ie will be copied to its own buffer
17388c2ecf20Sopenharmony_ci	 * in mwifiex_save_curr_bcn()
17398c2ecf20Sopenharmony_ci	 */
17408c2ecf20Sopenharmony_ci	mwifiex_save_curr_bcn(priv);
17418c2ecf20Sopenharmony_ci	spin_unlock_bh(&priv->curr_bcn_buf_lock);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_cidone:
17448c2ecf20Sopenharmony_ci	/* beacon_ie buffer was allocated in function
17458c2ecf20Sopenharmony_ci	 * mwifiex_fill_new_bss_desc(). Free it now.
17468c2ecf20Sopenharmony_ci	 */
17478c2ecf20Sopenharmony_ci	kfree(bss_desc->beacon_buf);
17488c2ecf20Sopenharmony_ci	kfree(bss_desc);
17498c2ecf20Sopenharmony_ci	return 0;
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic int
17538c2ecf20Sopenharmony_cimwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
17548c2ecf20Sopenharmony_ci				  u32 *bytes_left, u64 fw_tsf, u8 *radio_type,
17558c2ecf20Sopenharmony_ci				  bool ext_scan, s32 rssi_val)
17568c2ecf20Sopenharmony_ci{
17578c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
17588c2ecf20Sopenharmony_ci	struct mwifiex_chan_freq_power *cfp;
17598c2ecf20Sopenharmony_ci	struct cfg80211_bss *bss;
17608c2ecf20Sopenharmony_ci	u8 bssid[ETH_ALEN];
17618c2ecf20Sopenharmony_ci	s32 rssi;
17628c2ecf20Sopenharmony_ci	const u8 *ie_buf;
17638c2ecf20Sopenharmony_ci	size_t ie_len;
17648c2ecf20Sopenharmony_ci	u16 channel = 0;
17658c2ecf20Sopenharmony_ci	u16 beacon_size = 0;
17668c2ecf20Sopenharmony_ci	u32 curr_bcn_bytes;
17678c2ecf20Sopenharmony_ci	u32 freq;
17688c2ecf20Sopenharmony_ci	u16 beacon_period;
17698c2ecf20Sopenharmony_ci	u16 cap_info_bitmap;
17708c2ecf20Sopenharmony_ci	u8 *current_ptr;
17718c2ecf20Sopenharmony_ci	u64 timestamp;
17728c2ecf20Sopenharmony_ci	struct mwifiex_fixed_bcn_param *bcn_param;
17738c2ecf20Sopenharmony_ci	struct mwifiex_bss_priv *bss_priv;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	if (*bytes_left >= sizeof(beacon_size)) {
17768c2ecf20Sopenharmony_ci		/* Extract & convert beacon size from command buffer */
17778c2ecf20Sopenharmony_ci		beacon_size = get_unaligned_le16((*bss_info));
17788c2ecf20Sopenharmony_ci		*bytes_left -= sizeof(beacon_size);
17798c2ecf20Sopenharmony_ci		*bss_info += sizeof(beacon_size);
17808c2ecf20Sopenharmony_ci	}
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	if (!beacon_size || beacon_size > *bytes_left) {
17838c2ecf20Sopenharmony_ci		*bss_info += *bytes_left;
17848c2ecf20Sopenharmony_ci		*bytes_left = 0;
17858c2ecf20Sopenharmony_ci		return -EFAULT;
17868c2ecf20Sopenharmony_ci	}
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	/* Initialize the current working beacon pointer for this BSS
17898c2ecf20Sopenharmony_ci	 * iteration
17908c2ecf20Sopenharmony_ci	 */
17918c2ecf20Sopenharmony_ci	current_ptr = *bss_info;
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci	/* Advance the return beacon pointer past the current beacon */
17948c2ecf20Sopenharmony_ci	*bss_info += beacon_size;
17958c2ecf20Sopenharmony_ci	*bytes_left -= beacon_size;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	curr_bcn_bytes = beacon_size;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	/* First 5 fields are bssid, RSSI(for legacy scan only),
18008c2ecf20Sopenharmony_ci	 * time stamp, beacon interval, and capability information
18018c2ecf20Sopenharmony_ci	 */
18028c2ecf20Sopenharmony_ci	if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) +
18038c2ecf20Sopenharmony_ci	    sizeof(struct mwifiex_fixed_bcn_param)) {
18048c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
18058c2ecf20Sopenharmony_ci			    "InterpretIE: not enough bytes left\n");
18068c2ecf20Sopenharmony_ci		return -EFAULT;
18078c2ecf20Sopenharmony_ci	}
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	memcpy(bssid, current_ptr, ETH_ALEN);
18108c2ecf20Sopenharmony_ci	current_ptr += ETH_ALEN;
18118c2ecf20Sopenharmony_ci	curr_bcn_bytes -= ETH_ALEN;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	if (!ext_scan) {
18148c2ecf20Sopenharmony_ci		rssi = (s32) *current_ptr;
18158c2ecf20Sopenharmony_ci		rssi = (-rssi) * 100;		/* Convert dBm to mBm */
18168c2ecf20Sopenharmony_ci		current_ptr += sizeof(u8);
18178c2ecf20Sopenharmony_ci		curr_bcn_bytes -= sizeof(u8);
18188c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
18198c2ecf20Sopenharmony_ci			    "info: InterpretIE: RSSI=%d\n", rssi);
18208c2ecf20Sopenharmony_ci	} else {
18218c2ecf20Sopenharmony_ci		rssi = rssi_val;
18228c2ecf20Sopenharmony_ci	}
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr;
18258c2ecf20Sopenharmony_ci	current_ptr += sizeof(*bcn_param);
18268c2ecf20Sopenharmony_ci	curr_bcn_bytes -= sizeof(*bcn_param);
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	timestamp = le64_to_cpu(bcn_param->timestamp);
18298c2ecf20Sopenharmony_ci	beacon_period = le16_to_cpu(bcn_param->beacon_period);
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
18328c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
18338c2ecf20Sopenharmony_ci		    "info: InterpretIE: capabilities=0x%X\n",
18348c2ecf20Sopenharmony_ci		    cap_info_bitmap);
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	/* Rest of the current buffer are IE's */
18378c2ecf20Sopenharmony_ci	ie_buf = current_ptr;
18388c2ecf20Sopenharmony_ci	ie_len = curr_bcn_bytes;
18398c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
18408c2ecf20Sopenharmony_ci		    "info: InterpretIE: IELength for this AP = %d\n",
18418c2ecf20Sopenharmony_ci		    curr_bcn_bytes);
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
18448c2ecf20Sopenharmony_ci		u8 element_id, element_len;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci		element_id = *current_ptr;
18478c2ecf20Sopenharmony_ci		element_len = *(current_ptr + 1);
18488c2ecf20Sopenharmony_ci		if (curr_bcn_bytes < element_len +
18498c2ecf20Sopenharmony_ci				sizeof(struct ieee_types_header)) {
18508c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
18518c2ecf20Sopenharmony_ci				    "%s: bytes left < IE length\n", __func__);
18528c2ecf20Sopenharmony_ci			return -EFAULT;
18538c2ecf20Sopenharmony_ci		}
18548c2ecf20Sopenharmony_ci		if (element_id == WLAN_EID_DS_PARAMS) {
18558c2ecf20Sopenharmony_ci			channel = *(current_ptr +
18568c2ecf20Sopenharmony_ci				    sizeof(struct ieee_types_header));
18578c2ecf20Sopenharmony_ci			break;
18588c2ecf20Sopenharmony_ci		}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci		current_ptr += element_len + sizeof(struct ieee_types_header);
18618c2ecf20Sopenharmony_ci		curr_bcn_bytes -= element_len +
18628c2ecf20Sopenharmony_ci					sizeof(struct ieee_types_header);
18638c2ecf20Sopenharmony_ci	}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	if (channel) {
18668c2ecf20Sopenharmony_ci		struct ieee80211_channel *chan;
18678c2ecf20Sopenharmony_ci		u8 band;
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci		/* Skip entry if on csa closed channel */
18708c2ecf20Sopenharmony_ci		if (channel == priv->csa_chan) {
18718c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, WARN,
18728c2ecf20Sopenharmony_ci				    "Dropping entry on csa closed channel\n");
18738c2ecf20Sopenharmony_ci			return 0;
18748c2ecf20Sopenharmony_ci		}
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci		band = BAND_G;
18778c2ecf20Sopenharmony_ci		if (radio_type)
18788c2ecf20Sopenharmony_ci			band = mwifiex_radio_type_to_band(*radio_type &
18798c2ecf20Sopenharmony_ci							  (BIT(0) | BIT(1)));
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci		cfp = mwifiex_get_cfp(priv, band, channel, 0);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci		freq = cfp ? cfp->freq : 0;
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci		chan = ieee80211_get_channel(priv->wdev.wiphy, freq);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci		if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
18888c2ecf20Sopenharmony_ci			bss = cfg80211_inform_bss(priv->wdev.wiphy,
18898c2ecf20Sopenharmony_ci					    chan, CFG80211_BSS_FTYPE_UNKNOWN,
18908c2ecf20Sopenharmony_ci					    bssid, timestamp,
18918c2ecf20Sopenharmony_ci					    cap_info_bitmap, beacon_period,
18928c2ecf20Sopenharmony_ci					    ie_buf, ie_len, rssi, GFP_ATOMIC);
18938c2ecf20Sopenharmony_ci			if (bss) {
18948c2ecf20Sopenharmony_ci				bss_priv = (struct mwifiex_bss_priv *)bss->priv;
18958c2ecf20Sopenharmony_ci				bss_priv->band = band;
18968c2ecf20Sopenharmony_ci				bss_priv->fw_tsf = fw_tsf;
18978c2ecf20Sopenharmony_ci				if (priv->media_connected &&
18988c2ecf20Sopenharmony_ci				    !memcmp(bssid, priv->curr_bss_params.
18998c2ecf20Sopenharmony_ci					    bss_descriptor.mac_address,
19008c2ecf20Sopenharmony_ci					    ETH_ALEN))
19018c2ecf20Sopenharmony_ci					mwifiex_update_curr_bss_params(priv,
19028c2ecf20Sopenharmony_ci								       bss);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci				if ((chan->flags & IEEE80211_CHAN_RADAR) ||
19058c2ecf20Sopenharmony_ci				    (chan->flags & IEEE80211_CHAN_NO_IR)) {
19068c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
19078c2ecf20Sopenharmony_ci						    "radar or passive channel %d\n",
19088c2ecf20Sopenharmony_ci						    channel);
19098c2ecf20Sopenharmony_ci					mwifiex_save_hidden_ssid_channels(priv,
19108c2ecf20Sopenharmony_ci									  bss);
19118c2ecf20Sopenharmony_ci				}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci				cfg80211_put_bss(priv->wdev.wiphy, bss);
19148c2ecf20Sopenharmony_ci			}
19158c2ecf20Sopenharmony_ci		}
19168c2ecf20Sopenharmony_ci	} else {
19178c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n");
19188c2ecf20Sopenharmony_ci	}
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	return 0;
19218c2ecf20Sopenharmony_ci}
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_cistatic void mwifiex_complete_scan(struct mwifiex_private *priv)
19248c2ecf20Sopenharmony_ci{
19258c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	adapter->survey_idx = 0;
19288c2ecf20Sopenharmony_ci	if (adapter->curr_cmd->wait_q_enabled) {
19298c2ecf20Sopenharmony_ci		adapter->cmd_wait_q.status = 0;
19308c2ecf20Sopenharmony_ci		if (!priv->scan_request) {
19318c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
19328c2ecf20Sopenharmony_ci				    "complete internal scan\n");
19338c2ecf20Sopenharmony_ci			mwifiex_complete_cmd(adapter, adapter->curr_cmd);
19348c2ecf20Sopenharmony_ci		}
19358c2ecf20Sopenharmony_ci	}
19368c2ecf20Sopenharmony_ci}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci/* This function checks if any hidden SSID found in passive scan channels
19398c2ecf20Sopenharmony_ci * and do specific SSID active scan for those channels
19408c2ecf20Sopenharmony_ci */
19418c2ecf20Sopenharmony_cistatic int
19428c2ecf20Sopenharmony_cimwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv)
19438c2ecf20Sopenharmony_ci{
19448c2ecf20Sopenharmony_ci	int ret;
19458c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
19468c2ecf20Sopenharmony_ci	u8 id = 0;
19478c2ecf20Sopenharmony_ci	struct mwifiex_user_scan_cfg  *user_scan_cfg;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	if (adapter->active_scan_triggered || !priv->scan_request ||
19508c2ecf20Sopenharmony_ci	    priv->scan_aborting) {
19518c2ecf20Sopenharmony_ci		adapter->active_scan_triggered = false;
19528c2ecf20Sopenharmony_ci		return 0;
19538c2ecf20Sopenharmony_ci	}
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	if (!priv->hidden_chan[0].chan_number) {
19568c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n");
19578c2ecf20Sopenharmony_ci		return 0;
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci	user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	if (!user_scan_cfg)
19628c2ecf20Sopenharmony_ci		return -ENOMEM;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) {
19658c2ecf20Sopenharmony_ci		if (!priv->hidden_chan[id].chan_number)
19668c2ecf20Sopenharmony_ci			break;
19678c2ecf20Sopenharmony_ci		memcpy(&user_scan_cfg->chan_list[id],
19688c2ecf20Sopenharmony_ci		       &priv->hidden_chan[id],
19698c2ecf20Sopenharmony_ci		       sizeof(struct mwifiex_user_scan_chan));
19708c2ecf20Sopenharmony_ci	}
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	adapter->active_scan_triggered = true;
19738c2ecf20Sopenharmony_ci	if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
19748c2ecf20Sopenharmony_ci		ether_addr_copy(user_scan_cfg->random_mac,
19758c2ecf20Sopenharmony_ci				priv->scan_request->mac_addr);
19768c2ecf20Sopenharmony_ci	user_scan_cfg->num_ssids = priv->scan_request->n_ssids;
19778c2ecf20Sopenharmony_ci	user_scan_cfg->ssid_list = priv->scan_request->ssids;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	ret = mwifiex_scan_networks(priv, user_scan_cfg);
19808c2ecf20Sopenharmony_ci	kfree(user_scan_cfg);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan));
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	if (ret) {
19858c2ecf20Sopenharmony_ci		dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
19868c2ecf20Sopenharmony_ci		return ret;
19878c2ecf20Sopenharmony_ci	}
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	return 0;
19908c2ecf20Sopenharmony_ci}
19918c2ecf20Sopenharmony_cistatic void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
19928c2ecf20Sopenharmony_ci{
19938c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
19948c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->scan_pending_q_lock);
19978c2ecf20Sopenharmony_ci	if (list_empty(&adapter->scan_pending_q)) {
19988c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->scan_pending_q_lock);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
20018c2ecf20Sopenharmony_ci		adapter->scan_processing = false;
20028c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci		mwifiex_active_scan_req_for_passive_chan(priv);
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci		if (!adapter->ext_scan)
20078c2ecf20Sopenharmony_ci			mwifiex_complete_scan(priv);
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci		if (priv->scan_request) {
20108c2ecf20Sopenharmony_ci			struct cfg80211_scan_info info = {
20118c2ecf20Sopenharmony_ci				.aborted = false,
20128c2ecf20Sopenharmony_ci			};
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
20158c2ecf20Sopenharmony_ci				    "info: notifying scan done\n");
20168c2ecf20Sopenharmony_ci			cfg80211_scan_done(priv->scan_request, &info);
20178c2ecf20Sopenharmony_ci			priv->scan_request = NULL;
20188c2ecf20Sopenharmony_ci			priv->scan_aborting = false;
20198c2ecf20Sopenharmony_ci		} else {
20208c2ecf20Sopenharmony_ci			priv->scan_aborting = false;
20218c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
20228c2ecf20Sopenharmony_ci				    "info: scan already aborted\n");
20238c2ecf20Sopenharmony_ci		}
20248c2ecf20Sopenharmony_ci	} else if ((priv->scan_aborting && !priv->scan_request) ||
20258c2ecf20Sopenharmony_ci		   priv->scan_block) {
20268c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->scan_pending_q_lock);
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci		mwifiex_cancel_pending_scan_cmd(adapter);
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
20318c2ecf20Sopenharmony_ci		adapter->scan_processing = false;
20328c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci		if (!adapter->active_scan_triggered) {
20358c2ecf20Sopenharmony_ci			if (priv->scan_request) {
20368c2ecf20Sopenharmony_ci				struct cfg80211_scan_info info = {
20378c2ecf20Sopenharmony_ci					.aborted = true,
20388c2ecf20Sopenharmony_ci				};
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
20418c2ecf20Sopenharmony_ci					    "info: aborting scan\n");
20428c2ecf20Sopenharmony_ci				cfg80211_scan_done(priv->scan_request, &info);
20438c2ecf20Sopenharmony_ci				priv->scan_request = NULL;
20448c2ecf20Sopenharmony_ci				priv->scan_aborting = false;
20458c2ecf20Sopenharmony_ci			} else {
20468c2ecf20Sopenharmony_ci				priv->scan_aborting = false;
20478c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
20488c2ecf20Sopenharmony_ci					    "info: scan already aborted\n");
20498c2ecf20Sopenharmony_ci			}
20508c2ecf20Sopenharmony_ci		}
20518c2ecf20Sopenharmony_ci	} else {
20528c2ecf20Sopenharmony_ci		/* Get scan command from scan_pending_q and put to
20538c2ecf20Sopenharmony_ci		 * cmd_pending_q
20548c2ecf20Sopenharmony_ci		 */
20558c2ecf20Sopenharmony_ci		cmd_node = list_first_entry(&adapter->scan_pending_q,
20568c2ecf20Sopenharmony_ci					    struct cmd_ctrl_node, list);
20578c2ecf20Sopenharmony_ci		list_del(&cmd_node->list);
20588c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->scan_pending_q_lock);
20598c2ecf20Sopenharmony_ci		mwifiex_insert_cmd_to_pending_q(adapter, cmd_node);
20608c2ecf20Sopenharmony_ci	}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	return;
20638c2ecf20Sopenharmony_ci}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_civoid mwifiex_cancel_scan(struct mwifiex_adapter *adapter)
20668c2ecf20Sopenharmony_ci{
20678c2ecf20Sopenharmony_ci	struct mwifiex_private *priv;
20688c2ecf20Sopenharmony_ci	int i;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	mwifiex_cancel_pending_scan_cmd(adapter);
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	if (adapter->scan_processing) {
20738c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
20748c2ecf20Sopenharmony_ci		adapter->scan_processing = false;
20758c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
20768c2ecf20Sopenharmony_ci		for (i = 0; i < adapter->priv_num; i++) {
20778c2ecf20Sopenharmony_ci			priv = adapter->priv[i];
20788c2ecf20Sopenharmony_ci			if (!priv)
20798c2ecf20Sopenharmony_ci				continue;
20808c2ecf20Sopenharmony_ci			if (priv->scan_request) {
20818c2ecf20Sopenharmony_ci				struct cfg80211_scan_info info = {
20828c2ecf20Sopenharmony_ci					.aborted = true,
20838c2ecf20Sopenharmony_ci				};
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
20868c2ecf20Sopenharmony_ci					    "info: aborting scan\n");
20878c2ecf20Sopenharmony_ci				cfg80211_scan_done(priv->scan_request, &info);
20888c2ecf20Sopenharmony_ci				priv->scan_request = NULL;
20898c2ecf20Sopenharmony_ci				priv->scan_aborting = false;
20908c2ecf20Sopenharmony_ci			}
20918c2ecf20Sopenharmony_ci		}
20928c2ecf20Sopenharmony_ci	}
20938c2ecf20Sopenharmony_ci}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci/*
20968c2ecf20Sopenharmony_ci * This function handles the command response of scan.
20978c2ecf20Sopenharmony_ci *
20988c2ecf20Sopenharmony_ci * The response buffer for the scan command has the following
20998c2ecf20Sopenharmony_ci * memory layout:
21008c2ecf20Sopenharmony_ci *
21018c2ecf20Sopenharmony_ci *      .-------------------------------------------------------------.
21028c2ecf20Sopenharmony_ci *      |  Header (4 * sizeof(t_u16)):  Standard command response hdr |
21038c2ecf20Sopenharmony_ci *      .-------------------------------------------------------------.
21048c2ecf20Sopenharmony_ci *      |  BufSize (t_u16) : sizeof the BSS Description data          |
21058c2ecf20Sopenharmony_ci *      .-------------------------------------------------------------.
21068c2ecf20Sopenharmony_ci *      |  NumOfSet (t_u8) : Number of BSS Descs returned             |
21078c2ecf20Sopenharmony_ci *      .-------------------------------------------------------------.
21088c2ecf20Sopenharmony_ci *      |  BSSDescription data (variable, size given in BufSize)      |
21098c2ecf20Sopenharmony_ci *      .-------------------------------------------------------------.
21108c2ecf20Sopenharmony_ci *      |  TLV data (variable, size calculated using Header->Size,    |
21118c2ecf20Sopenharmony_ci *      |            BufSize and sizeof the fixed fields above)       |
21128c2ecf20Sopenharmony_ci *      .-------------------------------------------------------------.
21138c2ecf20Sopenharmony_ci */
21148c2ecf20Sopenharmony_ciint mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
21158c2ecf20Sopenharmony_ci			    struct host_cmd_ds_command *resp)
21168c2ecf20Sopenharmony_ci{
21178c2ecf20Sopenharmony_ci	int ret = 0;
21188c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
21198c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
21208c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_data *tlv_data;
21218c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_tsf_timestamp *tsf_tlv;
21228c2ecf20Sopenharmony_ci	u8 *bss_info;
21238c2ecf20Sopenharmony_ci	u32 scan_resp_size;
21248c2ecf20Sopenharmony_ci	u32 bytes_left;
21258c2ecf20Sopenharmony_ci	u32 idx;
21268c2ecf20Sopenharmony_ci	u32 tlv_buf_size;
21278c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv;
21288c2ecf20Sopenharmony_ci	struct chan_band_param_set *chan_band;
21298c2ecf20Sopenharmony_ci	u8 is_bgscan_resp;
21308c2ecf20Sopenharmony_ci	__le64 fw_tsf = 0;
21318c2ecf20Sopenharmony_ci	u8 *radio_type;
21328c2ecf20Sopenharmony_ci	struct cfg80211_wowlan_nd_match *pmatch;
21338c2ecf20Sopenharmony_ci	struct cfg80211_sched_scan_request *nd_config = NULL;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	is_bgscan_resp = (le16_to_cpu(resp->command)
21368c2ecf20Sopenharmony_ci			  == HostCmd_CMD_802_11_BG_SCAN_QUERY);
21378c2ecf20Sopenharmony_ci	if (is_bgscan_resp)
21388c2ecf20Sopenharmony_ci		scan_rsp = &resp->params.bg_scan_query_resp.scan_resp;
21398c2ecf20Sopenharmony_ci	else
21408c2ecf20Sopenharmony_ci		scan_rsp = &resp->params.scan_resp;
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) {
21448c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
21458c2ecf20Sopenharmony_ci			    "SCAN_RESP: too many AP returned (%d)\n",
21468c2ecf20Sopenharmony_ci			    scan_rsp->number_of_sets);
21478c2ecf20Sopenharmony_ci		ret = -1;
21488c2ecf20Sopenharmony_ci		goto check_next_scan;
21498c2ecf20Sopenharmony_ci	}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci	/* Check csa channel expiry before parsing scan response */
21528c2ecf20Sopenharmony_ci	mwifiex_11h_get_csa_closed_channel(priv);
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
21558c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
21568c2ecf20Sopenharmony_ci		    "info: SCAN_RESP: bss_descript_size %d\n",
21578c2ecf20Sopenharmony_ci		    bytes_left);
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci	scan_resp_size = le16_to_cpu(resp->size);
21608c2ecf20Sopenharmony_ci
21618c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
21628c2ecf20Sopenharmony_ci		    "info: SCAN_RESP: returned %d APs before parsing\n",
21638c2ecf20Sopenharmony_ci		    scan_rsp->number_of_sets);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	bss_info = scan_rsp->bss_desc_and_tlv_buffer;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	/*
21688c2ecf20Sopenharmony_ci	 * The size of the TLV buffer is equal to the entire command response
21698c2ecf20Sopenharmony_ci	 *   size (scan_resp_size) minus the fixed fields (sizeof()'s), the
21708c2ecf20Sopenharmony_ci	 *   BSS Descriptions (bss_descript_size as bytesLef) and the command
21718c2ecf20Sopenharmony_ci	 *   response header (S_DS_GEN)
21728c2ecf20Sopenharmony_ci	 */
21738c2ecf20Sopenharmony_ci	tlv_buf_size = scan_resp_size - (bytes_left
21748c2ecf20Sopenharmony_ci					 + sizeof(scan_rsp->bss_descript_size)
21758c2ecf20Sopenharmony_ci					 + sizeof(scan_rsp->number_of_sets)
21768c2ecf20Sopenharmony_ci					 + S_DS_GEN);
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp->
21798c2ecf20Sopenharmony_ci						 bss_desc_and_tlv_buffer +
21808c2ecf20Sopenharmony_ci						 bytes_left);
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	/* Search the TLV buffer space in the scan response for any valid
21838c2ecf20Sopenharmony_ci	   TLVs */
21848c2ecf20Sopenharmony_ci	mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size,
21858c2ecf20Sopenharmony_ci					     TLV_TYPE_TSFTIMESTAMP,
21868c2ecf20Sopenharmony_ci					     (struct mwifiex_ie_types_data **)
21878c2ecf20Sopenharmony_ci					     &tsf_tlv);
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	/* Search the TLV buffer space in the scan response for any valid
21908c2ecf20Sopenharmony_ci	   TLVs */
21918c2ecf20Sopenharmony_ci	mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size,
21928c2ecf20Sopenharmony_ci					     TLV_TYPE_CHANNELBANDLIST,
21938c2ecf20Sopenharmony_ci					     (struct mwifiex_ie_types_data **)
21948c2ecf20Sopenharmony_ci					     &chan_band_tlv);
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
21978c2ecf20Sopenharmony_ci	if (priv->wdev.wiphy->wowlan_config)
21988c2ecf20Sopenharmony_ci		nd_config = priv->wdev.wiphy->wowlan_config->nd_config;
21998c2ecf20Sopenharmony_ci#endif
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	if (nd_config) {
22028c2ecf20Sopenharmony_ci		adapter->nd_info =
22038c2ecf20Sopenharmony_ci			kzalloc(struct_size(adapter->nd_info, matches,
22048c2ecf20Sopenharmony_ci					    scan_rsp->number_of_sets),
22058c2ecf20Sopenharmony_ci				GFP_ATOMIC);
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci		if (adapter->nd_info)
22088c2ecf20Sopenharmony_ci			adapter->nd_info->n_matches = scan_rsp->number_of_sets;
22098c2ecf20Sopenharmony_ci	}
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
22128c2ecf20Sopenharmony_ci		/*
22138c2ecf20Sopenharmony_ci		 * If the TSF TLV was appended to the scan results, save this
22148c2ecf20Sopenharmony_ci		 * entry's TSF value in the fw_tsf field. It is the firmware's
22158c2ecf20Sopenharmony_ci		 * TSF value at the time the beacon or probe response was
22168c2ecf20Sopenharmony_ci		 * received.
22178c2ecf20Sopenharmony_ci		 */
22188c2ecf20Sopenharmony_ci		if (tsf_tlv)
22198c2ecf20Sopenharmony_ci			memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
22208c2ecf20Sopenharmony_ci			       sizeof(fw_tsf));
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci		if (chan_band_tlv) {
22238c2ecf20Sopenharmony_ci			chan_band = &chan_band_tlv->chan_band_param[idx];
22248c2ecf20Sopenharmony_ci			radio_type = &chan_band->radio_type;
22258c2ecf20Sopenharmony_ci		} else {
22268c2ecf20Sopenharmony_ci			radio_type = NULL;
22278c2ecf20Sopenharmony_ci		}
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci		if (chan_band_tlv && adapter->nd_info) {
22308c2ecf20Sopenharmony_ci			adapter->nd_info->matches[idx] =
22318c2ecf20Sopenharmony_ci				kzalloc(sizeof(*pmatch) + sizeof(u32),
22328c2ecf20Sopenharmony_ci					GFP_ATOMIC);
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci			pmatch = adapter->nd_info->matches[idx];
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci			if (pmatch) {
22378c2ecf20Sopenharmony_ci				pmatch->n_channels = 1;
22388c2ecf20Sopenharmony_ci				pmatch->channels[0] = chan_band->chan_number;
22398c2ecf20Sopenharmony_ci			}
22408c2ecf20Sopenharmony_ci		}
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci		ret = mwifiex_parse_single_response_buf(priv, &bss_info,
22438c2ecf20Sopenharmony_ci							&bytes_left,
22448c2ecf20Sopenharmony_ci							le64_to_cpu(fw_tsf),
22458c2ecf20Sopenharmony_ci							radio_type, false, 0);
22468c2ecf20Sopenharmony_ci		if (ret)
22478c2ecf20Sopenharmony_ci			goto check_next_scan;
22488c2ecf20Sopenharmony_ci	}
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_cicheck_next_scan:
22518c2ecf20Sopenharmony_ci	mwifiex_check_next_scan_command(priv);
22528c2ecf20Sopenharmony_ci	return ret;
22538c2ecf20Sopenharmony_ci}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci/*
22568c2ecf20Sopenharmony_ci * This function prepares an extended scan command to be sent to the firmware
22578c2ecf20Sopenharmony_ci *
22588c2ecf20Sopenharmony_ci * This uses the scan command configuration sent to the command processing
22598c2ecf20Sopenharmony_ci * module in command preparation stage to configure a extended scan command
22608c2ecf20Sopenharmony_ci * structure to send to firmware.
22618c2ecf20Sopenharmony_ci */
22628c2ecf20Sopenharmony_ciint mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
22638c2ecf20Sopenharmony_ci				struct host_cmd_ds_command *cmd,
22648c2ecf20Sopenharmony_ci				void *data_buf)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan;
22678c2ecf20Sopenharmony_ci	struct mwifiex_scan_cmd_config *scan_cfg = data_buf;
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT);
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
22748c2ecf20Sopenharmony_ci	cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved)
22758c2ecf20Sopenharmony_ci				      + scan_cfg->tlv_buf_len + S_DS_GEN));
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	return 0;
22788c2ecf20Sopenharmony_ci}
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci/* This function prepares an background scan config command to be sent
22818c2ecf20Sopenharmony_ci * to the firmware
22828c2ecf20Sopenharmony_ci */
22838c2ecf20Sopenharmony_ciint mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv,
22848c2ecf20Sopenharmony_ci				      struct host_cmd_ds_command *cmd,
22858c2ecf20Sopenharmony_ci				      void *data_buf)
22868c2ecf20Sopenharmony_ci{
22878c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_bg_scan_config *bgscan_config =
22888c2ecf20Sopenharmony_ci					&cmd->params.bg_scan_config;
22898c2ecf20Sopenharmony_ci	struct mwifiex_bg_scan_cfg *bgscan_cfg_in = data_buf;
22908c2ecf20Sopenharmony_ci	u8 *tlv_pos = bgscan_config->tlv;
22918c2ecf20Sopenharmony_ci	u8 num_probes;
22928c2ecf20Sopenharmony_ci	u32 ssid_len, chan_idx, scan_type, scan_dur, chan_num;
22938c2ecf20Sopenharmony_ci	int i;
22948c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_num_probes *num_probes_tlv;
22958c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_repeat_count *repeat_count_tlv;
22968c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_min_rssi_threshold *rssi_threshold_tlv;
22978c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_bgscan_start_later *start_later_tlv;
22988c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
22998c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_chan_list_param_set *chan_list_tlv;
23008c2ecf20Sopenharmony_ci	struct mwifiex_chan_scan_param_set *temp_chan;
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG);
23038c2ecf20Sopenharmony_ci	cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN);
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action);
23068c2ecf20Sopenharmony_ci	bgscan_config->enable = bgscan_cfg_in->enable;
23078c2ecf20Sopenharmony_ci	bgscan_config->bss_type = bgscan_cfg_in->bss_type;
23088c2ecf20Sopenharmony_ci	bgscan_config->scan_interval =
23098c2ecf20Sopenharmony_ci		cpu_to_le32(bgscan_cfg_in->scan_interval);
23108c2ecf20Sopenharmony_ci	bgscan_config->report_condition =
23118c2ecf20Sopenharmony_ci		cpu_to_le32(bgscan_cfg_in->report_condition);
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci	/*  stop sched scan  */
23148c2ecf20Sopenharmony_ci	if (!bgscan_config->enable)
23158c2ecf20Sopenharmony_ci		return 0;
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	num_probes = (bgscan_cfg_in->num_probes ? bgscan_cfg_in->
23208c2ecf20Sopenharmony_ci		      num_probes : priv->adapter->scan_probes);
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_ci	if (num_probes) {
23238c2ecf20Sopenharmony_ci		num_probes_tlv = (struct mwifiex_ie_types_num_probes *)tlv_pos;
23248c2ecf20Sopenharmony_ci		num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
23258c2ecf20Sopenharmony_ci		num_probes_tlv->header.len =
23268c2ecf20Sopenharmony_ci			cpu_to_le16(sizeof(num_probes_tlv->num_probes));
23278c2ecf20Sopenharmony_ci		num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes);
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci		tlv_pos += sizeof(num_probes_tlv->header) +
23308c2ecf20Sopenharmony_ci			le16_to_cpu(num_probes_tlv->header.len);
23318c2ecf20Sopenharmony_ci	}
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	if (bgscan_cfg_in->repeat_count) {
23348c2ecf20Sopenharmony_ci		repeat_count_tlv =
23358c2ecf20Sopenharmony_ci			(struct mwifiex_ie_types_repeat_count *)tlv_pos;
23368c2ecf20Sopenharmony_ci		repeat_count_tlv->header.type =
23378c2ecf20Sopenharmony_ci			cpu_to_le16(TLV_TYPE_REPEAT_COUNT);
23388c2ecf20Sopenharmony_ci		repeat_count_tlv->header.len =
23398c2ecf20Sopenharmony_ci			cpu_to_le16(sizeof(repeat_count_tlv->repeat_count));
23408c2ecf20Sopenharmony_ci		repeat_count_tlv->repeat_count =
23418c2ecf20Sopenharmony_ci			cpu_to_le16(bgscan_cfg_in->repeat_count);
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci		tlv_pos += sizeof(repeat_count_tlv->header) +
23448c2ecf20Sopenharmony_ci			le16_to_cpu(repeat_count_tlv->header.len);
23458c2ecf20Sopenharmony_ci	}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	if (bgscan_cfg_in->rssi_threshold) {
23488c2ecf20Sopenharmony_ci		rssi_threshold_tlv =
23498c2ecf20Sopenharmony_ci			(struct mwifiex_ie_types_min_rssi_threshold *)tlv_pos;
23508c2ecf20Sopenharmony_ci		rssi_threshold_tlv->header.type =
23518c2ecf20Sopenharmony_ci			cpu_to_le16(TLV_TYPE_RSSI_LOW);
23528c2ecf20Sopenharmony_ci		rssi_threshold_tlv->header.len =
23538c2ecf20Sopenharmony_ci			cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold));
23548c2ecf20Sopenharmony_ci		rssi_threshold_tlv->rssi_threshold =
23558c2ecf20Sopenharmony_ci			cpu_to_le16(bgscan_cfg_in->rssi_threshold);
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci		tlv_pos += sizeof(rssi_threshold_tlv->header) +
23588c2ecf20Sopenharmony_ci			le16_to_cpu(rssi_threshold_tlv->header.len);
23598c2ecf20Sopenharmony_ci	}
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	for (i = 0; i < bgscan_cfg_in->num_ssids; i++) {
23628c2ecf20Sopenharmony_ci		ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci		wildcard_ssid_tlv =
23658c2ecf20Sopenharmony_ci			(struct mwifiex_ie_types_wildcard_ssid_params *)tlv_pos;
23668c2ecf20Sopenharmony_ci		wildcard_ssid_tlv->header.type =
23678c2ecf20Sopenharmony_ci				cpu_to_le16(TLV_TYPE_WILDCARDSSID);
23688c2ecf20Sopenharmony_ci		wildcard_ssid_tlv->header.len = cpu_to_le16(
23698c2ecf20Sopenharmony_ci				(u16)(ssid_len + sizeof(wildcard_ssid_tlv->
23708c2ecf20Sopenharmony_ci							 max_ssid_length)));
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci		/* max_ssid_length = 0 tells firmware to perform
23738c2ecf20Sopenharmony_ci		 * specific scan for the SSID filled, whereas
23748c2ecf20Sopenharmony_ci		 * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
23758c2ecf20Sopenharmony_ci		 * wildcard scan.
23768c2ecf20Sopenharmony_ci		 */
23778c2ecf20Sopenharmony_ci		if (ssid_len)
23788c2ecf20Sopenharmony_ci			wildcard_ssid_tlv->max_ssid_length = 0;
23798c2ecf20Sopenharmony_ci		else
23808c2ecf20Sopenharmony_ci			wildcard_ssid_tlv->max_ssid_length =
23818c2ecf20Sopenharmony_ci						IEEE80211_MAX_SSID_LEN;
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci		memcpy(wildcard_ssid_tlv->ssid,
23848c2ecf20Sopenharmony_ci		       bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len);
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci		tlv_pos += (sizeof(wildcard_ssid_tlv->header)
23878c2ecf20Sopenharmony_ci				+ le16_to_cpu(wildcard_ssid_tlv->header.len));
23888c2ecf20Sopenharmony_ci	}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	chan_list_tlv = (struct mwifiex_ie_types_chan_list_param_set *)tlv_pos;
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	if (bgscan_cfg_in->chan_list[0].chan_number) {
23938c2ecf20Sopenharmony_ci		dev_dbg(priv->adapter->dev, "info: bgscan: Using supplied channel list\n");
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci		chan_list_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci		for (chan_idx = 0;
23988c2ecf20Sopenharmony_ci		     chan_idx < MWIFIEX_BG_SCAN_CHAN_MAX &&
23998c2ecf20Sopenharmony_ci		     bgscan_cfg_in->chan_list[chan_idx].chan_number;
24008c2ecf20Sopenharmony_ci		     chan_idx++) {
24018c2ecf20Sopenharmony_ci			temp_chan = chan_list_tlv->chan_scan_param + chan_idx;
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci			/* Increment the TLV header length by size appended */
24048c2ecf20Sopenharmony_ci			le16_unaligned_add_cpu(&chan_list_tlv->header.len,
24058c2ecf20Sopenharmony_ci					       sizeof(
24068c2ecf20Sopenharmony_ci					       chan_list_tlv->chan_scan_param));
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci			temp_chan->chan_number =
24098c2ecf20Sopenharmony_ci				bgscan_cfg_in->chan_list[chan_idx].chan_number;
24108c2ecf20Sopenharmony_ci			temp_chan->radio_type =
24118c2ecf20Sopenharmony_ci				bgscan_cfg_in->chan_list[chan_idx].radio_type;
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci			scan_type =
24148c2ecf20Sopenharmony_ci				bgscan_cfg_in->chan_list[chan_idx].scan_type;
24158c2ecf20Sopenharmony_ci
24168c2ecf20Sopenharmony_ci			if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
24178c2ecf20Sopenharmony_ci				temp_chan->chan_scan_mode_bitmap
24188c2ecf20Sopenharmony_ci					|= MWIFIEX_PASSIVE_SCAN;
24198c2ecf20Sopenharmony_ci			else
24208c2ecf20Sopenharmony_ci				temp_chan->chan_scan_mode_bitmap
24218c2ecf20Sopenharmony_ci					&= ~MWIFIEX_PASSIVE_SCAN;
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_ci			if (bgscan_cfg_in->chan_list[chan_idx].scan_time) {
24248c2ecf20Sopenharmony_ci				scan_dur = (u16)bgscan_cfg_in->
24258c2ecf20Sopenharmony_ci					chan_list[chan_idx].scan_time;
24268c2ecf20Sopenharmony_ci			} else {
24278c2ecf20Sopenharmony_ci				scan_dur = (scan_type ==
24288c2ecf20Sopenharmony_ci					    MWIFIEX_SCAN_TYPE_PASSIVE) ?
24298c2ecf20Sopenharmony_ci					    priv->adapter->passive_scan_time :
24308c2ecf20Sopenharmony_ci					    priv->adapter->specific_scan_time;
24318c2ecf20Sopenharmony_ci			}
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci			temp_chan->min_scan_time = cpu_to_le16(scan_dur);
24348c2ecf20Sopenharmony_ci			temp_chan->max_scan_time = cpu_to_le16(scan_dur);
24358c2ecf20Sopenharmony_ci		}
24368c2ecf20Sopenharmony_ci	} else {
24378c2ecf20Sopenharmony_ci		dev_dbg(priv->adapter->dev,
24388c2ecf20Sopenharmony_ci			"info: bgscan: Creating full region channel list\n");
24398c2ecf20Sopenharmony_ci		chan_num =
24408c2ecf20Sopenharmony_ci			mwifiex_bgscan_create_channel_list(priv, bgscan_cfg_in,
24418c2ecf20Sopenharmony_ci							   chan_list_tlv->
24428c2ecf20Sopenharmony_ci							   chan_scan_param);
24438c2ecf20Sopenharmony_ci		le16_unaligned_add_cpu(&chan_list_tlv->header.len,
24448c2ecf20Sopenharmony_ci				       chan_num *
24458c2ecf20Sopenharmony_ci			     sizeof(chan_list_tlv->chan_scan_param[0]));
24468c2ecf20Sopenharmony_ci	}
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ci	tlv_pos += (sizeof(chan_list_tlv->header)
24498c2ecf20Sopenharmony_ci			+ le16_to_cpu(chan_list_tlv->header.len));
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci	if (bgscan_cfg_in->start_later) {
24528c2ecf20Sopenharmony_ci		start_later_tlv =
24538c2ecf20Sopenharmony_ci			(struct mwifiex_ie_types_bgscan_start_later *)tlv_pos;
24548c2ecf20Sopenharmony_ci		start_later_tlv->header.type =
24558c2ecf20Sopenharmony_ci			cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER);
24568c2ecf20Sopenharmony_ci		start_later_tlv->header.len =
24578c2ecf20Sopenharmony_ci			cpu_to_le16(sizeof(start_later_tlv->start_later));
24588c2ecf20Sopenharmony_ci		start_later_tlv->start_later =
24598c2ecf20Sopenharmony_ci			cpu_to_le16(bgscan_cfg_in->start_later);
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci		tlv_pos += sizeof(start_later_tlv->header) +
24628c2ecf20Sopenharmony_ci			le16_to_cpu(start_later_tlv->header.len);
24638c2ecf20Sopenharmony_ci	}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci	/* Append vendor specific IE TLV */
24668c2ecf20Sopenharmony_ci	mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_BGSCAN, &tlv_pos);
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv);
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci	return 0;
24718c2ecf20Sopenharmony_ci}
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_ciint mwifiex_stop_bg_scan(struct mwifiex_private *priv)
24748c2ecf20Sopenharmony_ci{
24758c2ecf20Sopenharmony_ci	struct mwifiex_bg_scan_cfg *bgscan_cfg;
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci	if (!priv->sched_scanning) {
24788c2ecf20Sopenharmony_ci		dev_dbg(priv->adapter->dev, "bgscan already stopped!\n");
24798c2ecf20Sopenharmony_ci		return 0;
24808c2ecf20Sopenharmony_ci	}
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_ci	bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL);
24838c2ecf20Sopenharmony_ci	if (!bgscan_cfg)
24848c2ecf20Sopenharmony_ci		return -ENOMEM;
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	bgscan_cfg->bss_type = MWIFIEX_BSS_MODE_INFRA;
24878c2ecf20Sopenharmony_ci	bgscan_cfg->action = MWIFIEX_BGSCAN_ACT_SET;
24888c2ecf20Sopenharmony_ci	bgscan_cfg->enable = false;
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_CONFIG,
24918c2ecf20Sopenharmony_ci			     HostCmd_ACT_GEN_SET, 0, bgscan_cfg, true)) {
24928c2ecf20Sopenharmony_ci		kfree(bgscan_cfg);
24938c2ecf20Sopenharmony_ci		return -EFAULT;
24948c2ecf20Sopenharmony_ci	}
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci	kfree(bgscan_cfg);
24978c2ecf20Sopenharmony_ci	priv->sched_scanning = false;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	return 0;
25008c2ecf20Sopenharmony_ci}
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_cistatic void
25038c2ecf20Sopenharmony_cimwifiex_update_chan_statistics(struct mwifiex_private *priv,
25048c2ecf20Sopenharmony_ci			       struct mwifiex_ietypes_chanstats *tlv_stat)
25058c2ecf20Sopenharmony_ci{
25068c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
25078c2ecf20Sopenharmony_ci	u8 i, num_chan;
25088c2ecf20Sopenharmony_ci	struct mwifiex_fw_chan_stats *fw_chan_stats;
25098c2ecf20Sopenharmony_ci	struct mwifiex_chan_stats chan_stats;
25108c2ecf20Sopenharmony_ci
25118c2ecf20Sopenharmony_ci	fw_chan_stats = (void *)((u8 *)tlv_stat +
25128c2ecf20Sopenharmony_ci			      sizeof(struct mwifiex_ie_types_header));
25138c2ecf20Sopenharmony_ci	num_chan = le16_to_cpu(tlv_stat->header.len) /
25148c2ecf20Sopenharmony_ci					      sizeof(struct mwifiex_chan_stats);
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_ci	for (i = 0 ; i < num_chan; i++) {
25178c2ecf20Sopenharmony_ci		if (adapter->survey_idx >= adapter->num_in_chan_stats) {
25188c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, WARN,
25198c2ecf20Sopenharmony_ci				    "FW reported too many channel results (max %d)\n",
25208c2ecf20Sopenharmony_ci				    adapter->num_in_chan_stats);
25218c2ecf20Sopenharmony_ci			return;
25228c2ecf20Sopenharmony_ci		}
25238c2ecf20Sopenharmony_ci		chan_stats.chan_num = fw_chan_stats->chan_num;
25248c2ecf20Sopenharmony_ci		chan_stats.bandcfg = fw_chan_stats->bandcfg;
25258c2ecf20Sopenharmony_ci		chan_stats.flags = fw_chan_stats->flags;
25268c2ecf20Sopenharmony_ci		chan_stats.noise = fw_chan_stats->noise;
25278c2ecf20Sopenharmony_ci		chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss);
25288c2ecf20Sopenharmony_ci		chan_stats.cca_scan_dur =
25298c2ecf20Sopenharmony_ci				       le16_to_cpu(fw_chan_stats->cca_scan_dur);
25308c2ecf20Sopenharmony_ci		chan_stats.cca_busy_dur =
25318c2ecf20Sopenharmony_ci				       le16_to_cpu(fw_chan_stats->cca_busy_dur);
25328c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
25338c2ecf20Sopenharmony_ci			    "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n",
25348c2ecf20Sopenharmony_ci			    chan_stats.chan_num,
25358c2ecf20Sopenharmony_ci			    chan_stats.noise,
25368c2ecf20Sopenharmony_ci			    chan_stats.total_bss,
25378c2ecf20Sopenharmony_ci			    chan_stats.cca_scan_dur,
25388c2ecf20Sopenharmony_ci			    chan_stats.cca_busy_dur);
25398c2ecf20Sopenharmony_ci		memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats,
25408c2ecf20Sopenharmony_ci		       sizeof(struct mwifiex_chan_stats));
25418c2ecf20Sopenharmony_ci		fw_chan_stats++;
25428c2ecf20Sopenharmony_ci	}
25438c2ecf20Sopenharmony_ci}
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_ci/* This function handles the command response of extended scan */
25468c2ecf20Sopenharmony_ciint mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
25478c2ecf20Sopenharmony_ci				struct host_cmd_ds_command *resp)
25488c2ecf20Sopenharmony_ci{
25498c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
25508c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_scan_ext *ext_scan_resp;
25518c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_header *tlv;
25528c2ecf20Sopenharmony_ci	struct mwifiex_ietypes_chanstats *tlv_stat;
25538c2ecf20Sopenharmony_ci	u16 buf_left, type, len;
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *cmd_ptr;
25568c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
25578c2ecf20Sopenharmony_ci	bool complete_scan = false;
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n");
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	ext_scan_resp = &resp->params.ext_scan;
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci	tlv = (void *)ext_scan_resp->tlv_buffer;
25648c2ecf20Sopenharmony_ci	buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN
25658c2ecf20Sopenharmony_ci					      - 1);
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci	while (buf_left >= sizeof(struct mwifiex_ie_types_header)) {
25688c2ecf20Sopenharmony_ci		type = le16_to_cpu(tlv->type);
25698c2ecf20Sopenharmony_ci		len = le16_to_cpu(tlv->len);
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci		if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) {
25728c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
25738c2ecf20Sopenharmony_ci				    "error processing scan response TLVs");
25748c2ecf20Sopenharmony_ci			break;
25758c2ecf20Sopenharmony_ci		}
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci		switch (type) {
25788c2ecf20Sopenharmony_ci		case TLV_TYPE_CHANNEL_STATS:
25798c2ecf20Sopenharmony_ci			tlv_stat = (void *)tlv;
25808c2ecf20Sopenharmony_ci			mwifiex_update_chan_statistics(priv, tlv_stat);
25818c2ecf20Sopenharmony_ci			break;
25828c2ecf20Sopenharmony_ci		default:
25838c2ecf20Sopenharmony_ci			break;
25848c2ecf20Sopenharmony_ci		}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci		buf_left -= len + sizeof(struct mwifiex_ie_types_header);
25878c2ecf20Sopenharmony_ci		tlv = (void *)((u8 *)tlv + len +
25888c2ecf20Sopenharmony_ci			       sizeof(struct mwifiex_ie_types_header));
25898c2ecf20Sopenharmony_ci	}
25908c2ecf20Sopenharmony_ci
25918c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->cmd_pending_q_lock);
25928c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->scan_pending_q_lock);
25938c2ecf20Sopenharmony_ci	if (list_empty(&adapter->scan_pending_q)) {
25948c2ecf20Sopenharmony_ci		complete_scan = true;
25958c2ecf20Sopenharmony_ci		list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) {
25968c2ecf20Sopenharmony_ci			cmd_ptr = (void *)cmd_node->cmd_skb->data;
25978c2ecf20Sopenharmony_ci			if (le16_to_cpu(cmd_ptr->command) ==
25988c2ecf20Sopenharmony_ci			    HostCmd_CMD_802_11_SCAN_EXT) {
25998c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
26008c2ecf20Sopenharmony_ci					    "Scan pending in command pending list");
26018c2ecf20Sopenharmony_ci				complete_scan = false;
26028c2ecf20Sopenharmony_ci				break;
26038c2ecf20Sopenharmony_ci			}
26048c2ecf20Sopenharmony_ci		}
26058c2ecf20Sopenharmony_ci	}
26068c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->scan_pending_q_lock);
26078c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_pending_q_lock);
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	if (complete_scan)
26108c2ecf20Sopenharmony_ci		mwifiex_complete_scan(priv);
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	return 0;
26138c2ecf20Sopenharmony_ci}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci/* This function This function handles the event extended scan report. It
26168c2ecf20Sopenharmony_ci * parses extended scan results and informs to cfg80211 stack.
26178c2ecf20Sopenharmony_ci */
26188c2ecf20Sopenharmony_ciint mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
26198c2ecf20Sopenharmony_ci					 void *buf)
26208c2ecf20Sopenharmony_ci{
26218c2ecf20Sopenharmony_ci	int ret = 0;
26228c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
26238c2ecf20Sopenharmony_ci	u8 *bss_info;
26248c2ecf20Sopenharmony_ci	u32 bytes_left, bytes_left_for_tlv, idx;
26258c2ecf20Sopenharmony_ci	u16 type, len;
26268c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_data *tlv;
26278c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv;
26288c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_bss_scan_info *scan_info_tlv;
26298c2ecf20Sopenharmony_ci	u8 *radio_type;
26308c2ecf20Sopenharmony_ci	u64 fw_tsf = 0;
26318c2ecf20Sopenharmony_ci	s32 rssi = 0;
26328c2ecf20Sopenharmony_ci	struct mwifiex_event_scan_result *event_scan = buf;
26338c2ecf20Sopenharmony_ci	u8 num_of_set = event_scan->num_of_set;
26348c2ecf20Sopenharmony_ci	u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result);
26358c2ecf20Sopenharmony_ci	u16 scan_resp_size = le16_to_cpu(event_scan->buf_size);
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	if (num_of_set > MWIFIEX_MAX_AP) {
26388c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
26398c2ecf20Sopenharmony_ci			    "EXT_SCAN: Invalid number of AP returned (%d)!!\n",
26408c2ecf20Sopenharmony_ci			    num_of_set);
26418c2ecf20Sopenharmony_ci		ret = -1;
26428c2ecf20Sopenharmony_ci		goto check_next_scan;
26438c2ecf20Sopenharmony_ci	}
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	bytes_left = scan_resp_size;
26468c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
26478c2ecf20Sopenharmony_ci		    "EXT_SCAN: size %d, returned %d APs...",
26488c2ecf20Sopenharmony_ci		    scan_resp_size, num_of_set);
26498c2ecf20Sopenharmony_ci	mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf,
26508c2ecf20Sopenharmony_ci			 scan_resp_size +
26518c2ecf20Sopenharmony_ci			 sizeof(struct mwifiex_event_scan_result));
26528c2ecf20Sopenharmony_ci
26538c2ecf20Sopenharmony_ci	tlv = (struct mwifiex_ie_types_data *)scan_resp;
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci	for (idx = 0; idx < num_of_set && bytes_left; idx++) {
26568c2ecf20Sopenharmony_ci		type = le16_to_cpu(tlv->header.type);
26578c2ecf20Sopenharmony_ci		len = le16_to_cpu(tlv->header.len);
26588c2ecf20Sopenharmony_ci		if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) {
26598c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
26608c2ecf20Sopenharmony_ci				    "EXT_SCAN: Error bytes left < TLV length\n");
26618c2ecf20Sopenharmony_ci			break;
26628c2ecf20Sopenharmony_ci		}
26638c2ecf20Sopenharmony_ci		scan_rsp_tlv = NULL;
26648c2ecf20Sopenharmony_ci		scan_info_tlv = NULL;
26658c2ecf20Sopenharmony_ci		bytes_left_for_tlv = bytes_left;
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci		/* BSS response TLV with beacon or probe response buffer
26688c2ecf20Sopenharmony_ci		 * at the initial position of each descriptor
26698c2ecf20Sopenharmony_ci		 */
26708c2ecf20Sopenharmony_ci		if (type != TLV_TYPE_BSS_SCAN_RSP)
26718c2ecf20Sopenharmony_ci			break;
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_ci		bss_info = (u8 *)tlv;
26748c2ecf20Sopenharmony_ci		scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv;
26758c2ecf20Sopenharmony_ci		tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
26768c2ecf20Sopenharmony_ci		bytes_left_for_tlv -=
26778c2ecf20Sopenharmony_ci				(len + sizeof(struct mwifiex_ie_types_header));
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_ci		while (bytes_left_for_tlv >=
26808c2ecf20Sopenharmony_ci		       sizeof(struct mwifiex_ie_types_header) &&
26818c2ecf20Sopenharmony_ci		       le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) {
26828c2ecf20Sopenharmony_ci			type = le16_to_cpu(tlv->header.type);
26838c2ecf20Sopenharmony_ci			len = le16_to_cpu(tlv->header.len);
26848c2ecf20Sopenharmony_ci			if (bytes_left_for_tlv <
26858c2ecf20Sopenharmony_ci			    sizeof(struct mwifiex_ie_types_header) + len) {
26868c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, ERROR,
26878c2ecf20Sopenharmony_ci					    "EXT_SCAN: Error in processing TLV,\t"
26888c2ecf20Sopenharmony_ci					    "bytes left < TLV length\n");
26898c2ecf20Sopenharmony_ci				scan_rsp_tlv = NULL;
26908c2ecf20Sopenharmony_ci				bytes_left_for_tlv = 0;
26918c2ecf20Sopenharmony_ci				continue;
26928c2ecf20Sopenharmony_ci			}
26938c2ecf20Sopenharmony_ci			switch (type) {
26948c2ecf20Sopenharmony_ci			case TLV_TYPE_BSS_SCAN_INFO:
26958c2ecf20Sopenharmony_ci				scan_info_tlv =
26968c2ecf20Sopenharmony_ci				  (struct mwifiex_ie_types_bss_scan_info *)tlv;
26978c2ecf20Sopenharmony_ci				if (len !=
26988c2ecf20Sopenharmony_ci				 sizeof(struct mwifiex_ie_types_bss_scan_info) -
26998c2ecf20Sopenharmony_ci				 sizeof(struct mwifiex_ie_types_header)) {
27008c2ecf20Sopenharmony_ci					bytes_left_for_tlv = 0;
27018c2ecf20Sopenharmony_ci					continue;
27028c2ecf20Sopenharmony_ci				}
27038c2ecf20Sopenharmony_ci				break;
27048c2ecf20Sopenharmony_ci			default:
27058c2ecf20Sopenharmony_ci				break;
27068c2ecf20Sopenharmony_ci			}
27078c2ecf20Sopenharmony_ci			tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
27088c2ecf20Sopenharmony_ci			bytes_left -=
27098c2ecf20Sopenharmony_ci				(len + sizeof(struct mwifiex_ie_types_header));
27108c2ecf20Sopenharmony_ci			bytes_left_for_tlv -=
27118c2ecf20Sopenharmony_ci				(len + sizeof(struct mwifiex_ie_types_header));
27128c2ecf20Sopenharmony_ci		}
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci		if (!scan_rsp_tlv)
27158c2ecf20Sopenharmony_ci			break;
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci		/* Advance pointer to the beacon buffer length and
27188c2ecf20Sopenharmony_ci		 * update the bytes count so that the function
27198c2ecf20Sopenharmony_ci		 * wlan_interpret_bss_desc_with_ie() can handle the
27208c2ecf20Sopenharmony_ci		 * scan buffer withut any change
27218c2ecf20Sopenharmony_ci		 */
27228c2ecf20Sopenharmony_ci		bss_info += sizeof(u16);
27238c2ecf20Sopenharmony_ci		bytes_left -= sizeof(u16);
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci		if (scan_info_tlv) {
27268c2ecf20Sopenharmony_ci			rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi));
27278c2ecf20Sopenharmony_ci			rssi *= 100;           /* Convert dBm to mBm */
27288c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, INFO,
27298c2ecf20Sopenharmony_ci				    "info: InterpretIE: RSSI=%d\n", rssi);
27308c2ecf20Sopenharmony_ci			fw_tsf = le64_to_cpu(scan_info_tlv->tsf);
27318c2ecf20Sopenharmony_ci			radio_type = &scan_info_tlv->radio_type;
27328c2ecf20Sopenharmony_ci		} else {
27338c2ecf20Sopenharmony_ci			radio_type = NULL;
27348c2ecf20Sopenharmony_ci		}
27358c2ecf20Sopenharmony_ci		ret = mwifiex_parse_single_response_buf(priv, &bss_info,
27368c2ecf20Sopenharmony_ci							&bytes_left, fw_tsf,
27378c2ecf20Sopenharmony_ci							radio_type, true, rssi);
27388c2ecf20Sopenharmony_ci		if (ret)
27398c2ecf20Sopenharmony_ci			goto check_next_scan;
27408c2ecf20Sopenharmony_ci	}
27418c2ecf20Sopenharmony_ci
27428c2ecf20Sopenharmony_cicheck_next_scan:
27438c2ecf20Sopenharmony_ci	if (!event_scan->more_event)
27448c2ecf20Sopenharmony_ci		mwifiex_check_next_scan_command(priv);
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	return ret;
27478c2ecf20Sopenharmony_ci}
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci/*
27508c2ecf20Sopenharmony_ci * This function prepares command for background scan query.
27518c2ecf20Sopenharmony_ci *
27528c2ecf20Sopenharmony_ci * Preparation includes -
27538c2ecf20Sopenharmony_ci *      - Setting command ID and proper size
27548c2ecf20Sopenharmony_ci *      - Setting background scan flush parameter
27558c2ecf20Sopenharmony_ci *      - Ensuring correct endian-ness
27568c2ecf20Sopenharmony_ci */
27578c2ecf20Sopenharmony_ciint mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd)
27588c2ecf20Sopenharmony_ci{
27598c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_bg_scan_query *bg_query =
27608c2ecf20Sopenharmony_ci		&cmd->params.bg_scan_query;
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY);
27638c2ecf20Sopenharmony_ci	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query)
27648c2ecf20Sopenharmony_ci				+ S_DS_GEN);
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	bg_query->flush = 1;
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	return 0;
27698c2ecf20Sopenharmony_ci}
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci/*
27728c2ecf20Sopenharmony_ci * This function inserts scan command node to the scan pending queue.
27738c2ecf20Sopenharmony_ci */
27748c2ecf20Sopenharmony_civoid
27758c2ecf20Sopenharmony_cimwifiex_queue_scan_cmd(struct mwifiex_private *priv,
27768c2ecf20Sopenharmony_ci		       struct cmd_ctrl_node *cmd_node)
27778c2ecf20Sopenharmony_ci{
27788c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
27798c2ecf20Sopenharmony_ci
27808c2ecf20Sopenharmony_ci	cmd_node->wait_q_enabled = true;
27818c2ecf20Sopenharmony_ci	cmd_node->condition = &adapter->scan_wait_q_woken;
27828c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->scan_pending_q_lock);
27838c2ecf20Sopenharmony_ci	list_add_tail(&cmd_node->list, &adapter->scan_pending_q);
27848c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->scan_pending_q_lock);
27858c2ecf20Sopenharmony_ci}
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci/*
27888c2ecf20Sopenharmony_ci * This function sends a scan command for all available channels to the
27898c2ecf20Sopenharmony_ci * firmware, filtered on a specific SSID.
27908c2ecf20Sopenharmony_ci */
27918c2ecf20Sopenharmony_cistatic int mwifiex_scan_specific_ssid(struct mwifiex_private *priv,
27928c2ecf20Sopenharmony_ci				      struct cfg80211_ssid *req_ssid)
27938c2ecf20Sopenharmony_ci{
27948c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
27958c2ecf20Sopenharmony_ci	int ret;
27968c2ecf20Sopenharmony_ci	struct mwifiex_user_scan_cfg *scan_cfg;
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci	if (adapter->scan_processing) {
27998c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, WARN,
28008c2ecf20Sopenharmony_ci			    "cmd: Scan already in process...\n");
28018c2ecf20Sopenharmony_ci		return -EBUSY;
28028c2ecf20Sopenharmony_ci	}
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	if (priv->scan_block) {
28058c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, WARN,
28068c2ecf20Sopenharmony_ci			    "cmd: Scan is blocked during association...\n");
28078c2ecf20Sopenharmony_ci		return -EBUSY;
28088c2ecf20Sopenharmony_ci	}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci	scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL);
28118c2ecf20Sopenharmony_ci	if (!scan_cfg)
28128c2ecf20Sopenharmony_ci		return -ENOMEM;
28138c2ecf20Sopenharmony_ci
28148c2ecf20Sopenharmony_ci	scan_cfg->ssid_list = req_ssid;
28158c2ecf20Sopenharmony_ci	scan_cfg->num_ssids = 1;
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_ci	ret = mwifiex_scan_networks(priv, scan_cfg);
28188c2ecf20Sopenharmony_ci
28198c2ecf20Sopenharmony_ci	kfree(scan_cfg);
28208c2ecf20Sopenharmony_ci	return ret;
28218c2ecf20Sopenharmony_ci}
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci/*
28248c2ecf20Sopenharmony_ci * Sends IOCTL request to start a scan.
28258c2ecf20Sopenharmony_ci *
28268c2ecf20Sopenharmony_ci * This function allocates the IOCTL request buffer, fills it
28278c2ecf20Sopenharmony_ci * with requisite parameters and calls the IOCTL handler.
28288c2ecf20Sopenharmony_ci *
28298c2ecf20Sopenharmony_ci * Scan command can be issued for both normal scan and specific SSID
28308c2ecf20Sopenharmony_ci * scan, depending upon whether an SSID is provided or not.
28318c2ecf20Sopenharmony_ci */
28328c2ecf20Sopenharmony_ciint mwifiex_request_scan(struct mwifiex_private *priv,
28338c2ecf20Sopenharmony_ci			 struct cfg80211_ssid *req_ssid)
28348c2ecf20Sopenharmony_ci{
28358c2ecf20Sopenharmony_ci	int ret;
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&priv->async_mutex)) {
28388c2ecf20Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR,
28398c2ecf20Sopenharmony_ci			    "%s: acquire semaphore fail\n",
28408c2ecf20Sopenharmony_ci			    __func__);
28418c2ecf20Sopenharmony_ci		return -1;
28428c2ecf20Sopenharmony_ci	}
28438c2ecf20Sopenharmony_ci
28448c2ecf20Sopenharmony_ci	priv->adapter->scan_wait_q_woken = false;
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_ci	if (req_ssid && req_ssid->ssid_len != 0)
28478c2ecf20Sopenharmony_ci		/* Specific SSID scan */
28488c2ecf20Sopenharmony_ci		ret = mwifiex_scan_specific_ssid(priv, req_ssid);
28498c2ecf20Sopenharmony_ci	else
28508c2ecf20Sopenharmony_ci		/* Normal scan */
28518c2ecf20Sopenharmony_ci		ret = mwifiex_scan_networks(priv, NULL);
28528c2ecf20Sopenharmony_ci
28538c2ecf20Sopenharmony_ci	mutex_unlock(&priv->async_mutex);
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci	return ret;
28568c2ecf20Sopenharmony_ci}
28578c2ecf20Sopenharmony_ci
28588c2ecf20Sopenharmony_ci/*
28598c2ecf20Sopenharmony_ci * This function appends the vendor specific IE TLV to a buffer.
28608c2ecf20Sopenharmony_ci */
28618c2ecf20Sopenharmony_ciint
28628c2ecf20Sopenharmony_cimwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv,
28638c2ecf20Sopenharmony_ci			    u16 vsie_mask, u8 **buffer)
28648c2ecf20Sopenharmony_ci{
28658c2ecf20Sopenharmony_ci	int id, ret_len = 0;
28668c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_vendor_param_set *vs_param_set;
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci	if (!buffer)
28698c2ecf20Sopenharmony_ci		return 0;
28708c2ecf20Sopenharmony_ci	if (!(*buffer))
28718c2ecf20Sopenharmony_ci		return 0;
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	/*
28748c2ecf20Sopenharmony_ci	 * Traverse through the saved vendor specific IE array and append
28758c2ecf20Sopenharmony_ci	 * the selected(scan/assoc/adhoc) IE as TLV to the command
28768c2ecf20Sopenharmony_ci	 */
28778c2ecf20Sopenharmony_ci	for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) {
28788c2ecf20Sopenharmony_ci		if (priv->vs_ie[id].mask & vsie_mask) {
28798c2ecf20Sopenharmony_ci			vs_param_set =
28808c2ecf20Sopenharmony_ci				(struct mwifiex_ie_types_vendor_param_set *)
28818c2ecf20Sopenharmony_ci				*buffer;
28828c2ecf20Sopenharmony_ci			vs_param_set->header.type =
28838c2ecf20Sopenharmony_ci				cpu_to_le16(TLV_TYPE_PASSTHROUGH);
28848c2ecf20Sopenharmony_ci			vs_param_set->header.len =
28858c2ecf20Sopenharmony_ci				cpu_to_le16((((u16) priv->vs_ie[id].ie[1])
28868c2ecf20Sopenharmony_ci				& 0x00FF) + 2);
28878c2ecf20Sopenharmony_ci			if (le16_to_cpu(vs_param_set->header.len) >
28888c2ecf20Sopenharmony_ci				MWIFIEX_MAX_VSIE_LEN) {
28898c2ecf20Sopenharmony_ci				mwifiex_dbg(priv->adapter, ERROR,
28908c2ecf20Sopenharmony_ci					    "Invalid param length!\n");
28918c2ecf20Sopenharmony_ci				break;
28928c2ecf20Sopenharmony_ci			}
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci			memcpy(vs_param_set->ie, priv->vs_ie[id].ie,
28958c2ecf20Sopenharmony_ci			       le16_to_cpu(vs_param_set->header.len));
28968c2ecf20Sopenharmony_ci			*buffer += le16_to_cpu(vs_param_set->header.len) +
28978c2ecf20Sopenharmony_ci				   sizeof(struct mwifiex_ie_types_header);
28988c2ecf20Sopenharmony_ci			ret_len += le16_to_cpu(vs_param_set->header.len) +
28998c2ecf20Sopenharmony_ci				   sizeof(struct mwifiex_ie_types_header);
29008c2ecf20Sopenharmony_ci		}
29018c2ecf20Sopenharmony_ci	}
29028c2ecf20Sopenharmony_ci	return ret_len;
29038c2ecf20Sopenharmony_ci}
29048c2ecf20Sopenharmony_ci
29058c2ecf20Sopenharmony_ci/*
29068c2ecf20Sopenharmony_ci * This function saves a beacon buffer of the current BSS descriptor.
29078c2ecf20Sopenharmony_ci *
29088c2ecf20Sopenharmony_ci * The current beacon buffer is saved so that it can be restored in the
29098c2ecf20Sopenharmony_ci * following cases that makes the beacon buffer not to contain the current
29108c2ecf20Sopenharmony_ci * ssid's beacon buffer.
29118c2ecf20Sopenharmony_ci *      - The current ssid was not found somehow in the last scan.
29128c2ecf20Sopenharmony_ci *      - The current ssid was the last entry of the scan table and overloaded.
29138c2ecf20Sopenharmony_ci */
29148c2ecf20Sopenharmony_civoid
29158c2ecf20Sopenharmony_cimwifiex_save_curr_bcn(struct mwifiex_private *priv)
29168c2ecf20Sopenharmony_ci{
29178c2ecf20Sopenharmony_ci	struct mwifiex_bssdescriptor *curr_bss =
29188c2ecf20Sopenharmony_ci		&priv->curr_bss_params.bss_descriptor;
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci	if (!curr_bss->beacon_buf_size)
29218c2ecf20Sopenharmony_ci		return;
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_ci	/* allocate beacon buffer at 1st time; or if it's size has changed */
29248c2ecf20Sopenharmony_ci	if (!priv->curr_bcn_buf ||
29258c2ecf20Sopenharmony_ci	    priv->curr_bcn_size != curr_bss->beacon_buf_size) {
29268c2ecf20Sopenharmony_ci		priv->curr_bcn_size = curr_bss->beacon_buf_size;
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci		kfree(priv->curr_bcn_buf);
29298c2ecf20Sopenharmony_ci		priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size,
29308c2ecf20Sopenharmony_ci					     GFP_ATOMIC);
29318c2ecf20Sopenharmony_ci		if (!priv->curr_bcn_buf)
29328c2ecf20Sopenharmony_ci			return;
29338c2ecf20Sopenharmony_ci	}
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf,
29368c2ecf20Sopenharmony_ci	       curr_bss->beacon_buf_size);
29378c2ecf20Sopenharmony_ci	mwifiex_dbg(priv->adapter, INFO,
29388c2ecf20Sopenharmony_ci		    "info: current beacon saved %d\n",
29398c2ecf20Sopenharmony_ci		    priv->curr_bcn_size);
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	curr_bss->beacon_buf = priv->curr_bcn_buf;
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci	/* adjust the pointers in the current BSS descriptor */
29448c2ecf20Sopenharmony_ci	if (curr_bss->bcn_wpa_ie)
29458c2ecf20Sopenharmony_ci		curr_bss->bcn_wpa_ie =
29468c2ecf20Sopenharmony_ci			(struct ieee_types_vendor_specific *)
29478c2ecf20Sopenharmony_ci			(curr_bss->beacon_buf +
29488c2ecf20Sopenharmony_ci			 curr_bss->wpa_offset);
29498c2ecf20Sopenharmony_ci
29508c2ecf20Sopenharmony_ci	if (curr_bss->bcn_rsn_ie)
29518c2ecf20Sopenharmony_ci		curr_bss->bcn_rsn_ie = (struct ieee_types_generic *)
29528c2ecf20Sopenharmony_ci			(curr_bss->beacon_buf +
29538c2ecf20Sopenharmony_ci			 curr_bss->rsn_offset);
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci	if (curr_bss->bcn_ht_cap)
29568c2ecf20Sopenharmony_ci		curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *)
29578c2ecf20Sopenharmony_ci			(curr_bss->beacon_buf +
29588c2ecf20Sopenharmony_ci			 curr_bss->ht_cap_offset);
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci	if (curr_bss->bcn_ht_oper)
29618c2ecf20Sopenharmony_ci		curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *)
29628c2ecf20Sopenharmony_ci			(curr_bss->beacon_buf +
29638c2ecf20Sopenharmony_ci			 curr_bss->ht_info_offset);
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_ci	if (curr_bss->bcn_vht_cap)
29668c2ecf20Sopenharmony_ci		curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf +
29678c2ecf20Sopenharmony_ci						 curr_bss->vht_cap_offset);
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci	if (curr_bss->bcn_vht_oper)
29708c2ecf20Sopenharmony_ci		curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf +
29718c2ecf20Sopenharmony_ci						  curr_bss->vht_info_offset);
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	if (curr_bss->bcn_bss_co_2040)
29748c2ecf20Sopenharmony_ci		curr_bss->bcn_bss_co_2040 =
29758c2ecf20Sopenharmony_ci			(curr_bss->beacon_buf + curr_bss->bss_co_2040_offset);
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_ci	if (curr_bss->bcn_ext_cap)
29788c2ecf20Sopenharmony_ci		curr_bss->bcn_ext_cap = curr_bss->beacon_buf +
29798c2ecf20Sopenharmony_ci			curr_bss->ext_cap_offset;
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci	if (curr_bss->oper_mode)
29828c2ecf20Sopenharmony_ci		curr_bss->oper_mode = (void *)(curr_bss->beacon_buf +
29838c2ecf20Sopenharmony_ci					       curr_bss->oper_mode_offset);
29848c2ecf20Sopenharmony_ci}
29858c2ecf20Sopenharmony_ci
29868c2ecf20Sopenharmony_ci/*
29878c2ecf20Sopenharmony_ci * This function frees the current BSS descriptor beacon buffer.
29888c2ecf20Sopenharmony_ci */
29898c2ecf20Sopenharmony_civoid
29908c2ecf20Sopenharmony_cimwifiex_free_curr_bcn(struct mwifiex_private *priv)
29918c2ecf20Sopenharmony_ci{
29928c2ecf20Sopenharmony_ci	kfree(priv->curr_bcn_buf);
29938c2ecf20Sopenharmony_ci	priv->curr_bcn_buf = NULL;
29948c2ecf20Sopenharmony_ci}
2995