xref: /linux/drivers/net/wireless/nxp/nxpwifi/scan.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
173b01e57SJeff Chen // SPDX-License-Identifier: GPL-2.0-only
273b01e57SJeff Chen /*
373b01e57SJeff Chen  * nxpwifi: scan ioctl and command handling
473b01e57SJeff Chen  *
573b01e57SJeff Chen  * Copyright 2011-2024 NXP
673b01e57SJeff Chen  */
773b01e57SJeff Chen 
873b01e57SJeff Chen #include "cfg.h"
973b01e57SJeff Chen #include "util.h"
1073b01e57SJeff Chen #include "fw.h"
1173b01e57SJeff Chen #include "main.h"
1273b01e57SJeff Chen #include "cmdevt.h"
1373b01e57SJeff Chen #include "11n.h"
1473b01e57SJeff Chen #include "11ac.h"
1573b01e57SJeff Chen #include "11ax.h"
1673b01e57SJeff Chen #include "cfg80211.h"
1773b01e57SJeff Chen 
1873b01e57SJeff Chen /* The maximum number of channels the firmware can scan per command */
1973b01e57SJeff Chen #define NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN   14
2073b01e57SJeff Chen 
2173b01e57SJeff Chen #define NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD        4
2273b01e57SJeff Chen 
2373b01e57SJeff Chen /* Memory needed to store a max sized Channel List TLV for a firmware scan */
2473b01e57SJeff Chen #define CHAN_TLV_MAX_SIZE  (sizeof(struct nxpwifi_ie_types_header)        \
2573b01e57SJeff Chen 			    + (NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN     \
2673b01e57SJeff Chen 			    * sizeof(struct nxpwifi_chan_scan_param_set)))
2773b01e57SJeff Chen 
2873b01e57SJeff Chen /* Memory needed to store supported rate */
2973b01e57SJeff Chen #define RATE_TLV_MAX_SIZE   (sizeof(struct nxpwifi_ie_types_rates_param_set) \
3073b01e57SJeff Chen 			     + HOSTCMD_SUPPORTED_RATES)
3173b01e57SJeff Chen 
3273b01e57SJeff Chen /* Memory needed to store a max number/size WildCard SSID TLV for a firmware scan */
3373b01e57SJeff Chen #define WILDCARD_SSID_TLV_MAX_SIZE  \
3473b01e57SJeff Chen 	(NXPWIFI_MAX_SSID_LIST_LENGTH *					\
3573b01e57SJeff Chen 		(sizeof(struct nxpwifi_ie_types_wildcard_ssid_params)	\
3673b01e57SJeff Chen 			+ IEEE80211_MAX_SSID_LEN))
3773b01e57SJeff Chen 
3873b01e57SJeff Chen /* Maximum memory needed for a nxpwifi_scan_cmd_config with all TLVs at max */
3973b01e57SJeff Chen #define MAX_SCAN_CFG_ALLOC (sizeof(struct nxpwifi_scan_cmd_config)        \
4073b01e57SJeff Chen 				+ sizeof(struct nxpwifi_ie_types_num_probes)   \
4173b01e57SJeff Chen 				+ sizeof(struct nxpwifi_ie_types_htcap)       \
4273b01e57SJeff Chen 				+ sizeof(struct nxpwifi_ie_types_vhtcap)      \
4373b01e57SJeff Chen 				+ sizeof(struct nxpwifi_ie_types_he_cap)      \
4473b01e57SJeff Chen 				+ CHAN_TLV_MAX_SIZE                 \
4573b01e57SJeff Chen 				+ RATE_TLV_MAX_SIZE                 \
4673b01e57SJeff Chen 				+ WILDCARD_SSID_TLV_MAX_SIZE)
4773b01e57SJeff Chen 
4873b01e57SJeff Chen union nxpwifi_scan_cmd_config_tlv {
4973b01e57SJeff Chen 	/* Scan configuration (variable length) */
5073b01e57SJeff Chen 	struct nxpwifi_scan_cmd_config config;
5173b01e57SJeff Chen 	/* Max allocated block */
5273b01e57SJeff Chen 	u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC];
5373b01e57SJeff Chen };
5473b01e57SJeff Chen 
5573b01e57SJeff Chen #define NXPWIFI_WPA_CIPHER_SUITE_TKIP		SUITE(WLAN_OUI_MICROSOFT, 2)
5673b01e57SJeff Chen #define NXPWIFI_WPA_CIPHER_SUITE_CCMP		SUITE(WLAN_OUI_MICROSOFT, 4)
5773b01e57SJeff Chen 
5873b01e57SJeff Chen static void
_dbg_security_flags(int log_level,const char * func,const char * desc,struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)5973b01e57SJeff Chen _dbg_security_flags(int log_level, const char *func, const char *desc,
6073b01e57SJeff Chen 		    struct nxpwifi_private *priv,
6173b01e57SJeff Chen 		    struct nxpwifi_bssdescriptor *bss_desc)
6273b01e57SJeff Chen {
6373b01e57SJeff Chen 	_nxpwifi_dbg(priv->adapter, log_level,
6473b01e57SJeff Chen 		     "info: %s: %s:\twpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\tEncMode=%#x privacy=%#x\n",
6573b01e57SJeff Chen 		     func, desc,
6673b01e57SJeff Chen 		     bss_desc->bcn_wpa_ie ?
6773b01e57SJeff Chen 		     bss_desc->bcn_wpa_ie->vend_hdr.element_id : 0,
6873b01e57SJeff Chen 		     bss_desc->bcn_rsn_ie ?
6973b01e57SJeff Chen 		     bss_desc->bcn_rsn_ie->id : 0,
7073b01e57SJeff Chen 		     priv->sec_info.wep_enabled ? "e" : "d",
7173b01e57SJeff Chen 		     priv->sec_info.wpa_enabled ? "e" : "d",
7273b01e57SJeff Chen 		     priv->sec_info.wpa2_enabled ? "e" : "d",
7373b01e57SJeff Chen 		     priv->sec_info.encryption_mode,
7473b01e57SJeff Chen 		     bss_desc->privacy);
7573b01e57SJeff Chen }
7673b01e57SJeff Chen 
7773b01e57SJeff Chen #define dbg_security_flags(mask, desc, priv, bss_desc) \
7873b01e57SJeff Chen 	_dbg_security_flags(NXPWIFI_DBG_##mask, __func__, desc, priv, bss_desc)
7973b01e57SJeff Chen 
8073b01e57SJeff Chen /* Parse a WPA/RSN element and check whether its PTK list contains the OUI */
8173b01e57SJeff Chen static u8
nxpwifi_search_oui_in_ie(struct ie_body * iebody,u8 * oui)8273b01e57SJeff Chen nxpwifi_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
8373b01e57SJeff Chen {
8473b01e57SJeff Chen 	u8 count;
8573b01e57SJeff Chen 
8673b01e57SJeff Chen 	count = iebody->ptk_cnt[0];
8773b01e57SJeff Chen 
8873b01e57SJeff Chen 	/*
8973b01e57SJeff Chen 	 * PTK may contain multiple OUIs; iterate through the list and compare
9073b01e57SJeff Chen 	 * each one
9173b01e57SJeff Chen 	 */
9273b01e57SJeff Chen 	while (count) {
9373b01e57SJeff Chen 		if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body)))
9473b01e57SJeff Chen 			return NXPWIFI_OUI_PRESENT;
9573b01e57SJeff Chen 
9673b01e57SJeff Chen 		--count;
9773b01e57SJeff Chen 		if (count)
9873b01e57SJeff Chen 			iebody = (struct ie_body *)((u8 *)iebody +
9973b01e57SJeff Chen 						sizeof(iebody->ptk_body));
10073b01e57SJeff Chen 	}
10173b01e57SJeff Chen 
10273b01e57SJeff Chen 	pr_debug("info: %s: OUI is not found in PTK\n", __func__);
10373b01e57SJeff Chen 	return NXPWIFI_OUI_NOT_PRESENT;
10473b01e57SJeff Chen }
10573b01e57SJeff Chen 
10673b01e57SJeff Chen /* Check whether the RSN IE is present and if its PTK list contains the OUI */
10773b01e57SJeff Chen static u8
nxpwifi_is_rsn_oui_present(struct nxpwifi_bssdescriptor * bss_desc,u32 cipher)10873b01e57SJeff Chen nxpwifi_is_rsn_oui_present(struct nxpwifi_bssdescriptor *bss_desc,
10973b01e57SJeff Chen 			   u32 cipher)
11073b01e57SJeff Chen {
11173b01e57SJeff Chen 	struct ie_body *iebody;
11273b01e57SJeff Chen 	u8 ret = NXPWIFI_OUI_NOT_PRESENT;
11373b01e57SJeff Chen 	__be32 oui = cpu_to_be32(cipher);
11473b01e57SJeff Chen 
11573b01e57SJeff Chen 	if (bss_desc->bcn_rsn_ie) {
11673b01e57SJeff Chen 		iebody = (struct ie_body *)
11773b01e57SJeff Chen 			 (((u8 *)bss_desc->bcn_rsn_ie->data) +
11873b01e57SJeff Chen 			  RSN_GTK_OUI_OFFSET);
11973b01e57SJeff Chen 		ret = nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui);
12073b01e57SJeff Chen 		if (ret)
12173b01e57SJeff Chen 			return ret;
12273b01e57SJeff Chen 	}
12373b01e57SJeff Chen 	return ret;
12473b01e57SJeff Chen }
12573b01e57SJeff Chen 
12673b01e57SJeff Chen /* Check if the WPA IE exists and whether its PTK list contains the OUI */
12773b01e57SJeff Chen static u8
nxpwifi_is_wpa_oui_present(struct nxpwifi_bssdescriptor * bss_desc,u32 cipher)12873b01e57SJeff Chen nxpwifi_is_wpa_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cipher)
12973b01e57SJeff Chen {
13073b01e57SJeff Chen 	struct ie_body *iebody;
13173b01e57SJeff Chen 	u8 ret = NXPWIFI_OUI_NOT_PRESENT;
13273b01e57SJeff Chen 	__be32 oui = cpu_to_be32(cipher);
13373b01e57SJeff Chen 
13473b01e57SJeff Chen 	if (bss_desc->bcn_wpa_ie) {
13573b01e57SJeff Chen 		iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
13673b01e57SJeff Chen 					    WPA_GTK_OUI_OFFSET);
13773b01e57SJeff Chen 		ret = nxpwifi_search_oui_in_ie(iebody, (u8 *)&oui);
13873b01e57SJeff Chen 		if (ret)
13973b01e57SJeff Chen 			return ret;
14073b01e57SJeff Chen 	}
14173b01e57SJeff Chen 	return ret;
14273b01e57SJeff Chen }
14373b01e57SJeff Chen 
14473b01e57SJeff Chen /* Check whether both driver and BSS operate with no security */
14573b01e57SJeff Chen static bool
nxpwifi_is_bss_no_sec(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)14673b01e57SJeff Chen nxpwifi_is_bss_no_sec(struct nxpwifi_private *priv,
14773b01e57SJeff Chen 		      struct nxpwifi_bssdescriptor *bss_desc)
14873b01e57SJeff Chen {
14973b01e57SJeff Chen 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
15073b01e57SJeff Chen 	    !priv->sec_info.wpa2_enabled &&
15173b01e57SJeff Chen 	    !bss_desc->bcn_rsn_ie &&
15273b01e57SJeff Chen 	    !bss_desc->bcn_wpa_ie &&
15373b01e57SJeff Chen 	    !priv->sec_info.encryption_mode && !bss_desc->privacy) {
15473b01e57SJeff Chen 		return true;
15573b01e57SJeff Chen 	}
15673b01e57SJeff Chen 	return false;
15773b01e57SJeff Chen }
15873b01e57SJeff Chen 
15973b01e57SJeff Chen /* Check whether static WEP is enabled and the BSS privacy setting matches */
16073b01e57SJeff Chen static bool
nxpwifi_is_bss_static_wep(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)16173b01e57SJeff Chen nxpwifi_is_bss_static_wep(struct nxpwifi_private *priv,
16273b01e57SJeff Chen 			  struct nxpwifi_bssdescriptor *bss_desc)
16373b01e57SJeff Chen {
16473b01e57SJeff Chen 	if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
16573b01e57SJeff Chen 	    !priv->sec_info.wpa2_enabled && bss_desc->privacy) {
16673b01e57SJeff Chen 		return true;
16773b01e57SJeff Chen 	}
16873b01e57SJeff Chen 	return false;
16973b01e57SJeff Chen }
17073b01e57SJeff Chen 
17173b01e57SJeff Chen /* Check whether WPA is enabled and the BSS contains a WPA IE */
17273b01e57SJeff Chen static bool
nxpwifi_is_bss_wpa(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)17373b01e57SJeff Chen nxpwifi_is_bss_wpa(struct nxpwifi_private *priv,
17473b01e57SJeff Chen 		   struct nxpwifi_bssdescriptor *bss_desc)
17573b01e57SJeff Chen {
17673b01e57SJeff Chen 	if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled &&
17773b01e57SJeff Chen 	    !priv->sec_info.wpa2_enabled &&
17873b01e57SJeff Chen 	    bss_desc->bcn_wpa_ie) {
17973b01e57SJeff Chen 		dbg_security_flags(INFO, "WPA", priv, bss_desc);
18073b01e57SJeff Chen 		return true;
18173b01e57SJeff Chen 	}
18273b01e57SJeff Chen 	return false;
18373b01e57SJeff Chen }
18473b01e57SJeff Chen 
18573b01e57SJeff Chen /* Check whether WPA2 is enabled and the BSS includes an RSN IE */
18673b01e57SJeff Chen static bool
nxpwifi_is_bss_wpa2(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)18773b01e57SJeff Chen nxpwifi_is_bss_wpa2(struct nxpwifi_private *priv,
18873b01e57SJeff Chen 		    struct nxpwifi_bssdescriptor *bss_desc)
18973b01e57SJeff Chen {
19073b01e57SJeff Chen 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
19173b01e57SJeff Chen 	    priv->sec_info.wpa2_enabled &&
19273b01e57SJeff Chen 	    bss_desc->bcn_rsn_ie) {
19373b01e57SJeff Chen 		/*
19473b01e57SJeff Chen 		 * Some APs (e.g., WRT54G) may omit the privacy bit even when
19573b01e57SJeff Chen 		 * using WPA2
19673b01e57SJeff Chen 		 */
19773b01e57SJeff Chen 		dbg_security_flags(ERROR, "WPA2", priv, bss_desc);
19873b01e57SJeff Chen 		return true;
19973b01e57SJeff Chen 	}
20073b01e57SJeff Chen 	return false;
20173b01e57SJeff Chen }
20273b01e57SJeff Chen 
20373b01e57SJeff Chen /* Check dynamic WEP: enabled in driver, privacy set, and no WPA/RSN IE present */
20473b01e57SJeff Chen static bool
nxpwifi_is_bss_dynamic_wep(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)20573b01e57SJeff Chen nxpwifi_is_bss_dynamic_wep(struct nxpwifi_private *priv,
20673b01e57SJeff Chen 			   struct nxpwifi_bssdescriptor *bss_desc)
20773b01e57SJeff Chen {
20873b01e57SJeff Chen 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
20973b01e57SJeff Chen 	    !priv->sec_info.wpa2_enabled &&
21073b01e57SJeff Chen 	    !bss_desc->bcn_wpa_ie &&
21173b01e57SJeff Chen 	    !bss_desc->bcn_rsn_ie &&
21273b01e57SJeff Chen 	    priv->sec_info.encryption_mode && bss_desc->privacy) {
21373b01e57SJeff Chen 		dbg_security_flags(INFO, "dynamic", priv, bss_desc);
21473b01e57SJeff Chen 		return true;
21573b01e57SJeff Chen 	}
21673b01e57SJeff Chen 	return false;
21773b01e57SJeff Chen }
21873b01e57SJeff Chen 
21973b01e57SJeff Chen /*
22073b01e57SJeff Chen  * Check whether a scanned network is compatible with the driver's security
22173b01e57SJeff Chen  * configuration. The decision considers WEP, WPA, WPA2, privacy settings,
22273b01e57SJeff Chen  * and whether HT must be disabled when required (e.g., no AES).
22373b01e57SJeff Chen  *
22473b01e57SJeff Chen  * General rules:
22573b01e57SJeff Chen  * - Open networks: always compatible.
22673b01e57SJeff Chen  * - WPA-only: compatible; HT disabled if AES is not supported.
22773b01e57SJeff Chen  * - WPA2-only: compatible; HT disabled if AES is not supported.
22873b01e57SJeff Chen  * - Static WEP: compatible; HT disabled.
22973b01e57SJeff Chen  * - Dynamic WEP: compatible when privacy is enabled.
23073b01e57SJeff Chen  *
23173b01e57SJeff Chen  * Note: Compatibility is not enforced during roaming except for security mode.
23273b01e57SJeff Chen  */
23373b01e57SJeff Chen static int
nxpwifi_is_network_compatible(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc,u32 mode)23473b01e57SJeff Chen nxpwifi_is_network_compatible(struct nxpwifi_private *priv,
23573b01e57SJeff Chen 			      struct nxpwifi_bssdescriptor *bss_desc, u32 mode)
23673b01e57SJeff Chen {
23773b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
23873b01e57SJeff Chen 
23973b01e57SJeff Chen 	bss_desc->disable_11n = false;
24073b01e57SJeff Chen 
24173b01e57SJeff Chen 	/* Skip compatibility checks while roaming */
24273b01e57SJeff Chen 	if (priv->media_connected &&
24373b01e57SJeff Chen 	    priv->bss_mode == NL80211_IFTYPE_STATION &&
24473b01e57SJeff Chen 	    bss_desc->bss_mode == NL80211_IFTYPE_STATION)
24573b01e57SJeff Chen 		return 0;
24673b01e57SJeff Chen 
24773b01e57SJeff Chen 	if (priv->wps.session_enable) {
24873b01e57SJeff Chen 		nxpwifi_dbg(adapter, IOCTL,
24973b01e57SJeff Chen 			    "info: return success directly in WPS period\n");
25073b01e57SJeff Chen 		return 0;
25173b01e57SJeff Chen 	}
25273b01e57SJeff Chen 
25373b01e57SJeff Chen 	if (bss_desc->chan_sw_ie_present) {
25473b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
25573b01e57SJeff Chen 			    "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
25673b01e57SJeff Chen 		return -EPERM;
25773b01e57SJeff Chen 	}
25873b01e57SJeff Chen 
25973b01e57SJeff Chen 	if (bss_desc->bss_mode == mode) {
26073b01e57SJeff Chen 		if (nxpwifi_is_bss_no_sec(priv, bss_desc)) {
26173b01e57SJeff Chen 			return 0;
26273b01e57SJeff Chen 		} else if (nxpwifi_is_bss_static_wep(priv, bss_desc)) {
26373b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
26473b01e57SJeff Chen 				    "info: Disable 11n in WEP mode.\n");
26573b01e57SJeff Chen 			bss_desc->disable_11n = true;
26673b01e57SJeff Chen 			return 0;
26773b01e57SJeff Chen 		} else if (nxpwifi_is_bss_wpa(priv, bss_desc)) {
26873b01e57SJeff Chen 			if (((priv->config_bands & BAND_GN ||
26973b01e57SJeff Chen 			      priv->config_bands & BAND_AN) &&
27073b01e57SJeff Chen 			     bss_desc->bcn_ht_cap) &&
27173b01e57SJeff Chen 			    !nxpwifi_is_wpa_oui_present(bss_desc,
27273b01e57SJeff Chen 							 NXPWIFI_WPA_CIPHER_SUITE_CCMP)) {
27373b01e57SJeff Chen 				if (nxpwifi_is_wpa_oui_present
27473b01e57SJeff Chen 						(bss_desc, NXPWIFI_WPA_CIPHER_SUITE_TKIP)) {
27573b01e57SJeff Chen 					nxpwifi_dbg(adapter, INFO,
27673b01e57SJeff Chen 						    "info: Disable 11n if AES\t"
27773b01e57SJeff Chen 						    "is not supported by AP\n");
27873b01e57SJeff Chen 					bss_desc->disable_11n = true;
27973b01e57SJeff Chen 				} else {
28073b01e57SJeff Chen 					return -EINVAL;
28173b01e57SJeff Chen 				}
28273b01e57SJeff Chen 			}
28373b01e57SJeff Chen 			return 0;
28473b01e57SJeff Chen 		} else if (nxpwifi_is_bss_wpa2(priv, bss_desc)) {
28573b01e57SJeff Chen 			if (((priv->config_bands & BAND_GN ||
28673b01e57SJeff Chen 			      priv->config_bands & BAND_AN) &&
28773b01e57SJeff Chen 			     bss_desc->bcn_ht_cap) &&
28873b01e57SJeff Chen 			    !nxpwifi_is_rsn_oui_present(bss_desc,
28973b01e57SJeff Chen 							WLAN_CIPHER_SUITE_CCMP)) {
29073b01e57SJeff Chen 				if (nxpwifi_is_rsn_oui_present
29173b01e57SJeff Chen 						(bss_desc, WLAN_CIPHER_SUITE_TKIP)) {
29273b01e57SJeff Chen 					nxpwifi_dbg(adapter, INFO,
29373b01e57SJeff Chen 						    "info: Disable 11n if AES\t"
29473b01e57SJeff Chen 						    "is not supported by AP\n");
29573b01e57SJeff Chen 					bss_desc->disable_11n = true;
29673b01e57SJeff Chen 				} else if (nxpwifi_is_rsn_oui_present
29773b01e57SJeff Chen 						(bss_desc, WLAN_CIPHER_SUITE_GCMP_256) ||
29873b01e57SJeff Chen 						nxpwifi_is_rsn_oui_present
29973b01e57SJeff Chen 						(bss_desc, WLAN_CIPHER_SUITE_CCMP_256)) {
30073b01e57SJeff Chen 					return 0;
30173b01e57SJeff Chen 				} else {
30273b01e57SJeff Chen 					return -EINVAL;
30373b01e57SJeff Chen 				}
30473b01e57SJeff Chen 			}
30573b01e57SJeff Chen 			return 0;
30673b01e57SJeff Chen 		} else if (nxpwifi_is_bss_dynamic_wep(priv, bss_desc)) {
30773b01e57SJeff Chen 			return 0;
30873b01e57SJeff Chen 		}
30973b01e57SJeff Chen 
31073b01e57SJeff Chen 		/* Security mismatch */
31173b01e57SJeff Chen 		dbg_security_flags(ERROR, "failed", priv, bss_desc);
31273b01e57SJeff Chen 		return -EINVAL;
31373b01e57SJeff Chen 	}
31473b01e57SJeff Chen 
31573b01e57SJeff Chen 	return -EINVAL;
31673b01e57SJeff Chen }
31773b01e57SJeff Chen 
31873b01e57SJeff Chen /*
31973b01e57SJeff Chen  * Build the channel list for scanning based on region and band settings.
32073b01e57SJeff Chen  * Used when a scan request does not specify its own channel list.
32173b01e57SJeff Chen  */
32273b01e57SJeff Chen static int
nxpwifi_scan_create_channel_list(struct nxpwifi_private * priv,const struct nxpwifi_user_scan_cfg * user_scan_in,struct nxpwifi_chan_scan_param_set * scan_chan_list,u8 filtered_scan)32373b01e57SJeff Chen nxpwifi_scan_create_channel_list(struct nxpwifi_private *priv,
32473b01e57SJeff Chen 				 const struct nxpwifi_user_scan_cfg
32573b01e57SJeff Chen 				 *user_scan_in,
32673b01e57SJeff Chen 				 struct nxpwifi_chan_scan_param_set
32773b01e57SJeff Chen 				 *scan_chan_list,
32873b01e57SJeff Chen 				 u8 filtered_scan)
32973b01e57SJeff Chen {
33073b01e57SJeff Chen 	enum nl80211_band band;
33173b01e57SJeff Chen 	struct ieee80211_supported_band *sband;
33273b01e57SJeff Chen 	struct ieee80211_channel *ch;
33373b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
33473b01e57SJeff Chen 	int chan_idx = 0, i;
33573b01e57SJeff Chen 	u16 scan_time = 0;
33673b01e57SJeff Chen 
33773b01e57SJeff Chen 	if (user_scan_in)
33873b01e57SJeff Chen 		scan_time = (u16)user_scan_in->chan_list[0].scan_time;
33973b01e57SJeff Chen 
34073b01e57SJeff Chen 	for (band = 0; (band < NUM_NL80211_BANDS) ; band++) {
34173b01e57SJeff Chen 		if (!priv->wdev.wiphy->bands[band])
34273b01e57SJeff Chen 			continue;
34373b01e57SJeff Chen 
34473b01e57SJeff Chen 		sband = priv->wdev.wiphy->bands[band];
34573b01e57SJeff Chen 
34673b01e57SJeff Chen 		for (i = 0; (i < sband->n_channels) ; i++) {
34773b01e57SJeff Chen 			ch = &sband->channels[i];
34873b01e57SJeff Chen 			if (ch->flags & IEEE80211_CHAN_DISABLED)
34973b01e57SJeff Chen 				continue;
35073b01e57SJeff Chen 			scan_chan_list[chan_idx].band_cfg = band;
35173b01e57SJeff Chen 
35273b01e57SJeff Chen 			if (scan_time)
35373b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
35473b01e57SJeff Chen 					cpu_to_le16(scan_time);
35573b01e57SJeff Chen 			else if ((ch->flags & IEEE80211_CHAN_NO_IR) ||
35673b01e57SJeff Chen 				 (ch->flags & IEEE80211_CHAN_RADAR))
35773b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
35873b01e57SJeff Chen 					cpu_to_le16(adapter->passive_scan_time);
35973b01e57SJeff Chen 			else
36073b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
36173b01e57SJeff Chen 					cpu_to_le16(adapter->active_scan_time);
36273b01e57SJeff Chen 
36373b01e57SJeff Chen 			if (ch->flags & IEEE80211_CHAN_NO_IR)
36473b01e57SJeff Chen 				scan_chan_list[chan_idx].chan_scan_mode_bmap |=
36573b01e57SJeff Chen 					(NXPWIFI_PASSIVE_SCAN | NXPWIFI_HIDDEN_SSID_REPORT);
36673b01e57SJeff Chen 			else
36773b01e57SJeff Chen 				scan_chan_list[chan_idx].chan_scan_mode_bmap &=
36873b01e57SJeff Chen 					~NXPWIFI_PASSIVE_SCAN;
36973b01e57SJeff Chen 
37073b01e57SJeff Chen 			scan_chan_list[chan_idx].chan_number = (u32)ch->hw_value;
37173b01e57SJeff Chen 			scan_chan_list[chan_idx].chan_scan_mode_bmap |=
37273b01e57SJeff Chen 				NXPWIFI_DISABLE_CHAN_FILT;
37373b01e57SJeff Chen 
37473b01e57SJeff Chen 			if (filtered_scan &&
37573b01e57SJeff Chen 			    !((ch->flags & IEEE80211_CHAN_NO_IR) ||
37673b01e57SJeff Chen 			      (ch->flags & IEEE80211_CHAN_RADAR)))
37773b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
37873b01e57SJeff Chen 				cpu_to_le16(adapter->specific_scan_time);
37973b01e57SJeff Chen 
38073b01e57SJeff Chen 			chan_idx++;
38173b01e57SJeff Chen 		}
38273b01e57SJeff Chen 	}
38373b01e57SJeff Chen 	return chan_idx;
38473b01e57SJeff Chen }
38573b01e57SJeff Chen 
38673b01e57SJeff Chen /*
38773b01e57SJeff Chen  * Build the channel-list TLV for bgscan based on region and band settings.
38873b01e57SJeff Chen  */
38973b01e57SJeff Chen static int
nxpwifi_bgscan_create_channel_list(struct nxpwifi_private * priv,const struct nxpwifi_bg_scan_cfg * bgscan_cfg_in,struct nxpwifi_chan_scan_param_set * scan_chan_list)39073b01e57SJeff Chen nxpwifi_bgscan_create_channel_list(struct nxpwifi_private *priv,
39173b01e57SJeff Chen 				   const struct nxpwifi_bg_scan_cfg
39273b01e57SJeff Chen 				   *bgscan_cfg_in,
39373b01e57SJeff Chen 				   struct nxpwifi_chan_scan_param_set
39473b01e57SJeff Chen 				   *scan_chan_list)
39573b01e57SJeff Chen {
39673b01e57SJeff Chen 	enum nl80211_band band;
39773b01e57SJeff Chen 	struct ieee80211_supported_band *sband;
39873b01e57SJeff Chen 	struct ieee80211_channel *ch;
39973b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
40073b01e57SJeff Chen 	int chan_idx = 0, i;
40173b01e57SJeff Chen 	u16 scan_time = 0, specific_scan_time = adapter->specific_scan_time;
40273b01e57SJeff Chen 
40373b01e57SJeff Chen 	if (bgscan_cfg_in)
40473b01e57SJeff Chen 		scan_time = (u16)bgscan_cfg_in->chan_list[0].scan_time;
40573b01e57SJeff Chen 
40673b01e57SJeff Chen 	for (band = 0; (band < NUM_NL80211_BANDS); band++) {
40773b01e57SJeff Chen 		if (!priv->wdev.wiphy->bands[band])
40873b01e57SJeff Chen 			continue;
40973b01e57SJeff Chen 
41073b01e57SJeff Chen 		sband = priv->wdev.wiphy->bands[band];
41173b01e57SJeff Chen 
41273b01e57SJeff Chen 		for (i = 0; (i < sband->n_channels) ; i++) {
41373b01e57SJeff Chen 			ch = &sband->channels[i];
41473b01e57SJeff Chen 			if (ch->flags & IEEE80211_CHAN_DISABLED)
41573b01e57SJeff Chen 				continue;
41673b01e57SJeff Chen 			scan_chan_list[chan_idx].band_cfg = band;
41773b01e57SJeff Chen 
41873b01e57SJeff Chen 			if (scan_time)
41973b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
42073b01e57SJeff Chen 					cpu_to_le16(scan_time);
42173b01e57SJeff Chen 			else if (ch->flags & IEEE80211_CHAN_NO_IR)
42273b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
42373b01e57SJeff Chen 					cpu_to_le16(adapter->passive_scan_time);
42473b01e57SJeff Chen 			else
42573b01e57SJeff Chen 				scan_chan_list[chan_idx].max_scan_time =
42673b01e57SJeff Chen 					cpu_to_le16(specific_scan_time);
42773b01e57SJeff Chen 
42873b01e57SJeff Chen 			if (ch->flags & IEEE80211_CHAN_NO_IR)
42973b01e57SJeff Chen 				scan_chan_list[chan_idx].chan_scan_mode_bmap |=
43073b01e57SJeff Chen 					NXPWIFI_PASSIVE_SCAN;
43173b01e57SJeff Chen 			else
43273b01e57SJeff Chen 				scan_chan_list[chan_idx].chan_scan_mode_bmap &=
43373b01e57SJeff Chen 					~NXPWIFI_PASSIVE_SCAN;
43473b01e57SJeff Chen 
43573b01e57SJeff Chen 			scan_chan_list[chan_idx].chan_number = (u32)ch->hw_value;
43673b01e57SJeff Chen 			chan_idx++;
43773b01e57SJeff Chen 		}
43873b01e57SJeff Chen 	}
43973b01e57SJeff Chen 	return chan_idx;
44073b01e57SJeff Chen }
44173b01e57SJeff Chen 
44273b01e57SJeff Chen /* Append the rate TLV to the scan configuration command */
44373b01e57SJeff Chen static int
nxpwifi_append_rate_tlv(struct nxpwifi_private * priv,struct nxpwifi_scan_cmd_config * scan_cfg_out,u8 radio)44473b01e57SJeff Chen nxpwifi_append_rate_tlv(struct nxpwifi_private *priv,
44573b01e57SJeff Chen 			struct nxpwifi_scan_cmd_config *scan_cfg_out,
44673b01e57SJeff Chen 			u8 radio)
44773b01e57SJeff Chen {
44873b01e57SJeff Chen 	struct nxpwifi_ie_types_rates_param_set *rates_tlv;
44973b01e57SJeff Chen 	u8 rates[NXPWIFI_SUPPORTED_RATES], *tlv_pos;
45073b01e57SJeff Chen 	u32 rates_size;
45173b01e57SJeff Chen 
45273b01e57SJeff Chen 	memset(rates, 0, sizeof(rates));
45373b01e57SJeff Chen 
45473b01e57SJeff Chen 	tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
45573b01e57SJeff Chen 
45673b01e57SJeff Chen 	if (priv->scan_request)
45773b01e57SJeff Chen 		rates_size = nxpwifi_get_rates_from_cfg80211(priv, rates,
45873b01e57SJeff Chen 							     radio);
45973b01e57SJeff Chen 	else
46073b01e57SJeff Chen 		rates_size = nxpwifi_get_supported_rates(priv, rates);
46173b01e57SJeff Chen 
46273b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, CMD,
46373b01e57SJeff Chen 		    "info: SCAN_CMD: Rates size = %d\n",
46473b01e57SJeff Chen 		rates_size);
46573b01e57SJeff Chen 	rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)tlv_pos;
46673b01e57SJeff Chen 	rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
46773b01e57SJeff Chen 	rates_tlv->header.len = cpu_to_le16((u16)rates_size);
46873b01e57SJeff Chen 	memcpy(rates_tlv->rates, rates, rates_size);
46973b01e57SJeff Chen 	scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
47073b01e57SJeff Chen 
47173b01e57SJeff Chen 	return rates_size;
47273b01e57SJeff Chen }
47373b01e57SJeff Chen 
47473b01e57SJeff Chen /*
47573b01e57SJeff Chen  * Build and send multiple scan commands by chunking channel TLVs per scan
47673b01e57SJeff Chen  * limit.
47773b01e57SJeff Chen  */
47873b01e57SJeff Chen static int
nxpwifi_scan_channel_list(struct nxpwifi_private * priv,u32 max_chan_per_scan,u8 filtered_scan,struct nxpwifi_scan_cmd_config * scan_cfg_out,struct nxpwifi_ie_types_chan_list_param_set * tlv_o,struct nxpwifi_chan_scan_param_set * scan_chan_list)47973b01e57SJeff Chen nxpwifi_scan_channel_list(struct nxpwifi_private *priv,
48073b01e57SJeff Chen 			  u32 max_chan_per_scan, u8 filtered_scan,
48173b01e57SJeff Chen 			  struct nxpwifi_scan_cmd_config *scan_cfg_out,
48273b01e57SJeff Chen 			  struct nxpwifi_ie_types_chan_list_param_set *tlv_o,
48373b01e57SJeff Chen 			  struct nxpwifi_chan_scan_param_set *scan_chan_list)
48473b01e57SJeff Chen {
48573b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
48673b01e57SJeff Chen 	int ret = 0;
48773b01e57SJeff Chen 	struct nxpwifi_chan_scan_param_set *tmp_chan_list;
48873b01e57SJeff Chen 	u32 tlv_idx, rates_size, cmd_no;
48973b01e57SJeff Chen 	u32 total_scan_time;
49073b01e57SJeff Chen 	u32 done_early;
49173b01e57SJeff Chen 	u8 radio_type;
49273b01e57SJeff Chen 
49373b01e57SJeff Chen 	if (!scan_cfg_out || !tlv_o || !scan_chan_list) {
49473b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, ERROR,
49573b01e57SJeff Chen 			    "info: Scan: Null detect: %p, %p, %p\n",
49673b01e57SJeff Chen 			    scan_cfg_out, tlv_o, scan_chan_list);
49773b01e57SJeff Chen 		return -EINVAL;
49873b01e57SJeff Chen 	}
49973b01e57SJeff Chen 
50073b01e57SJeff Chen 	/* Check csa channel expiry before preparing scan list */
50173b01e57SJeff Chen 	nxpwifi_11h_get_csa_closed_channel(priv);
50273b01e57SJeff Chen 
50373b01e57SJeff Chen 	tlv_o->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
50473b01e57SJeff Chen 
50573b01e57SJeff Chen 	tmp_chan_list = scan_chan_list;
50673b01e57SJeff Chen 
50773b01e57SJeff Chen 	/*
50873b01e57SJeff Chen 	 * Iterate through the channel list and send a firmware scan command for
50973b01e57SJeff Chen 	 * each group of max_chan_per_scan channels, or individually for
51073b01e57SJeff Chen 	 * channels 1, 6, and 11 when configured.
51173b01e57SJeff Chen 	 */
51273b01e57SJeff Chen 	while (tmp_chan_list->chan_number) {
51373b01e57SJeff Chen 		tlv_idx = 0;
51473b01e57SJeff Chen 		total_scan_time = 0;
51573b01e57SJeff Chen 		radio_type = 0;
51673b01e57SJeff Chen 		tlv_o->header.len = 0;
51773b01e57SJeff Chen 		done_early = false;
51873b01e57SJeff Chen 
51973b01e57SJeff Chen 		/*
52073b01e57SJeff Chen 		 * Build the channel TLV for the scan command. Continue adding
52173b01e57SJeff Chen 		 * channel TLVs until one of the following conditions is met:
52273b01e57SJeff Chen 		 * - tlv_idx reaches the maximum allowed per scan command
52373b01e57SJeff Chen 		 * - the next channel is 0 (end of the desired channel list)
52473b01e57SJeff Chen 		 * - done_early is set (used for per-channel scanning of 1, 6,
52573b01e57SJeff Chen 		 * and 11)
52673b01e57SJeff Chen 		 */
52773b01e57SJeff Chen 		while (tlv_idx < max_chan_per_scan &&
52873b01e57SJeff Chen 		       tmp_chan_list->chan_number && !done_early) {
52973b01e57SJeff Chen 			if (tmp_chan_list->chan_number == priv->csa_chan) {
53073b01e57SJeff Chen 				tmp_chan_list++;
53173b01e57SJeff Chen 				continue;
53273b01e57SJeff Chen 			}
53373b01e57SJeff Chen 
53473b01e57SJeff Chen 			radio_type = tmp_chan_list->band_cfg;
53573b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, INFO,
53673b01e57SJeff Chen 				    "info: Scan: Chan(%3d), Band(%d),\t"
53773b01e57SJeff Chen 				    "Mode(%d, %d), Dur(%d)\n",
53873b01e57SJeff Chen 				    tmp_chan_list->chan_number,
53973b01e57SJeff Chen 				    tmp_chan_list->band_cfg,
54073b01e57SJeff Chen 				    tmp_chan_list->chan_scan_mode_bmap
54173b01e57SJeff Chen 				    & NXPWIFI_PASSIVE_SCAN,
54273b01e57SJeff Chen 				    (tmp_chan_list->chan_scan_mode_bmap
54373b01e57SJeff Chen 				    & NXPWIFI_DISABLE_CHAN_FILT) >> 1,
54473b01e57SJeff Chen 				    le16_to_cpu(tmp_chan_list->max_scan_time));
54573b01e57SJeff Chen 
54673b01e57SJeff Chen 			/* Copy the current channel TLV into the command being prepared */
54773b01e57SJeff Chen 			memcpy(&tlv_o->chan_scan_param[tlv_idx], tmp_chan_list,
54873b01e57SJeff Chen 			       sizeof(*tlv_o->chan_scan_param));
54973b01e57SJeff Chen 
55073b01e57SJeff Chen 			/*
55173b01e57SJeff Chen 			 * Increment the TLV header length by the size
55273b01e57SJeff Chen 			 * appended
55373b01e57SJeff Chen 			 */
55473b01e57SJeff Chen 			le16_unaligned_add_cpu(&tlv_o->header.len,
55573b01e57SJeff Chen 					       sizeof(*tlv_o->chan_scan_param));
55673b01e57SJeff Chen 
55773b01e57SJeff Chen 			/*
55873b01e57SJeff Chen 			 * The tlv buffer length is set to the number of bytes
55973b01e57SJeff Chen 			 * of the between the channel tlv pointer and the start
56073b01e57SJeff Chen 			 * of the tlv buffer.  This compensates for any TLVs
56173b01e57SJeff Chen 			 * that were appended before the channel list.
56273b01e57SJeff Chen 			 */
56373b01e57SJeff Chen 			scan_cfg_out->tlv_buf_len =
56473b01e57SJeff Chen 				(u32)((u8 *)tlv_o - scan_cfg_out->tlv_buf);
56573b01e57SJeff Chen 
56673b01e57SJeff Chen 			scan_cfg_out->tlv_buf_len +=
56773b01e57SJeff Chen 				(sizeof(tlv_o->header)
56873b01e57SJeff Chen 				 + le16_to_cpu(tlv_o->header.len));
56973b01e57SJeff Chen 
57073b01e57SJeff Chen 			/* Advance the index for the channel TLV being constructed. */
57173b01e57SJeff Chen 			tlv_idx++;
57273b01e57SJeff Chen 
57373b01e57SJeff Chen 			/* Count the total scan time per command */
57473b01e57SJeff Chen 			total_scan_time +=
57573b01e57SJeff Chen 				le16_to_cpu(tmp_chan_list->max_scan_time);
57673b01e57SJeff Chen 
57773b01e57SJeff Chen 			done_early = false;
57873b01e57SJeff Chen 
57973b01e57SJeff Chen 			/*
58073b01e57SJeff Chen 			 * Stop the loop if the current channel is one of 1, 6,
58173b01e57SJeff Chen 			 * or 11 and no SSID or BSSID filter is applied.
58273b01e57SJeff Chen 			 */
58373b01e57SJeff Chen 			if (!filtered_scan &&
58473b01e57SJeff Chen 			    (tmp_chan_list->chan_number == 1 ||
58573b01e57SJeff Chen 			     tmp_chan_list->chan_number == 6 ||
58673b01e57SJeff Chen 			     tmp_chan_list->chan_number == 11))
58773b01e57SJeff Chen 				done_early = true;
58873b01e57SJeff Chen 
58973b01e57SJeff Chen 			/* Advance the tmp pointer to the next channel to be scanned. */
59073b01e57SJeff Chen 			tmp_chan_list++;
59173b01e57SJeff Chen 
59273b01e57SJeff Chen 			/*
59373b01e57SJeff Chen 			 * Stop the loop if the next channel is one of 1, 6,
59473b01e57SJeff Chen 			 * or 11. This causes that channel to be scanned alone
59573b01e57SJeff Chen 			 * in the next iteration.
59673b01e57SJeff Chen 			 */
59773b01e57SJeff Chen 			if (!filtered_scan &&
59873b01e57SJeff Chen 			    (tmp_chan_list->chan_number == 1 ||
59973b01e57SJeff Chen 			     tmp_chan_list->chan_number == 6 ||
60073b01e57SJeff Chen 			     tmp_chan_list->chan_number == 11))
60173b01e57SJeff Chen 				done_early = true;
60273b01e57SJeff Chen 		}
60373b01e57SJeff Chen 
60473b01e57SJeff Chen 		/* Ensure the total scan time does not exceed the scan-command timeout. */
60573b01e57SJeff Chen 		if (total_scan_time > NXPWIFI_MAX_TOTAL_SCAN_TIME) {
60673b01e57SJeff Chen 			nxpwifi_dbg(priv->adapter, ERROR,
60773b01e57SJeff Chen 				    "total scan time %dms\t"
60873b01e57SJeff Chen 				    "is over limit (%dms), scan skipped\n",
60973b01e57SJeff Chen 				    total_scan_time,
61073b01e57SJeff Chen 				    NXPWIFI_MAX_TOTAL_SCAN_TIME);
61173b01e57SJeff Chen 			ret = -EINVAL;
61273b01e57SJeff Chen 			break;
61373b01e57SJeff Chen 		}
61473b01e57SJeff Chen 
61573b01e57SJeff Chen 		rates_size = nxpwifi_append_rate_tlv(priv, scan_cfg_out,
61673b01e57SJeff Chen 						     radio_type);
61773b01e57SJeff Chen 
61873b01e57SJeff Chen 		if (priv->adapter->ext_scan)
61973b01e57SJeff Chen 			cmd_no = HOST_CMD_802_11_SCAN_EXT;
62073b01e57SJeff Chen 		else
62173b01e57SJeff Chen 			cmd_no = HOST_CMD_802_11_SCAN;
62273b01e57SJeff Chen 
62373b01e57SJeff Chen 		ret = nxpwifi_send_cmd(priv, cmd_no, HOST_ACT_GEN_SET,
62473b01e57SJeff Chen 				       0, scan_cfg_out, false);
62573b01e57SJeff Chen 
62673b01e57SJeff Chen 		/*
62773b01e57SJeff Chen 		 * The rate element is updated for each scan command, but the
62873b01e57SJeff Chen 		 * same starting pointer is reused, so the previous rate element
62973b01e57SJeff Chen 		 * in scan_cfg_out->buf is overwritten.
63073b01e57SJeff Chen 		 */
63173b01e57SJeff Chen 		scan_cfg_out->tlv_buf_len -=
63273b01e57SJeff Chen 		    sizeof(struct nxpwifi_ie_types_header) + rates_size;
63373b01e57SJeff Chen 
63473b01e57SJeff Chen 		if (ret) {
63573b01e57SJeff Chen 			nxpwifi_cancel_pending_scan_cmd(adapter);
63673b01e57SJeff Chen 			break;
63773b01e57SJeff Chen 		}
63873b01e57SJeff Chen 	}
63973b01e57SJeff Chen 
64073b01e57SJeff Chen 	return ret;
64173b01e57SJeff Chen }
64273b01e57SJeff Chen 
64373b01e57SJeff Chen /*
64473b01e57SJeff Chen  * Build final scan config from user params, disabling missing filters and using
64573b01e57SJeff Chen  * defaults.
64673b01e57SJeff Chen  */
64773b01e57SJeff Chen static void
nxpwifi_config_scan(struct nxpwifi_private * priv,const struct nxpwifi_user_scan_cfg * user_scan_in,struct nxpwifi_scan_cmd_config * scan_cfg_out,struct nxpwifi_ie_types_chan_list_param_set ** chan_list_out,struct nxpwifi_chan_scan_param_set * scan_chan_list,u8 * max_chan_per_scan,u8 * filtered_scan,u8 * scan_current_only)64873b01e57SJeff Chen nxpwifi_config_scan(struct nxpwifi_private *priv,
64973b01e57SJeff Chen 		    const struct nxpwifi_user_scan_cfg *user_scan_in,
65073b01e57SJeff Chen 		    struct nxpwifi_scan_cmd_config *scan_cfg_out,
65173b01e57SJeff Chen 		    struct nxpwifi_ie_types_chan_list_param_set **chan_list_out,
65273b01e57SJeff Chen 		    struct nxpwifi_chan_scan_param_set *scan_chan_list,
65373b01e57SJeff Chen 		    u8 *max_chan_per_scan, u8 *filtered_scan,
65473b01e57SJeff Chen 		    u8 *scan_current_only)
65573b01e57SJeff Chen {
65673b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
65773b01e57SJeff Chen 	struct nxpwifi_ie_types_num_probes *num_probes_tlv;
65873b01e57SJeff Chen 	struct nxpwifi_ie_types_scan_chan_gap *chan_gap_tlv;
65973b01e57SJeff Chen 	struct nxpwifi_ie_types_random_mac *random_mac_tlv;
66073b01e57SJeff Chen 	struct nxpwifi_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
66173b01e57SJeff Chen 	struct nxpwifi_ie_types_bssid_list *bssid_tlv;
66273b01e57SJeff Chen 	struct nxpwifi_ie_types_extcap *ext_cap;
66373b01e57SJeff Chen 	u8 *ext_capab = NULL;
66473b01e57SJeff Chen 	u8 *tlv_pos;
66573b01e57SJeff Chen 	u32 num_probes;
66673b01e57SJeff Chen 	u32 ssid_len;
66773b01e57SJeff Chen 	u32 chan_idx;
66873b01e57SJeff Chen 	u32 scan_time;
66973b01e57SJeff Chen 	u32 scan_type;
67073b01e57SJeff Chen 	u16 scan_dur;
67173b01e57SJeff Chen 	u8 channel;
67273b01e57SJeff Chen 	u8 radio_type;
67373b01e57SJeff Chen 	int i, vsid;
67473b01e57SJeff Chen 	u8 ssid_filter;
67573b01e57SJeff Chen 	struct nxpwifi_ie_types_htcap *ht_cap;
67673b01e57SJeff Chen 	struct nxpwifi_ie_types_bss_mode *bss_mode;
67773b01e57SJeff Chen 	struct nxpwifi_ie_types_vhtcap *vht_cap;
67873b01e57SJeff Chen 	struct nxpwifi_ie_types_he_cap *he_cap;
67973b01e57SJeff Chen 
68073b01e57SJeff Chen 	/*
68173b01e57SJeff Chen 	 * tlv_buf_len is recalculated for each scan command. TLVs added in this
68273b01e57SJeff Chen 	 * routine are preserved because the send routine appends channel TLVs
68373b01e57SJeff Chen 	 * at chan_list_out. The difference between chan_list_out and the start
68473b01e57SJeff Chen 	 * of the TLV buffer determines the size of the TLVs added here.
68573b01e57SJeff Chen 	 */
68673b01e57SJeff Chen 	scan_cfg_out->tlv_buf_len = 0;
68773b01e57SJeff Chen 
68873b01e57SJeff Chen 	/*
68973b01e57SJeff Chen 	 * Running TLV pointer. It is assigned to chan_list_out at the end of
69073b01e57SJeff Chen 	 * the function so later routines know where channel TLVs can be
69173b01e57SJeff Chen 	 * appended in the command buffer.
69273b01e57SJeff Chen 	 */
69373b01e57SJeff Chen 	tlv_pos = scan_cfg_out->tlv_buf;
69473b01e57SJeff Chen 
69573b01e57SJeff Chen 	/*
69673b01e57SJeff Chen 	 * Initialize the scan as un-filtered; the flag is later set to TRUE
69773b01e57SJeff Chen 	 * below if a SSID or BSSID filter is sent in the command
69873b01e57SJeff Chen 	 */
69973b01e57SJeff Chen 	*filtered_scan = false;
70073b01e57SJeff Chen 
70173b01e57SJeff Chen 	/*
70273b01e57SJeff Chen 	 * Initialize the scan as not being only on the current channel.  If
70373b01e57SJeff Chen 	 * the channel list is customized, only contains one channel, and is
70473b01e57SJeff Chen 	 * the active channel, this is set true and data flow is not halted.
70573b01e57SJeff Chen 	 */
70673b01e57SJeff Chen 	*scan_current_only = false;
70773b01e57SJeff Chen 
70873b01e57SJeff Chen 	if (user_scan_in) {
70973b01e57SJeff Chen 		u8 tmpaddr[ETH_ALEN];
71073b01e57SJeff Chen 
71173b01e57SJeff Chen 		/*
71273b01e57SJeff Chen 		 * Default the ssid_filter flag to TRUE, set false under
71373b01e57SJeff Chen 		 * certain wildcard conditions and qualified by the existence
71473b01e57SJeff Chen 		 * of an SSID list before marking the scan as filtered
71573b01e57SJeff Chen 		 */
71673b01e57SJeff Chen 		ssid_filter = true;
71773b01e57SJeff Chen 
71873b01e57SJeff Chen 		/*
71973b01e57SJeff Chen 		 * Set the BSS type scan filter, use Adapter setting if
72073b01e57SJeff Chen 		 * unset
72173b01e57SJeff Chen 		 */
72273b01e57SJeff Chen 		scan_cfg_out->bss_mode =
72373b01e57SJeff Chen 			(u8)(user_scan_in->bss_mode ?: adapter->scan_mode);
72473b01e57SJeff Chen 
72573b01e57SJeff Chen 		/*
72673b01e57SJeff Chen 		 * Set the number of probes to send, use Adapter setting
72773b01e57SJeff Chen 		 * if unset
72873b01e57SJeff Chen 		 */
72973b01e57SJeff Chen 		num_probes = user_scan_in->num_probes ?: adapter->scan_probes;
73073b01e57SJeff Chen 
73173b01e57SJeff Chen 		/*
73273b01e57SJeff Chen 		 * Set the BSSID filter to the incoming configuration,
73373b01e57SJeff Chen 		 * if non-zero.  If not set, it will remain disabled
73473b01e57SJeff Chen 		 * (all zeros).
73573b01e57SJeff Chen 		 */
73673b01e57SJeff Chen 		memcpy(scan_cfg_out->specific_bssid,
73773b01e57SJeff Chen 		       user_scan_in->specific_bssid,
73873b01e57SJeff Chen 		       sizeof(scan_cfg_out->specific_bssid));
73973b01e57SJeff Chen 
74073b01e57SJeff Chen 		memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
74173b01e57SJeff Chen 
74273b01e57SJeff Chen 		if (adapter->ext_scan &&
74373b01e57SJeff Chen 		    !is_zero_ether_addr(tmpaddr)) {
74473b01e57SJeff Chen 			bssid_tlv =
74573b01e57SJeff Chen 				(struct nxpwifi_ie_types_bssid_list *)tlv_pos;
74673b01e57SJeff Chen 			bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID);
74773b01e57SJeff Chen 			bssid_tlv->header.len = cpu_to_le16(ETH_ALEN);
74873b01e57SJeff Chen 			memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid,
74973b01e57SJeff Chen 			       ETH_ALEN);
75073b01e57SJeff Chen 			tlv_pos += sizeof(struct nxpwifi_ie_types_bssid_list);
75173b01e57SJeff Chen 		}
75273b01e57SJeff Chen 
75373b01e57SJeff Chen 		for (i = 0; i < user_scan_in->num_ssids; i++) {
75473b01e57SJeff Chen 			ssid_len = user_scan_in->ssid_list[i].ssid_len;
75573b01e57SJeff Chen 
75673b01e57SJeff Chen 			wildcard_ssid_tlv =
75773b01e57SJeff Chen 				(struct nxpwifi_ie_types_wildcard_ssid_params *)
75873b01e57SJeff Chen 				tlv_pos;
75973b01e57SJeff Chen 			wildcard_ssid_tlv->header.type =
76073b01e57SJeff Chen 				cpu_to_le16(TLV_TYPE_WILDCARDSSID);
76173b01e57SJeff Chen 			wildcard_ssid_tlv->header.len =
76273b01e57SJeff Chen 				cpu_to_le16((u16)(ssid_len + sizeof(u8)));
76373b01e57SJeff Chen 
76473b01e57SJeff Chen 			/*
76573b01e57SJeff Chen 			 * max_ssid_length = 0 tells firmware to perform
76673b01e57SJeff Chen 			 * specific scan for the SSID filled, whereas
76773b01e57SJeff Chen 			 * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
76873b01e57SJeff Chen 			 * wildcard scan.
76973b01e57SJeff Chen 			 */
77073b01e57SJeff Chen 			if (ssid_len)
77173b01e57SJeff Chen 				wildcard_ssid_tlv->max_ssid_length = 0;
77273b01e57SJeff Chen 			else
77373b01e57SJeff Chen 				wildcard_ssid_tlv->max_ssid_length =
77473b01e57SJeff Chen 							IEEE80211_MAX_SSID_LEN;
77573b01e57SJeff Chen 
77673b01e57SJeff Chen 			if (!memcmp(user_scan_in->ssid_list[i].ssid,
77773b01e57SJeff Chen 				    "DIRECT-", 7))
77873b01e57SJeff Chen 				wildcard_ssid_tlv->max_ssid_length = 0xfe;
77973b01e57SJeff Chen 
78073b01e57SJeff Chen 			memcpy(wildcard_ssid_tlv->ssid,
78173b01e57SJeff Chen 			       user_scan_in->ssid_list[i].ssid, ssid_len);
78273b01e57SJeff Chen 
78373b01e57SJeff Chen 			tlv_pos += (sizeof(wildcard_ssid_tlv->header)
78473b01e57SJeff Chen 				+ le16_to_cpu(wildcard_ssid_tlv->header.len));
78573b01e57SJeff Chen 
78673b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
78773b01e57SJeff Chen 				    "info: scan: ssid[%d]: %s, %d\n",
78873b01e57SJeff Chen 				    i, wildcard_ssid_tlv->ssid,
78973b01e57SJeff Chen 				    wildcard_ssid_tlv->max_ssid_length);
79073b01e57SJeff Chen 
79173b01e57SJeff Chen 			/*
79273b01e57SJeff Chen 			 * Empty wildcard ssid with a maxlen will match many or
79373b01e57SJeff Chen 			 * potentially all SSIDs (maxlen == 32), therefore do
79473b01e57SJeff Chen 			 * not treat the scan as
79573b01e57SJeff Chen 			 * filtered.
79673b01e57SJeff Chen 			 */
79773b01e57SJeff Chen 			if (!ssid_len && wildcard_ssid_tlv->max_ssid_length)
79873b01e57SJeff Chen 				ssid_filter = false;
79973b01e57SJeff Chen 		}
80073b01e57SJeff Chen 
80173b01e57SJeff Chen 		/*
80273b01e57SJeff Chen 		 * The default number of channels sent in the command is low to
80373b01e57SJeff Chen 		 *  ensure the response buffer from the firmware does not
80473b01e57SJeff Chen 		 *  truncate scan results.  That is not an issue with an SSID
80573b01e57SJeff Chen 		 *  or BSSID filter applied to the scan results in the firmware.
80673b01e57SJeff Chen 		 */
80773b01e57SJeff Chen 		memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
80873b01e57SJeff Chen 		if ((i && ssid_filter) ||
80973b01e57SJeff Chen 		    !is_zero_ether_addr(tmpaddr))
81073b01e57SJeff Chen 			*filtered_scan = true;
81173b01e57SJeff Chen 
81273b01e57SJeff Chen 		if (user_scan_in->scan_chan_gap) {
81373b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
81473b01e57SJeff Chen 				    "info: scan: channel gap = %d\n",
81573b01e57SJeff Chen 				    user_scan_in->scan_chan_gap);
81673b01e57SJeff Chen 			*max_chan_per_scan =
81773b01e57SJeff Chen 					NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN;
81873b01e57SJeff Chen 
81973b01e57SJeff Chen 			chan_gap_tlv = (void *)tlv_pos;
82073b01e57SJeff Chen 			chan_gap_tlv->header.type =
82173b01e57SJeff Chen 					 cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP);
82273b01e57SJeff Chen 			chan_gap_tlv->header.len =
82373b01e57SJeff Chen 				    cpu_to_le16(sizeof(chan_gap_tlv->chan_gap));
82473b01e57SJeff Chen 			chan_gap_tlv->chan_gap =
82573b01e57SJeff Chen 				     cpu_to_le16((user_scan_in->scan_chan_gap));
82673b01e57SJeff Chen 			tlv_pos +=
82773b01e57SJeff Chen 				  sizeof(struct nxpwifi_ie_types_scan_chan_gap);
82873b01e57SJeff Chen 		}
82973b01e57SJeff Chen 
83073b01e57SJeff Chen 		if (!is_zero_ether_addr(user_scan_in->random_mac)) {
83173b01e57SJeff Chen 			random_mac_tlv = (void *)tlv_pos;
83273b01e57SJeff Chen 			random_mac_tlv->header.type =
83373b01e57SJeff Chen 					 cpu_to_le16(TLV_TYPE_RANDOM_MAC);
83473b01e57SJeff Chen 			random_mac_tlv->header.len =
83573b01e57SJeff Chen 				    cpu_to_le16(sizeof(random_mac_tlv->mac));
83673b01e57SJeff Chen 			ether_addr_copy(random_mac_tlv->mac,
83773b01e57SJeff Chen 					user_scan_in->random_mac);
83873b01e57SJeff Chen 			tlv_pos +=
83973b01e57SJeff Chen 				  sizeof(struct nxpwifi_ie_types_random_mac);
84073b01e57SJeff Chen 		}
84173b01e57SJeff Chen 	} else {
84273b01e57SJeff Chen 		scan_cfg_out->bss_mode = (u8)adapter->scan_mode;
84373b01e57SJeff Chen 		num_probes = adapter->scan_probes;
84473b01e57SJeff Chen 	}
84573b01e57SJeff Chen 
84673b01e57SJeff Chen 	/*
84773b01e57SJeff Chen 	 * If a specific BSSID or SSID is used, the number of channels in the
84873b01e57SJeff Chen 	 *  scan command will be increased to the absolute maximum.
84973b01e57SJeff Chen 	 */
85073b01e57SJeff Chen 	if (*filtered_scan) {
85173b01e57SJeff Chen 		*max_chan_per_scan = NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN;
85273b01e57SJeff Chen 	} else {
85373b01e57SJeff Chen 		if (!priv->media_connected)
85473b01e57SJeff Chen 			*max_chan_per_scan = NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD;
85573b01e57SJeff Chen 		else
85673b01e57SJeff Chen 			*max_chan_per_scan =
85773b01e57SJeff Chen 					NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD / 2;
85873b01e57SJeff Chen 	}
85973b01e57SJeff Chen 
86073b01e57SJeff Chen 	if (adapter->ext_scan) {
86173b01e57SJeff Chen 		bss_mode = (struct nxpwifi_ie_types_bss_mode *)tlv_pos;
86273b01e57SJeff Chen 		bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE);
86373b01e57SJeff Chen 		bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode));
86473b01e57SJeff Chen 		bss_mode->bss_mode = scan_cfg_out->bss_mode;
86573b01e57SJeff Chen 		tlv_pos += sizeof(bss_mode->header) +
86673b01e57SJeff Chen 			   le16_to_cpu(bss_mode->header.len);
86773b01e57SJeff Chen 	}
86873b01e57SJeff Chen 
86973b01e57SJeff Chen 	/*
87073b01e57SJeff Chen 	 * If the input config or adapter has the number of Probes set,
87173b01e57SJeff Chen 	 * add tlv
87273b01e57SJeff Chen 	 */
87373b01e57SJeff Chen 	if (num_probes) {
87473b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
87573b01e57SJeff Chen 			    "info: scan: num_probes = %d\n",
87673b01e57SJeff Chen 			    num_probes);
87773b01e57SJeff Chen 
87873b01e57SJeff Chen 		num_probes_tlv = (struct nxpwifi_ie_types_num_probes *)tlv_pos;
87973b01e57SJeff Chen 		num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
88073b01e57SJeff Chen 		num_probes_tlv->header.len =
88173b01e57SJeff Chen 			cpu_to_le16(sizeof(num_probes_tlv->num_probes));
88273b01e57SJeff Chen 		num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes);
88373b01e57SJeff Chen 
88473b01e57SJeff Chen 		tlv_pos += sizeof(num_probes_tlv->header) +
88573b01e57SJeff Chen 			le16_to_cpu(num_probes_tlv->header.len);
88673b01e57SJeff Chen 	}
88773b01e57SJeff Chen 
88873b01e57SJeff Chen 	if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
88973b01e57SJeff Chen 	    (priv->config_bands & BAND_GN ||
89073b01e57SJeff Chen 	     priv->config_bands & BAND_AN)) {
89173b01e57SJeff Chen 		ht_cap = (struct nxpwifi_ie_types_htcap *)tlv_pos;
89273b01e57SJeff Chen 		memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap));
89373b01e57SJeff Chen 		ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
89473b01e57SJeff Chen 		ht_cap->header.len =
89573b01e57SJeff Chen 			cpu_to_le16(sizeof(struct ieee80211_ht_cap));
89673b01e57SJeff Chen 		radio_type =
89773b01e57SJeff Chen 			nxpwifi_band_to_radio_type(priv->config_bands);
89873b01e57SJeff Chen 		nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
89973b01e57SJeff Chen 		tlv_pos += sizeof(struct nxpwifi_ie_types_htcap);
90073b01e57SJeff Chen 	}
90173b01e57SJeff Chen 
90273b01e57SJeff Chen 	if (ISSUPP_11ACENABLED(adapter->fw_cap_info) &&
90373b01e57SJeff Chen 	    (priv->config_bands & BAND_AAC)) {
90473b01e57SJeff Chen 		vht_cap = (struct nxpwifi_ie_types_vhtcap *)tlv_pos;
90573b01e57SJeff Chen 		memset(vht_cap, 0, sizeof(struct nxpwifi_ie_types_vhtcap));
90673b01e57SJeff Chen 		vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
90773b01e57SJeff Chen 		vht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_vht_cap));
90873b01e57SJeff Chen 		nxpwifi_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, priv->config_bands);
90973b01e57SJeff Chen 		tlv_pos += sizeof(*vht_cap);
91073b01e57SJeff Chen 	}
91173b01e57SJeff Chen 
91273b01e57SJeff Chen 	if (ISSUPP_11AXENABLED(adapter->fw_cap_ext) &&
91373b01e57SJeff Chen 	    (priv->config_bands & BAND_GAX ||
91473b01e57SJeff Chen 	     priv->config_bands & BAND_AAX)) {
91573b01e57SJeff Chen 		he_cap = (struct nxpwifi_ie_types_he_cap *)tlv_pos;
91673b01e57SJeff Chen 		memset(he_cap, 0, sizeof(struct nxpwifi_ie_types_he_cap));
91773b01e57SJeff Chen 		tlv_pos += nxpwifi_fill_he_cap_tlv(priv, he_cap, priv->config_bands);
91873b01e57SJeff Chen 	}
91973b01e57SJeff Chen 
92073b01e57SJeff Chen 	if (nxpwifi_is_sta_11ax_twt_req_supported(priv)) {
92173b01e57SJeff Chen 		for (vsid = 0; vsid < NXPWIFI_MAX_VSIE_NUM; vsid++) {
92273b01e57SJeff Chen 			if (priv->vs_ie[vsid].mask & NXPWIFI_VSIE_MASK_SCAN) {
92373b01e57SJeff Chen 				ext_capab = (u8 *)cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY,
92473b01e57SJeff Chen 								   priv->vs_ie[vsid].ie,
92573b01e57SJeff Chen 								   sizeof(priv->vs_ie[vsid].ie));
92673b01e57SJeff Chen 				break;
92773b01e57SJeff Chen 			}
92873b01e57SJeff Chen 		}
92973b01e57SJeff Chen 
93073b01e57SJeff Chen 		if (ext_capab) {
93173b01e57SJeff Chen 			ext_capab += 2;
93273b01e57SJeff Chen 		} else {
93373b01e57SJeff Chen 			ext_cap = (struct nxpwifi_ie_types_extcap *)tlv_pos;
93473b01e57SJeff Chen 			memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap) +
93573b01e57SJeff Chen 			       NXPWIFI_EXT_CAPAB_IE_LEN);
93673b01e57SJeff Chen 			ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
93773b01e57SJeff Chen 			ext_cap->header.len = cpu_to_le16(NXPWIFI_EXT_CAPAB_IE_LEN);
93873b01e57SJeff Chen 			ext_capab = ext_cap->ext_capab;
93973b01e57SJeff Chen 			tlv_pos += sizeof(struct nxpwifi_ie_types_extcap) +
94073b01e57SJeff Chen 				le16_to_cpu(ext_cap->header.len);
94173b01e57SJeff Chen 		}
94273b01e57SJeff Chen 
94373b01e57SJeff Chen 		ext_capab[9] |= WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT;
94473b01e57SJeff Chen 	}
94573b01e57SJeff Chen 
94673b01e57SJeff Chen 	/* Append vendor specific element TLV */
94773b01e57SJeff Chen 	nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_SCAN, &tlv_pos);
94873b01e57SJeff Chen 
94973b01e57SJeff Chen 	/*
95073b01e57SJeff Chen 	 * Set the channel TLV output pointer to the end of the newly added TLVs
95173b01e57SJeff Chen 	 * (SSID, num_probes). Channel TLVs for each scan will be appended after
95273b01e57SJeff Chen 	 * these, preserving previously added TLVs.
95373b01e57SJeff Chen 	 */
95473b01e57SJeff Chen 	*chan_list_out =
95573b01e57SJeff Chen 		(struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos;
95673b01e57SJeff Chen 
95773b01e57SJeff Chen 	if (user_scan_in && user_scan_in->chan_list[0].chan_number) {
95873b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
95973b01e57SJeff Chen 			    "info: Scan: Using supplied channel list\n");
96073b01e57SJeff Chen 
96173b01e57SJeff Chen 		for (chan_idx = 0;
96273b01e57SJeff Chen 		     chan_idx < NXPWIFI_USER_SCAN_CHAN_MAX &&
96373b01e57SJeff Chen 		     user_scan_in->chan_list[chan_idx].chan_number;
96473b01e57SJeff Chen 		     chan_idx++) {
96573b01e57SJeff Chen 			channel = user_scan_in->chan_list[chan_idx].chan_number;
96673b01e57SJeff Chen 			scan_chan_list[chan_idx].chan_number = channel;
96773b01e57SJeff Chen 
96873b01e57SJeff Chen 			radio_type =
96973b01e57SJeff Chen 				user_scan_in->chan_list[chan_idx].radio_type;
97073b01e57SJeff Chen 			scan_chan_list[chan_idx].band_cfg = radio_type;
97173b01e57SJeff Chen 
97273b01e57SJeff Chen 			scan_type = user_scan_in->chan_list[chan_idx].scan_type;
97373b01e57SJeff Chen 
97473b01e57SJeff Chen 			if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE)
97573b01e57SJeff Chen 				scan_chan_list[chan_idx].chan_scan_mode_bmap |=
97673b01e57SJeff Chen 					(NXPWIFI_PASSIVE_SCAN |
97773b01e57SJeff Chen 					 NXPWIFI_HIDDEN_SSID_REPORT);
97873b01e57SJeff Chen 			else
97973b01e57SJeff Chen 				scan_chan_list[chan_idx].chan_scan_mode_bmap &=
98073b01e57SJeff Chen 					~NXPWIFI_PASSIVE_SCAN;
98173b01e57SJeff Chen 
98273b01e57SJeff Chen 			scan_chan_list[chan_idx].chan_scan_mode_bmap |=
98373b01e57SJeff Chen 				NXPWIFI_DISABLE_CHAN_FILT;
98473b01e57SJeff Chen 
98573b01e57SJeff Chen 			scan_time = user_scan_in->chan_list[chan_idx].scan_time;
98673b01e57SJeff Chen 
98773b01e57SJeff Chen 			if (scan_time) {
98873b01e57SJeff Chen 				scan_dur = (u16)scan_time;
98973b01e57SJeff Chen 			} else {
99073b01e57SJeff Chen 				if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE)
99173b01e57SJeff Chen 					scan_dur = adapter->passive_scan_time;
99273b01e57SJeff Chen 				else if (*filtered_scan)
99373b01e57SJeff Chen 					scan_dur = adapter->specific_scan_time;
99473b01e57SJeff Chen 				else
99573b01e57SJeff Chen 					scan_dur = adapter->active_scan_time;
99673b01e57SJeff Chen 			}
99773b01e57SJeff Chen 
99873b01e57SJeff Chen 			scan_chan_list[chan_idx].min_scan_time =
99973b01e57SJeff Chen 				cpu_to_le16(scan_dur);
100073b01e57SJeff Chen 			scan_chan_list[chan_idx].max_scan_time =
100173b01e57SJeff Chen 				cpu_to_le16(scan_dur);
100273b01e57SJeff Chen 		}
100373b01e57SJeff Chen 
100473b01e57SJeff Chen 		/* Check if we are only scanning the current channel */
100573b01e57SJeff Chen 		if (chan_idx == 1 &&
100673b01e57SJeff Chen 		    user_scan_in->chan_list[0].chan_number ==
100773b01e57SJeff Chen 		    priv->curr_bss_params.bss_descriptor.channel) {
100873b01e57SJeff Chen 			*scan_current_only = true;
100973b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
101073b01e57SJeff Chen 				    "info: Scan: Scanning current channel only\n");
101173b01e57SJeff Chen 		}
101273b01e57SJeff Chen 	} else {
101373b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
101473b01e57SJeff Chen 			    "info: Scan: Creating full region channel list\n");
101573b01e57SJeff Chen 		nxpwifi_scan_create_channel_list(priv, user_scan_in,
101673b01e57SJeff Chen 						 scan_chan_list,
101773b01e57SJeff Chen 						 *filtered_scan);
101873b01e57SJeff Chen 	}
101973b01e57SJeff Chen }
102073b01e57SJeff Chen 
102173b01e57SJeff Chen /* Parse the beacon buffer and update the BSS descriptor fields. */
nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter * adapter,struct nxpwifi_bssdescriptor * bss_entry)102273b01e57SJeff Chen int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter,
102373b01e57SJeff Chen 				    struct nxpwifi_bssdescriptor *bss_entry)
102473b01e57SJeff Chen {
102573b01e57SJeff Chen 	u8 element_id;
102673b01e57SJeff Chen 	u16 elem_size = sizeof(struct element);
102773b01e57SJeff Chen 	struct ieee_types_fh_param_set *fh_param_set;
102873b01e57SJeff Chen 	struct ieee_types_ds_param_set *ds_param_set;
102973b01e57SJeff Chen 	struct ieee_types_cf_param_set *cf_param_set;
103073b01e57SJeff Chen 	u8 *current_ptr;
103173b01e57SJeff Chen 	u8 *rate;
103273b01e57SJeff Chen 	u8 element_len;
103373b01e57SJeff Chen 	u16 total_ie_len;
103473b01e57SJeff Chen 	u8 bytes_to_copy;
103573b01e57SJeff Chen 	u8 rate_size;
103673b01e57SJeff Chen 	u8 found_data_rate_ie;
103773b01e57SJeff Chen 	u32 bytes_left;
103873b01e57SJeff Chen 	struct ieee_types_vendor_specific *vendor_ie;
103973b01e57SJeff Chen 	const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
104073b01e57SJeff Chen 	const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
104173b01e57SJeff Chen 	struct element *elem;
104273b01e57SJeff Chen 
104373b01e57SJeff Chen 	found_data_rate_ie = false;
104473b01e57SJeff Chen 	rate_size = 0;
104573b01e57SJeff Chen 	current_ptr = bss_entry->beacon_buf;
104673b01e57SJeff Chen 	bytes_left = bss_entry->beacon_buf_size;
104773b01e57SJeff Chen 
104873b01e57SJeff Chen 	/* Process variable element */
104973b01e57SJeff Chen 	while (bytes_left >= 2) {
105073b01e57SJeff Chen 		element_id = *current_ptr;
105173b01e57SJeff Chen 		element_len = *(current_ptr + 1);
105273b01e57SJeff Chen 		total_ie_len = element_len + elem_size;
105373b01e57SJeff Chen 
105473b01e57SJeff Chen 		if (bytes_left < total_ie_len) {
105573b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
105673b01e57SJeff Chen 				    "err: InterpretIE: in processing\t"
105773b01e57SJeff Chen 				    "element, bytes left < element length\n");
105873b01e57SJeff Chen 			return -EINVAL;
105973b01e57SJeff Chen 		}
106073b01e57SJeff Chen 		switch (element_id) {
106173b01e57SJeff Chen 		case WLAN_EID_SSID:
106273b01e57SJeff Chen 			if (element_len > IEEE80211_MAX_SSID_LEN)
106373b01e57SJeff Chen 				return -EINVAL;
106473b01e57SJeff Chen 			bss_entry->ssid.ssid_len = element_len;
106573b01e57SJeff Chen 			memcpy(bss_entry->ssid.ssid, (current_ptr + 2),
106673b01e57SJeff Chen 			       element_len);
106773b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
106873b01e57SJeff Chen 				    "info: InterpretIE: ssid: %-32s\n",
106973b01e57SJeff Chen 				    bss_entry->ssid.ssid);
107073b01e57SJeff Chen 			break;
107173b01e57SJeff Chen 
107273b01e57SJeff Chen 		case WLAN_EID_SUPP_RATES:
107373b01e57SJeff Chen 			if (element_len > NXPWIFI_SUPPORTED_RATES)
107473b01e57SJeff Chen 				return -EINVAL;
107573b01e57SJeff Chen 			memcpy(bss_entry->data_rates, current_ptr + 2,
107673b01e57SJeff Chen 			       element_len);
107773b01e57SJeff Chen 			memcpy(bss_entry->supported_rates, current_ptr + 2,
107873b01e57SJeff Chen 			       element_len);
107973b01e57SJeff Chen 			rate_size = element_len;
108073b01e57SJeff Chen 			found_data_rate_ie = true;
108173b01e57SJeff Chen 			break;
108273b01e57SJeff Chen 
108373b01e57SJeff Chen 		case WLAN_EID_FH_PARAMS:
108473b01e57SJeff Chen 			if (total_ie_len < sizeof(*fh_param_set))
108573b01e57SJeff Chen 				return -EINVAL;
108673b01e57SJeff Chen 			fh_param_set =
108773b01e57SJeff Chen 				(struct ieee_types_fh_param_set *)current_ptr;
108873b01e57SJeff Chen 			memcpy(&bss_entry->phy_param_set.fh_param_set,
108973b01e57SJeff Chen 			       fh_param_set,
109073b01e57SJeff Chen 			       sizeof(struct ieee_types_fh_param_set));
109173b01e57SJeff Chen 			break;
109273b01e57SJeff Chen 
109373b01e57SJeff Chen 		case WLAN_EID_DS_PARAMS:
109473b01e57SJeff Chen 			if (total_ie_len < sizeof(*ds_param_set))
109573b01e57SJeff Chen 				return -EINVAL;
109673b01e57SJeff Chen 			ds_param_set =
109773b01e57SJeff Chen 				(struct ieee_types_ds_param_set *)current_ptr;
109873b01e57SJeff Chen 
109973b01e57SJeff Chen 			bss_entry->channel = ds_param_set->current_chan;
110073b01e57SJeff Chen 
110173b01e57SJeff Chen 			memcpy(&bss_entry->phy_param_set.ds_param_set,
110273b01e57SJeff Chen 			       ds_param_set,
110373b01e57SJeff Chen 			       sizeof(struct ieee_types_ds_param_set));
110473b01e57SJeff Chen 			break;
110573b01e57SJeff Chen 
110673b01e57SJeff Chen 		case WLAN_EID_CF_PARAMS:
110773b01e57SJeff Chen 			if (total_ie_len < sizeof(*cf_param_set))
110873b01e57SJeff Chen 				return -EINVAL;
110973b01e57SJeff Chen 			cf_param_set =
111073b01e57SJeff Chen 				(struct ieee_types_cf_param_set *)current_ptr;
111173b01e57SJeff Chen 			memcpy(&bss_entry->cf_param_set,
111273b01e57SJeff Chen 			       cf_param_set,
111373b01e57SJeff Chen 			       sizeof(struct ieee_types_cf_param_set));
111473b01e57SJeff Chen 			break;
111573b01e57SJeff Chen 
111673b01e57SJeff Chen 		case WLAN_EID_ERP_INFO:
111773b01e57SJeff Chen 			if (!element_len)
111873b01e57SJeff Chen 				return -EINVAL;
111973b01e57SJeff Chen 			bss_entry->erp_flags = *(current_ptr + 2);
112073b01e57SJeff Chen 			break;
112173b01e57SJeff Chen 
112273b01e57SJeff Chen 		case WLAN_EID_PWR_CONSTRAINT:
112373b01e57SJeff Chen 			if (!element_len)
112473b01e57SJeff Chen 				return -EINVAL;
112573b01e57SJeff Chen 			bss_entry->local_constraint = *(current_ptr + 2);
112673b01e57SJeff Chen 			bss_entry->sensed_11h = true;
112773b01e57SJeff Chen 			break;
112873b01e57SJeff Chen 
112973b01e57SJeff Chen 		case WLAN_EID_CHANNEL_SWITCH:
113073b01e57SJeff Chen 			bss_entry->chan_sw_ie_present = true;
113173b01e57SJeff Chen 			fallthrough;
113273b01e57SJeff Chen 		case WLAN_EID_PWR_CAPABILITY:
113373b01e57SJeff Chen 		case WLAN_EID_TPC_REPORT:
113473b01e57SJeff Chen 		case WLAN_EID_QUIET:
113573b01e57SJeff Chen 			bss_entry->sensed_11h = true;
113673b01e57SJeff Chen 			break;
113773b01e57SJeff Chen 
113873b01e57SJeff Chen 		case WLAN_EID_EXT_SUPP_RATES:
113973b01e57SJeff Chen 			/*
114073b01e57SJeff Chen 			 * Only process extended supported rate
114173b01e57SJeff Chen 			 * if data rate is already found.
114273b01e57SJeff Chen 			 * Data rate element should come before
114373b01e57SJeff Chen 			 * extended supported rate element
114473b01e57SJeff Chen 			 */
114573b01e57SJeff Chen 			if (found_data_rate_ie) {
114673b01e57SJeff Chen 				if ((element_len + rate_size) >
114773b01e57SJeff Chen 				    NXPWIFI_SUPPORTED_RATES)
114873b01e57SJeff Chen 					bytes_to_copy =
114973b01e57SJeff Chen 						(NXPWIFI_SUPPORTED_RATES -
115073b01e57SJeff Chen 						 rate_size);
115173b01e57SJeff Chen 				else
115273b01e57SJeff Chen 					bytes_to_copy = element_len;
115373b01e57SJeff Chen 
115473b01e57SJeff Chen 				rate = (u8 *)bss_entry->data_rates;
115573b01e57SJeff Chen 				rate += rate_size;
115673b01e57SJeff Chen 				memcpy(rate, current_ptr + 2, bytes_to_copy);
115773b01e57SJeff Chen 
115873b01e57SJeff Chen 				rate = (u8 *)bss_entry->supported_rates;
115973b01e57SJeff Chen 				rate += rate_size;
116073b01e57SJeff Chen 				memcpy(rate, current_ptr + 2, bytes_to_copy);
116173b01e57SJeff Chen 			}
116273b01e57SJeff Chen 			break;
116373b01e57SJeff Chen 
116473b01e57SJeff Chen 		case WLAN_EID_VENDOR_SPECIFIC:
116573b01e57SJeff Chen 			vendor_ie = (struct ieee_types_vendor_specific *)
116673b01e57SJeff Chen 				current_ptr;
116773b01e57SJeff Chen 
116873b01e57SJeff Chen 			/* 802.11 requires at least 3-byte OUI. */
116973b01e57SJeff Chen 			if (element_len < sizeof(vendor_ie->vend_hdr.oui))
117073b01e57SJeff Chen 				return -EINVAL;
117173b01e57SJeff Chen 
117273b01e57SJeff Chen 			/* Not long enough for a match? Skip it. */
117373b01e57SJeff Chen 			if (element_len < sizeof(wpa_oui))
117473b01e57SJeff Chen 				break;
117573b01e57SJeff Chen 
117673b01e57SJeff Chen 			if (!memcmp(&vendor_ie->vend_hdr.oui, wpa_oui,
117773b01e57SJeff Chen 				    sizeof(wpa_oui))) {
117873b01e57SJeff Chen 				bss_entry->bcn_wpa_ie =
117973b01e57SJeff Chen 					(struct ieee_types_vendor_specific *)
118073b01e57SJeff Chen 					current_ptr;
118173b01e57SJeff Chen 				bss_entry->wpa_offset =
118273b01e57SJeff Chen 					(u16)(current_ptr -
118373b01e57SJeff Chen 					      bss_entry->beacon_buf);
118473b01e57SJeff Chen 			} else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui,
118573b01e57SJeff Chen 					   sizeof(wmm_oui))) {
118673b01e57SJeff Chen 				if (total_ie_len ==
118773b01e57SJeff Chen 				    sizeof(struct ieee80211_wmm_param_ie) ||
118873b01e57SJeff Chen 				    total_ie_len ==
118973b01e57SJeff Chen 				    sizeof(struct ieee_types_wmm_info))
119073b01e57SJeff Chen 					/*
119173b01e57SJeff Chen 					 * Only accept and copy the WMM element if
119273b01e57SJeff Chen 					 * it matches the size expected for the
119373b01e57SJeff Chen 					 * WMM Info element or the WMM Parameter element.
119473b01e57SJeff Chen 					 */
119573b01e57SJeff Chen 					memcpy((u8 *)&bss_entry->wmm_ie,
119673b01e57SJeff Chen 					       current_ptr, total_ie_len);
119773b01e57SJeff Chen 			}
119873b01e57SJeff Chen 			break;
119973b01e57SJeff Chen 		case WLAN_EID_RSN:
120073b01e57SJeff Chen 			bss_entry->bcn_rsn_ie =
120173b01e57SJeff Chen 				(struct element *)current_ptr;
120273b01e57SJeff Chen 			bss_entry->rsn_offset =
120373b01e57SJeff Chen 				(u16)(current_ptr - bss_entry->beacon_buf);
120473b01e57SJeff Chen 			break;
120573b01e57SJeff Chen 		case WLAN_EID_RSNX:
120673b01e57SJeff Chen 			bss_entry->bcn_rsnx_ie =
120773b01e57SJeff Chen 				(struct element *)current_ptr;
120873b01e57SJeff Chen 			bss_entry->rsnx_offset =
120973b01e57SJeff Chen 				(u16)(current_ptr - bss_entry->beacon_buf);
121073b01e57SJeff Chen 			break;
121173b01e57SJeff Chen 		case WLAN_EID_HT_CAPABILITY:
121273b01e57SJeff Chen 			bss_entry->bcn_ht_cap =
121373b01e57SJeff Chen 				(struct ieee80211_ht_cap *)(current_ptr +
121473b01e57SJeff Chen 							    elem_size);
121573b01e57SJeff Chen 			bss_entry->ht_cap_offset =
121673b01e57SJeff Chen 				(u16)(current_ptr + elem_size -
121773b01e57SJeff Chen 				      bss_entry->beacon_buf);
121873b01e57SJeff Chen 			break;
121973b01e57SJeff Chen 		case WLAN_EID_HT_OPERATION:
122073b01e57SJeff Chen 			bss_entry->bcn_ht_oper =
122173b01e57SJeff Chen 				(struct ieee80211_ht_operation *)(current_ptr +
122273b01e57SJeff Chen 								  elem_size);
122373b01e57SJeff Chen 			bss_entry->ht_info_offset =
122473b01e57SJeff Chen 				(u16)(current_ptr + elem_size -
122573b01e57SJeff Chen 				      bss_entry->beacon_buf);
122673b01e57SJeff Chen 			break;
122773b01e57SJeff Chen 		case WLAN_EID_VHT_CAPABILITY:
122873b01e57SJeff Chen 			bss_entry->disable_11ac = false;
122973b01e57SJeff Chen 			bss_entry->bcn_vht_cap = (void *)(current_ptr +
123073b01e57SJeff Chen 							  elem_size);
123173b01e57SJeff Chen 			bss_entry->vht_cap_offset =
123273b01e57SJeff Chen 				(u16)((u8 *)bss_entry->bcn_vht_cap -
123373b01e57SJeff Chen 				      bss_entry->beacon_buf);
123473b01e57SJeff Chen 			break;
123573b01e57SJeff Chen 		case WLAN_EID_VHT_OPERATION:
123673b01e57SJeff Chen 			bss_entry->bcn_vht_oper =
123773b01e57SJeff Chen 				(void *)(current_ptr + elem_size);
123873b01e57SJeff Chen 			bss_entry->vht_info_offset =
123973b01e57SJeff Chen 				(u16)((u8 *)bss_entry->bcn_vht_oper -
124073b01e57SJeff Chen 				      bss_entry->beacon_buf);
124173b01e57SJeff Chen 			break;
124273b01e57SJeff Chen 		case WLAN_EID_BSS_COEX_2040:
124373b01e57SJeff Chen 			bss_entry->bcn_bss_co_2040 = current_ptr;
124473b01e57SJeff Chen 			bss_entry->bss_co_2040_offset =
124573b01e57SJeff Chen 				(u16)(current_ptr - bss_entry->beacon_buf);
124673b01e57SJeff Chen 			break;
124773b01e57SJeff Chen 		case WLAN_EID_EXT_CAPABILITY:
124873b01e57SJeff Chen 			bss_entry->bcn_ext_cap = current_ptr;
124973b01e57SJeff Chen 			bss_entry->ext_cap_offset =
125073b01e57SJeff Chen 				(u16)(current_ptr - bss_entry->beacon_buf);
125173b01e57SJeff Chen 			break;
125273b01e57SJeff Chen 		case WLAN_EID_OPMODE_NOTIF:
125373b01e57SJeff Chen 			bss_entry->oper_mode = (void *)current_ptr;
125473b01e57SJeff Chen 			bss_entry->oper_mode_offset =
125573b01e57SJeff Chen 				(u16)(current_ptr - bss_entry->beacon_buf);
125673b01e57SJeff Chen 			break;
125773b01e57SJeff Chen 		case WLAN_EID_EXTENSION:
1258*8e4f5ca8SLinmao Li 			if (!element_len)
1259*8e4f5ca8SLinmao Li 				return -EINVAL;
1260*8e4f5ca8SLinmao Li 
126173b01e57SJeff Chen 			elem = (struct element *)current_ptr;
126273b01e57SJeff Chen 
126373b01e57SJeff Chen 			switch (elem->data[0]) {
126473b01e57SJeff Chen 			case WLAN_EID_EXT_HE_CAPABILITY:
126573b01e57SJeff Chen 				bss_entry->disable_11ax = false;
126673b01e57SJeff Chen 				bss_entry->bcn_he_cap =
126773b01e57SJeff Chen 					(void *)(current_ptr + elem_size + 1);
126873b01e57SJeff Chen 				bss_entry->he_cap_offset =
126973b01e57SJeff Chen 					(u16)((u8 *)bss_entry->bcn_he_cap -
127073b01e57SJeff Chen 					      bss_entry->beacon_buf);
127173b01e57SJeff Chen 				break;
127273b01e57SJeff Chen 			case WLAN_EID_EXT_HE_OPERATION:
127373b01e57SJeff Chen 				bss_entry->bcn_he_oper =
127473b01e57SJeff Chen 					(void *)(current_ptr + elem_size + 1);
127573b01e57SJeff Chen 				bss_entry->he_info_offset =
127673b01e57SJeff Chen 					(u16)((u8 *)bss_entry->bcn_he_oper -
127773b01e57SJeff Chen 					      bss_entry->beacon_buf);
127873b01e57SJeff Chen 				break;
127973b01e57SJeff Chen 			default:
128073b01e57SJeff Chen 				break;
128173b01e57SJeff Chen 			}
128273b01e57SJeff Chen 			break;
128373b01e57SJeff Chen 		default:
128473b01e57SJeff Chen 			break;
128573b01e57SJeff Chen 		}
128673b01e57SJeff Chen 
128773b01e57SJeff Chen 		current_ptr += total_ie_len;
128873b01e57SJeff Chen 		bytes_left -= total_ie_len;
128973b01e57SJeff Chen 
129073b01e57SJeff Chen 	}	/* while (bytes_left > 2) */
129173b01e57SJeff Chen 	return 0;
129273b01e57SJeff Chen }
129373b01e57SJeff Chen 
129473b01e57SJeff Chen /* Convert the radio-type scan parameter to the join command's band config. */
129573b01e57SJeff Chen static u8
nxpwifi_radio_type_to_band(u8 radio_type)129673b01e57SJeff Chen nxpwifi_radio_type_to_band(u8 radio_type)
129773b01e57SJeff Chen {
129873b01e57SJeff Chen 	switch (radio_type) {
129973b01e57SJeff Chen 	case HOST_SCAN_RADIO_TYPE_A:
130073b01e57SJeff Chen 		return BAND_A;
130173b01e57SJeff Chen 	case HOST_SCAN_RADIO_TYPE_BG:
130273b01e57SJeff Chen 	default:
130373b01e57SJeff Chen 		return BAND_G;
130473b01e57SJeff Chen 	}
130573b01e57SJeff Chen }
130673b01e57SJeff Chen 
130773b01e57SJeff Chen /* Internal helper to start a scan using the given configuration. */
nxpwifi_scan_networks(struct nxpwifi_private * priv,const struct nxpwifi_user_scan_cfg * user_scan_in)130873b01e57SJeff Chen int nxpwifi_scan_networks(struct nxpwifi_private *priv,
130973b01e57SJeff Chen 			  const struct nxpwifi_user_scan_cfg *user_scan_in)
131073b01e57SJeff Chen {
131173b01e57SJeff Chen 	int ret;
131273b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
131373b01e57SJeff Chen 	struct cmd_ctrl_node *cmd_node;
131473b01e57SJeff Chen 	union nxpwifi_scan_cmd_config_tlv *scan_cfg_out;
131573b01e57SJeff Chen 	struct nxpwifi_ie_types_chan_list_param_set *chan_list_out;
131673b01e57SJeff Chen 	struct nxpwifi_chan_scan_param_set *scan_chan_list;
131773b01e57SJeff Chen 	u8 filtered_scan;
131873b01e57SJeff Chen 	u8 scan_current_chan_only;
131973b01e57SJeff Chen 	u8 max_chan_per_scan;
132073b01e57SJeff Chen 
132173b01e57SJeff Chen 	if (adapter->scan_processing) {
132273b01e57SJeff Chen 		nxpwifi_dbg(adapter, WARN,
132373b01e57SJeff Chen 			    "cmd: Scan already in process...\n");
132473b01e57SJeff Chen 		return -EBUSY;
132573b01e57SJeff Chen 	}
132673b01e57SJeff Chen 
132773b01e57SJeff Chen 	if (priv->scan_block) {
132873b01e57SJeff Chen 		nxpwifi_dbg(adapter, WARN,
132973b01e57SJeff Chen 			    "cmd: Scan is blocked during association...\n");
133073b01e57SJeff Chen 		return -EBUSY;
133173b01e57SJeff Chen 	}
133273b01e57SJeff Chen 
133373b01e57SJeff Chen 	if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) ||
133473b01e57SJeff Chen 	    test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
133573b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
133673b01e57SJeff Chen 			    "Ignore scan. Card removed or firmware in bad state\n");
133773b01e57SJeff Chen 		return -EPERM;
133873b01e57SJeff Chen 	}
133973b01e57SJeff Chen 
134073b01e57SJeff Chen 	spin_lock_bh(&adapter->nxpwifi_cmd_lock);
134173b01e57SJeff Chen 	adapter->scan_processing = true;
134273b01e57SJeff Chen 	spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
134373b01e57SJeff Chen 
134473b01e57SJeff Chen 	scan_cfg_out = kzalloc_obj(union nxpwifi_scan_cmd_config_tlv,
134573b01e57SJeff Chen 				   GFP_KERNEL);
134673b01e57SJeff Chen 	if (!scan_cfg_out) {
134773b01e57SJeff Chen 		ret = -ENOMEM;
134873b01e57SJeff Chen 		goto done;
134973b01e57SJeff Chen 	}
135073b01e57SJeff Chen 
135173b01e57SJeff Chen 	scan_chan_list = kzalloc_objs(struct nxpwifi_chan_scan_param_set,
135273b01e57SJeff Chen 				      NXPWIFI_USER_SCAN_CHAN_MAX, GFP_KERNEL);
135373b01e57SJeff Chen 	if (!scan_chan_list) {
135473b01e57SJeff Chen 		kfree(scan_cfg_out);
135573b01e57SJeff Chen 		ret = -ENOMEM;
135673b01e57SJeff Chen 		goto done;
135773b01e57SJeff Chen 	}
135873b01e57SJeff Chen 
135973b01e57SJeff Chen 	nxpwifi_config_scan(priv, user_scan_in, &scan_cfg_out->config,
136073b01e57SJeff Chen 			    &chan_list_out, scan_chan_list, &max_chan_per_scan,
136173b01e57SJeff Chen 			    &filtered_scan, &scan_current_chan_only);
136273b01e57SJeff Chen 
136373b01e57SJeff Chen 	ret = nxpwifi_scan_channel_list(priv, max_chan_per_scan, filtered_scan,
136473b01e57SJeff Chen 					&scan_cfg_out->config, chan_list_out,
136573b01e57SJeff Chen 					scan_chan_list);
136673b01e57SJeff Chen 
136773b01e57SJeff Chen 	/* Get scan command from scan_pending_q and put to cmd_pending_q */
136873b01e57SJeff Chen 	if (!ret) {
136973b01e57SJeff Chen 		spin_lock_bh(&adapter->scan_pending_q_lock);
137073b01e57SJeff Chen 		if (!list_empty(&adapter->scan_pending_q)) {
137173b01e57SJeff Chen 			cmd_node = list_first_entry(&adapter->scan_pending_q,
137273b01e57SJeff Chen 						    struct cmd_ctrl_node, list);
137373b01e57SJeff Chen 			list_del(&cmd_node->list);
137473b01e57SJeff Chen 			spin_unlock_bh(&adapter->scan_pending_q_lock);
137573b01e57SJeff Chen 			nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node);
137673b01e57SJeff Chen 			nxpwifi_queue_work(adapter, &adapter->main_work);
137773b01e57SJeff Chen 
137873b01e57SJeff Chen 			/* Perform internal scan synchronously */
137973b01e57SJeff Chen 			if (!priv->scan_request) {
138073b01e57SJeff Chen 				nxpwifi_dbg(adapter, INFO,
138173b01e57SJeff Chen 					    "wait internal scan\n");
138273b01e57SJeff Chen 				nxpwifi_wait_queue_complete(adapter, cmd_node);
138373b01e57SJeff Chen 			}
138473b01e57SJeff Chen 		} else {
138573b01e57SJeff Chen 			spin_unlock_bh(&adapter->scan_pending_q_lock);
138673b01e57SJeff Chen 		}
138773b01e57SJeff Chen 	}
138873b01e57SJeff Chen 
138973b01e57SJeff Chen 	kfree(scan_cfg_out);
139073b01e57SJeff Chen 	kfree(scan_chan_list);
139173b01e57SJeff Chen done:
139273b01e57SJeff Chen 	if (ret) {
139373b01e57SJeff Chen 		spin_lock_bh(&adapter->nxpwifi_cmd_lock);
139473b01e57SJeff Chen 		adapter->scan_processing = false;
139573b01e57SJeff Chen 		spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
139673b01e57SJeff Chen 	}
139773b01e57SJeff Chen 	return ret;
139873b01e57SJeff Chen }
139973b01e57SJeff Chen 
140073b01e57SJeff Chen /*
140173b01e57SJeff Chen  * Build the firmware scan command from the given configuration, including
140273b01e57SJeff Chen  * fixed fields and TLVs, and set the command ID, size, and endianness.
140373b01e57SJeff Chen  */
nxpwifi_cmd_802_11_scan(struct host_cmd_ds_command * cmd,struct nxpwifi_scan_cmd_config * scan_cfg)140473b01e57SJeff Chen int nxpwifi_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
140573b01e57SJeff Chen 			    struct nxpwifi_scan_cmd_config *scan_cfg)
140673b01e57SJeff Chen {
140773b01e57SJeff Chen 	struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan;
140873b01e57SJeff Chen 
140973b01e57SJeff Chen 	/* Set fixed field variables in scan command */
141073b01e57SJeff Chen 	scan_cmd->bss_mode = scan_cfg->bss_mode;
141173b01e57SJeff Chen 	memcpy(scan_cmd->bssid, scan_cfg->specific_bssid,
141273b01e57SJeff Chen 	       sizeof(scan_cmd->bssid));
141373b01e57SJeff Chen 	memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
141473b01e57SJeff Chen 
141573b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_802_11_SCAN);
141673b01e57SJeff Chen 
141773b01e57SJeff Chen 	/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
141873b01e57SJeff Chen 	cmd->size = cpu_to_le16((u16)(sizeof(scan_cmd->bss_mode)
141973b01e57SJeff Chen 					  + sizeof(scan_cmd->bssid)
142073b01e57SJeff Chen 					  + scan_cfg->tlv_buf_len + S_DS_GEN));
142173b01e57SJeff Chen 
142273b01e57SJeff Chen 	return 0;
142373b01e57SJeff Chen }
142473b01e57SJeff Chen 
142573b01e57SJeff Chen /* Check compatibility of the requested network with current driver settings. */
nxpwifi_check_network_compatibility(struct nxpwifi_private * priv,struct nxpwifi_bssdescriptor * bss_desc)142673b01e57SJeff Chen int nxpwifi_check_network_compatibility(struct nxpwifi_private *priv,
142773b01e57SJeff Chen 					struct nxpwifi_bssdescriptor *bss_desc)
142873b01e57SJeff Chen {
142973b01e57SJeff Chen 	int ret = 0;
143073b01e57SJeff Chen 
143173b01e57SJeff Chen 	if (!bss_desc)
143273b01e57SJeff Chen 		return -EINVAL;
143373b01e57SJeff Chen 
143473b01e57SJeff Chen 	if ((nxpwifi_get_cfp(priv, (u8)bss_desc->bss_band,
143573b01e57SJeff Chen 			     (u16)bss_desc->channel, 0))) {
143673b01e57SJeff Chen 		switch (priv->bss_mode) {
143773b01e57SJeff Chen 		case NL80211_IFTYPE_STATION:
143873b01e57SJeff Chen 			ret = nxpwifi_is_network_compatible(priv, bss_desc,
143973b01e57SJeff Chen 							    priv->bss_mode);
144073b01e57SJeff Chen 			if (ret)
144173b01e57SJeff Chen 				nxpwifi_dbg(priv->adapter, ERROR,
144273b01e57SJeff Chen 					    "Incompatible network settings\n");
144373b01e57SJeff Chen 			break;
144473b01e57SJeff Chen 		default:
144573b01e57SJeff Chen 			ret = 0;
144673b01e57SJeff Chen 		}
144773b01e57SJeff Chen 	}
144873b01e57SJeff Chen 
144973b01e57SJeff Chen 	return ret;
145073b01e57SJeff Chen }
145173b01e57SJeff Chen 
145273b01e57SJeff Chen /* Check if the SSID length is zero or all bytes are zero. */
nxpwifi_is_hidden_ssid(struct cfg80211_ssid * ssid)145373b01e57SJeff Chen static bool nxpwifi_is_hidden_ssid(struct cfg80211_ssid *ssid)
145473b01e57SJeff Chen {
145573b01e57SJeff Chen 	int idx;
145673b01e57SJeff Chen 
145773b01e57SJeff Chen 	for (idx = 0; idx < ssid->ssid_len; idx++) {
145873b01e57SJeff Chen 		if (ssid->ssid[idx])
145973b01e57SJeff Chen 			return false;
146073b01e57SJeff Chen 	}
146173b01e57SJeff Chen 
146273b01e57SJeff Chen 	return true;
146373b01e57SJeff Chen }
146473b01e57SJeff Chen 
146573b01e57SJeff Chen /* Find hidden SSIDs on passive channels and save those channels for active scan. */
nxpwifi_save_hidden_ssid_channels(struct nxpwifi_private * priv,struct cfg80211_bss * bss)146673b01e57SJeff Chen static int nxpwifi_save_hidden_ssid_channels(struct nxpwifi_private *priv,
146773b01e57SJeff Chen 					     struct cfg80211_bss *bss)
146873b01e57SJeff Chen {
146973b01e57SJeff Chen 	struct nxpwifi_bssdescriptor *bss_desc;
147073b01e57SJeff Chen 	int ret;
147173b01e57SJeff Chen 	int chid;
147273b01e57SJeff Chen 
147373b01e57SJeff Chen 	/* Allocate and fill new bss descriptor */
147473b01e57SJeff Chen 	bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL);
147573b01e57SJeff Chen 	if (!bss_desc)
147673b01e57SJeff Chen 		return -ENOMEM;
147773b01e57SJeff Chen 
147873b01e57SJeff Chen 	ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
147973b01e57SJeff Chen 	if (ret)
148073b01e57SJeff Chen 		goto done;
148173b01e57SJeff Chen 
148273b01e57SJeff Chen 	if (nxpwifi_is_hidden_ssid(&bss_desc->ssid)) {
148373b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO, "found hidden SSID\n");
148473b01e57SJeff Chen 		for (chid = 0 ; chid < NXPWIFI_USER_SCAN_CHAN_MAX; chid++) {
148573b01e57SJeff Chen 			if (priv->hidden_chan[chid].chan_number ==
148673b01e57SJeff Chen 			    bss->channel->hw_value)
148773b01e57SJeff Chen 				break;
148873b01e57SJeff Chen 
148973b01e57SJeff Chen 			if (!priv->hidden_chan[chid].chan_number) {
149073b01e57SJeff Chen 				priv->hidden_chan[chid].chan_number =
149173b01e57SJeff Chen 					bss->channel->hw_value;
149273b01e57SJeff Chen 				priv->hidden_chan[chid].radio_type =
149373b01e57SJeff Chen 					bss->channel->band;
149473b01e57SJeff Chen 				priv->hidden_chan[chid].scan_type =
149573b01e57SJeff Chen 					NXPWIFI_SCAN_TYPE_ACTIVE;
149673b01e57SJeff Chen 				break;
149773b01e57SJeff Chen 			}
149873b01e57SJeff Chen 		}
149973b01e57SJeff Chen 	}
150073b01e57SJeff Chen 
150173b01e57SJeff Chen done:
150273b01e57SJeff Chen 	/* Free beacon_ie allocated by nxpwifi_fill_new_bss_desc(). */
150373b01e57SJeff Chen 	kfree(bss_desc->beacon_buf);
150473b01e57SJeff Chen 	kfree(bss_desc);
150573b01e57SJeff Chen 	return ret;
150673b01e57SJeff Chen }
150773b01e57SJeff Chen 
nxpwifi_update_curr_bss_params(struct nxpwifi_private * priv,struct cfg80211_bss * bss)150873b01e57SJeff Chen static int nxpwifi_update_curr_bss_params(struct nxpwifi_private *priv,
150973b01e57SJeff Chen 					  struct cfg80211_bss *bss)
151073b01e57SJeff Chen {
151173b01e57SJeff Chen 	struct nxpwifi_bssdescriptor *bss_desc;
151273b01e57SJeff Chen 	int ret;
151373b01e57SJeff Chen 
151473b01e57SJeff Chen 	/* Allocate and fill new bss descriptor */
151573b01e57SJeff Chen 	bss_desc = kzalloc_obj(*bss_desc, GFP_KERNEL);
151673b01e57SJeff Chen 	if (!bss_desc)
151773b01e57SJeff Chen 		return -ENOMEM;
151873b01e57SJeff Chen 
151973b01e57SJeff Chen 	ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
152073b01e57SJeff Chen 	if (ret)
152173b01e57SJeff Chen 		goto done;
152273b01e57SJeff Chen 
152373b01e57SJeff Chen 	ret = nxpwifi_check_network_compatibility(priv, bss_desc);
152473b01e57SJeff Chen 	if (ret)
152573b01e57SJeff Chen 		goto done;
152673b01e57SJeff Chen 
152773b01e57SJeff Chen 	spin_lock_bh(&priv->curr_bcn_buf_lock);
152873b01e57SJeff Chen 	/* Make a copy of current BSSID descriptor */
152973b01e57SJeff Chen 	memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc,
153073b01e57SJeff Chen 	       sizeof(priv->curr_bss_params.bss_descriptor));
153173b01e57SJeff Chen 
153273b01e57SJeff Chen 	/* beacon_ie will be copied to its own buffer in nxpwifi_save_curr_bcn(). */
153373b01e57SJeff Chen 	nxpwifi_save_curr_bcn(priv);
153473b01e57SJeff Chen 	spin_unlock_bh(&priv->curr_bcn_buf_lock);
153573b01e57SJeff Chen 
153673b01e57SJeff Chen done:
153773b01e57SJeff Chen 	/* Free beacon_ie allocated by nxpwifi_fill_new_bss_desc(). */
153873b01e57SJeff Chen 	kfree(bss_desc->beacon_buf);
153973b01e57SJeff Chen 	kfree(bss_desc);
154073b01e57SJeff Chen 	return ret;
154173b01e57SJeff Chen }
154273b01e57SJeff Chen 
154373b01e57SJeff Chen static int
nxpwifi_parse_single_response_buf(struct nxpwifi_private * priv,u8 ** bss_info,u32 * bytes_left,u64 fw_tsf,const u8 * radio_type,bool ext_scan,s32 rssi_val)154473b01e57SJeff Chen nxpwifi_parse_single_response_buf(struct nxpwifi_private *priv, u8 **bss_info,
154573b01e57SJeff Chen 				  u32 *bytes_left, u64 fw_tsf, const u8 *radio_type,
154673b01e57SJeff Chen 				  bool ext_scan, s32 rssi_val)
154773b01e57SJeff Chen {
154873b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
154973b01e57SJeff Chen 	struct nxpwifi_chan_freq_power *cfp;
155073b01e57SJeff Chen 	struct cfg80211_bss *bss;
155173b01e57SJeff Chen 	u8 bssid[ETH_ALEN];
155273b01e57SJeff Chen 	s32 rssi;
155373b01e57SJeff Chen 	const u8 *ie_buf;
155473b01e57SJeff Chen 	size_t ie_len;
155573b01e57SJeff Chen 	u16 channel = 0;
155673b01e57SJeff Chen 	u16 beacon_size = 0;
155773b01e57SJeff Chen 	u32 curr_bcn_bytes;
155873b01e57SJeff Chen 	u32 freq;
155973b01e57SJeff Chen 	u16 beacon_period;
156073b01e57SJeff Chen 	u16 cap_info_bitmap;
156173b01e57SJeff Chen 	u8 *current_ptr;
156273b01e57SJeff Chen 	u64 timestamp;
156373b01e57SJeff Chen 	struct nxpwifi_fixed_bcn_param *bcn_param;
156473b01e57SJeff Chen 	struct nxpwifi_bss_priv *bss_priv;
156573b01e57SJeff Chen 
156673b01e57SJeff Chen 	if (*bytes_left >= sizeof(beacon_size)) {
156773b01e57SJeff Chen 		/* Extract & convert beacon size from command buffer */
156873b01e57SJeff Chen 		beacon_size = get_unaligned_le16((*bss_info));
156973b01e57SJeff Chen 		*bytes_left -= sizeof(beacon_size);
157073b01e57SJeff Chen 		*bss_info += sizeof(beacon_size);
157173b01e57SJeff Chen 	}
157273b01e57SJeff Chen 
157373b01e57SJeff Chen 	if (!beacon_size || beacon_size > *bytes_left) {
157473b01e57SJeff Chen 		*bss_info += *bytes_left;
157573b01e57SJeff Chen 		*bytes_left = 0;
157673b01e57SJeff Chen 		return -EINVAL;
157773b01e57SJeff Chen 	}
157873b01e57SJeff Chen 
157973b01e57SJeff Chen 	/*
158073b01e57SJeff Chen 	 * Initialize the current working beacon pointer for this BSS
158173b01e57SJeff Chen 	 * iteration
158273b01e57SJeff Chen 	 */
158373b01e57SJeff Chen 	current_ptr = *bss_info;
158473b01e57SJeff Chen 
158573b01e57SJeff Chen 	/* Advance the return beacon pointer past the current beacon */
158673b01e57SJeff Chen 	*bss_info += beacon_size;
158773b01e57SJeff Chen 	*bytes_left -= beacon_size;
158873b01e57SJeff Chen 
158973b01e57SJeff Chen 	curr_bcn_bytes = beacon_size;
159073b01e57SJeff Chen 
159173b01e57SJeff Chen 	/*
159273b01e57SJeff Chen 	 * First 5 fields are bssid, RSSI(for legacy scan only),
159373b01e57SJeff Chen 	 * time stamp, beacon interval, and capability information
159473b01e57SJeff Chen 	 */
159573b01e57SJeff Chen 	if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) +
159673b01e57SJeff Chen 	    sizeof(struct nxpwifi_fixed_bcn_param)) {
159773b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
159873b01e57SJeff Chen 			    "InterpretIE: not enough bytes left\n");
159973b01e57SJeff Chen 		return -EINVAL;
160073b01e57SJeff Chen 	}
160173b01e57SJeff Chen 
160273b01e57SJeff Chen 	memcpy(bssid, current_ptr, ETH_ALEN);
160373b01e57SJeff Chen 	current_ptr += ETH_ALEN;
160473b01e57SJeff Chen 	curr_bcn_bytes -= ETH_ALEN;
160573b01e57SJeff Chen 
160673b01e57SJeff Chen 	if (!ext_scan) {
160773b01e57SJeff Chen 		rssi = (s32)*current_ptr;
160873b01e57SJeff Chen 		rssi = (-rssi) * 100;		/* Convert dBm to mBm */
160973b01e57SJeff Chen 		current_ptr += sizeof(u8);
161073b01e57SJeff Chen 		curr_bcn_bytes -= sizeof(u8);
161173b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
161273b01e57SJeff Chen 			    "info: InterpretIE: RSSI=%d\n", rssi);
161373b01e57SJeff Chen 	} else {
161473b01e57SJeff Chen 		rssi = rssi_val;
161573b01e57SJeff Chen 	}
161673b01e57SJeff Chen 
161773b01e57SJeff Chen 	bcn_param = (struct nxpwifi_fixed_bcn_param *)current_ptr;
161873b01e57SJeff Chen 	current_ptr += sizeof(*bcn_param);
161973b01e57SJeff Chen 	curr_bcn_bytes -= sizeof(*bcn_param);
162073b01e57SJeff Chen 
162173b01e57SJeff Chen 	timestamp = le64_to_cpu(bcn_param->timestamp);
162273b01e57SJeff Chen 	beacon_period = le16_to_cpu(bcn_param->beacon_period);
162373b01e57SJeff Chen 
162473b01e57SJeff Chen 	cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
162573b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
162673b01e57SJeff Chen 		    "info: InterpretIE: capabilities=0x%X\n",
162773b01e57SJeff Chen 		    cap_info_bitmap);
162873b01e57SJeff Chen 
162973b01e57SJeff Chen 	/* Rest of the current buffer are element's */
163073b01e57SJeff Chen 	ie_buf = current_ptr;
163173b01e57SJeff Chen 	ie_len = curr_bcn_bytes;
163273b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
163373b01e57SJeff Chen 		    "info: InterpretIE: IELength for this AP = %d\n",
163473b01e57SJeff Chen 		    curr_bcn_bytes);
163573b01e57SJeff Chen 
163673b01e57SJeff Chen 	while (curr_bcn_bytes >= sizeof(struct element)) {
163773b01e57SJeff Chen 		u8 element_id, element_len;
163873b01e57SJeff Chen 
163973b01e57SJeff Chen 		element_id = *current_ptr;
164073b01e57SJeff Chen 		element_len = *(current_ptr + 1);
164173b01e57SJeff Chen 		if (curr_bcn_bytes < element_len +
164273b01e57SJeff Chen 				sizeof(struct element)) {
164373b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
164473b01e57SJeff Chen 				    "%s: bytes left < element length\n", __func__);
164573b01e57SJeff Chen 			return -EFAULT;
164673b01e57SJeff Chen 		}
164773b01e57SJeff Chen 		if (element_id == WLAN_EID_DS_PARAMS) {
164873b01e57SJeff Chen 			channel = *(current_ptr +
164973b01e57SJeff Chen 				    sizeof(struct element));
165073b01e57SJeff Chen 			break;
165173b01e57SJeff Chen 		}
165273b01e57SJeff Chen 
165373b01e57SJeff Chen 		current_ptr += element_len + sizeof(struct element);
165473b01e57SJeff Chen 		curr_bcn_bytes -= element_len +
165573b01e57SJeff Chen 					sizeof(struct element);
165673b01e57SJeff Chen 	}
165773b01e57SJeff Chen 
165873b01e57SJeff Chen 	if (channel) {
165973b01e57SJeff Chen 		struct ieee80211_channel *chan;
166073b01e57SJeff Chen 		struct nxpwifi_bssdescriptor *bss_desc;
166173b01e57SJeff Chen 		u8 band;
166273b01e57SJeff Chen 
166373b01e57SJeff Chen 		/* Skip entry if on csa closed channel */
166473b01e57SJeff Chen 		if (channel == priv->csa_chan) {
166573b01e57SJeff Chen 			nxpwifi_dbg(adapter, WARN,
166673b01e57SJeff Chen 				    "Dropping entry on csa closed channel\n");
166773b01e57SJeff Chen 			return 0;
166873b01e57SJeff Chen 		}
166973b01e57SJeff Chen 
167073b01e57SJeff Chen 		band = BAND_G;
167173b01e57SJeff Chen 		if (radio_type)
167273b01e57SJeff Chen 			band = nxpwifi_radio_type_to_band(*radio_type &
167373b01e57SJeff Chen 							  (BIT(0) | BIT(1)));
167473b01e57SJeff Chen 
167573b01e57SJeff Chen 		cfp = nxpwifi_get_cfp(priv, band, channel, 0);
167673b01e57SJeff Chen 
167773b01e57SJeff Chen 		freq = cfp ? cfp->freq : 0;
167873b01e57SJeff Chen 
167973b01e57SJeff Chen 		chan = ieee80211_get_channel(priv->wdev.wiphy, freq);
168073b01e57SJeff Chen 
168173b01e57SJeff Chen 		if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
168273b01e57SJeff Chen 			bss = cfg80211_inform_bss(priv->wdev.wiphy, chan,
168373b01e57SJeff Chen 						  CFG80211_BSS_FTYPE_UNKNOWN,
168473b01e57SJeff Chen 						  bssid, timestamp,
168573b01e57SJeff Chen 						  cap_info_bitmap,
168673b01e57SJeff Chen 						  beacon_period,
168773b01e57SJeff Chen 						  ie_buf, ie_len, rssi,
168873b01e57SJeff Chen 						  GFP_ATOMIC);
168973b01e57SJeff Chen 			if (bss) {
169073b01e57SJeff Chen 				bss_priv = (struct nxpwifi_bss_priv *)bss->priv;
169173b01e57SJeff Chen 				bss_priv->band = band;
169273b01e57SJeff Chen 				bss_priv->fw_tsf = fw_tsf;
169373b01e57SJeff Chen 				bss_desc =
169473b01e57SJeff Chen 					&priv->curr_bss_params.bss_descriptor;
169573b01e57SJeff Chen 				if (priv->media_connected &&
169673b01e57SJeff Chen 				    !memcmp(bssid, bss_desc->mac_address,
169773b01e57SJeff Chen 					    ETH_ALEN))
169873b01e57SJeff Chen 					nxpwifi_update_curr_bss_params(priv,
169973b01e57SJeff Chen 								       bss);
170073b01e57SJeff Chen 
170173b01e57SJeff Chen 				if ((chan->flags & IEEE80211_CHAN_RADAR) ||
170273b01e57SJeff Chen 				    (chan->flags & IEEE80211_CHAN_NO_IR)) {
170373b01e57SJeff Chen 					nxpwifi_dbg(adapter, INFO,
170473b01e57SJeff Chen 						    "radar or passive channel %d\n",
170573b01e57SJeff Chen 						    channel);
170673b01e57SJeff Chen 					nxpwifi_save_hidden_ssid_channels(priv,
170773b01e57SJeff Chen 									  bss);
170873b01e57SJeff Chen 				}
170973b01e57SJeff Chen 
171073b01e57SJeff Chen 				cfg80211_put_bss(priv->wdev.wiphy, bss);
171173b01e57SJeff Chen 			}
171273b01e57SJeff Chen 		}
171373b01e57SJeff Chen 	} else {
171473b01e57SJeff Chen 		nxpwifi_dbg(adapter, WARN, "missing BSS channel element\n");
171573b01e57SJeff Chen 	}
171673b01e57SJeff Chen 
171773b01e57SJeff Chen 	return 0;
171873b01e57SJeff Chen }
171973b01e57SJeff Chen 
nxpwifi_complete_scan(struct nxpwifi_private * priv)172073b01e57SJeff Chen static void nxpwifi_complete_scan(struct nxpwifi_private *priv)
172173b01e57SJeff Chen {
172273b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
172373b01e57SJeff Chen 
172473b01e57SJeff Chen 	adapter->survey_idx = 0;
172573b01e57SJeff Chen 	if (adapter->curr_cmd->wait_q_enabled) {
172673b01e57SJeff Chen 		adapter->cmd_wait_q.status = 0;
172773b01e57SJeff Chen 		if (!priv->scan_request) {
172873b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
172973b01e57SJeff Chen 				    "complete internal scan\n");
173073b01e57SJeff Chen 			nxpwifi_complete_cmd(adapter, adapter->curr_cmd);
173173b01e57SJeff Chen 		}
173273b01e57SJeff Chen 	}
173373b01e57SJeff Chen }
173473b01e57SJeff Chen 
173573b01e57SJeff Chen /* Find hidden SSIDs on passive channels and run active scans on them. */
173673b01e57SJeff Chen static int
nxpwifi_active_scan_req_for_passive_chan(struct nxpwifi_private * priv)173773b01e57SJeff Chen nxpwifi_active_scan_req_for_passive_chan(struct nxpwifi_private *priv)
173873b01e57SJeff Chen {
173973b01e57SJeff Chen 	int ret;
174073b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
174173b01e57SJeff Chen 	u8 id = 0;
174273b01e57SJeff Chen 	struct nxpwifi_user_scan_cfg  *user_scan_cfg;
174373b01e57SJeff Chen 
174473b01e57SJeff Chen 	if (adapter->active_scan_triggered || !priv->scan_request ||
174573b01e57SJeff Chen 	    priv->scan_aborting) {
174673b01e57SJeff Chen 		adapter->active_scan_triggered = false;
174773b01e57SJeff Chen 		return 0;
174873b01e57SJeff Chen 	}
174973b01e57SJeff Chen 
175073b01e57SJeff Chen 	if (!priv->hidden_chan[0].chan_number) {
175173b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n");
175273b01e57SJeff Chen 		return 0;
175373b01e57SJeff Chen 	}
175473b01e57SJeff Chen 	user_scan_cfg = kzalloc_obj(*user_scan_cfg, GFP_KERNEL);
175573b01e57SJeff Chen 
175673b01e57SJeff Chen 	if (!user_scan_cfg)
175773b01e57SJeff Chen 		return -ENOMEM;
175873b01e57SJeff Chen 
175973b01e57SJeff Chen 	for (id = 0; id < NXPWIFI_USER_SCAN_CHAN_MAX; id++) {
176073b01e57SJeff Chen 		if (!priv->hidden_chan[id].chan_number)
176173b01e57SJeff Chen 			break;
176273b01e57SJeff Chen 		memcpy(&user_scan_cfg->chan_list[id],
176373b01e57SJeff Chen 		       &priv->hidden_chan[id],
176473b01e57SJeff Chen 		       sizeof(struct nxpwifi_user_scan_chan));
176573b01e57SJeff Chen 	}
176673b01e57SJeff Chen 
176773b01e57SJeff Chen 	adapter->active_scan_triggered = true;
176873b01e57SJeff Chen 	if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
176973b01e57SJeff Chen 		ether_addr_copy(user_scan_cfg->random_mac,
177073b01e57SJeff Chen 				priv->scan_request->mac_addr);
177173b01e57SJeff Chen 	user_scan_cfg->num_ssids = priv->scan_request->n_ssids;
177273b01e57SJeff Chen 	user_scan_cfg->ssid_list = priv->scan_request->ssids;
177373b01e57SJeff Chen 
177473b01e57SJeff Chen 	ret = nxpwifi_scan_networks(priv, user_scan_cfg);
177573b01e57SJeff Chen 	kfree(user_scan_cfg);
177673b01e57SJeff Chen 
177773b01e57SJeff Chen 	memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan));
177873b01e57SJeff Chen 
177973b01e57SJeff Chen 	if (ret)
178073b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR, "scan failed: %d\n", ret);
178173b01e57SJeff Chen 
178273b01e57SJeff Chen 	return ret;
178373b01e57SJeff Chen }
178473b01e57SJeff Chen 
nxpwifi_check_next_scan_command(struct nxpwifi_private * priv)178573b01e57SJeff Chen static void nxpwifi_check_next_scan_command(struct nxpwifi_private *priv)
178673b01e57SJeff Chen {
178773b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
178873b01e57SJeff Chen 	struct cmd_ctrl_node *cmd_node;
178973b01e57SJeff Chen 
179073b01e57SJeff Chen 	spin_lock_bh(&adapter->scan_pending_q_lock);
179173b01e57SJeff Chen 	if (list_empty(&adapter->scan_pending_q)) {
179273b01e57SJeff Chen 		spin_unlock_bh(&adapter->scan_pending_q_lock);
179373b01e57SJeff Chen 
179473b01e57SJeff Chen 		spin_lock_bh(&adapter->nxpwifi_cmd_lock);
179573b01e57SJeff Chen 		adapter->scan_processing = false;
179673b01e57SJeff Chen 		spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
179773b01e57SJeff Chen 
179873b01e57SJeff Chen 		nxpwifi_active_scan_req_for_passive_chan(priv);
179973b01e57SJeff Chen 
180073b01e57SJeff Chen 		if (!adapter->ext_scan)
180173b01e57SJeff Chen 			nxpwifi_complete_scan(priv);
180273b01e57SJeff Chen 
180373b01e57SJeff Chen 		if (priv->scan_request) {
180473b01e57SJeff Chen 			struct cfg80211_scan_info info = {
180573b01e57SJeff Chen 				.aborted = false,
180673b01e57SJeff Chen 			};
180773b01e57SJeff Chen 
180873b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
180973b01e57SJeff Chen 				    "info: notifying scan done\n");
181073b01e57SJeff Chen 			cfg80211_scan_done(priv->scan_request, &info);
181173b01e57SJeff Chen 			priv->scan_request = NULL;
181273b01e57SJeff Chen 			priv->scan_aborting = false;
181373b01e57SJeff Chen 		} else {
181473b01e57SJeff Chen 			priv->scan_aborting = false;
181573b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
181673b01e57SJeff Chen 				    "info: scan already aborted\n");
181773b01e57SJeff Chen 		}
181873b01e57SJeff Chen 	} else if ((priv->scan_aborting && !priv->scan_request) ||
181973b01e57SJeff Chen 		   priv->scan_block) {
182073b01e57SJeff Chen 		spin_unlock_bh(&adapter->scan_pending_q_lock);
182173b01e57SJeff Chen 
182273b01e57SJeff Chen 		nxpwifi_cancel_pending_scan_cmd(adapter);
182373b01e57SJeff Chen 
182473b01e57SJeff Chen 		spin_lock_bh(&adapter->nxpwifi_cmd_lock);
182573b01e57SJeff Chen 		adapter->scan_processing = false;
182673b01e57SJeff Chen 		spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
182773b01e57SJeff Chen 
182873b01e57SJeff Chen 		if (!adapter->active_scan_triggered) {
182973b01e57SJeff Chen 			if (priv->scan_request) {
183073b01e57SJeff Chen 				struct cfg80211_scan_info info = {
183173b01e57SJeff Chen 					.aborted = true,
183273b01e57SJeff Chen 				};
183373b01e57SJeff Chen 
183473b01e57SJeff Chen 				nxpwifi_dbg(adapter, INFO,
183573b01e57SJeff Chen 					    "info: aborting scan\n");
183673b01e57SJeff Chen 				cfg80211_scan_done(priv->scan_request, &info);
183773b01e57SJeff Chen 				priv->scan_request = NULL;
183873b01e57SJeff Chen 				priv->scan_aborting = false;
183973b01e57SJeff Chen 			} else {
184073b01e57SJeff Chen 				priv->scan_aborting = false;
184173b01e57SJeff Chen 				nxpwifi_dbg(adapter, INFO,
184273b01e57SJeff Chen 					    "info: scan already aborted\n");
184373b01e57SJeff Chen 			}
184473b01e57SJeff Chen 		}
184573b01e57SJeff Chen 	} else {
184673b01e57SJeff Chen 		/* Move a scan command from scan_pending_q to cmd_pending_q. */
184773b01e57SJeff Chen 		cmd_node = list_first_entry(&adapter->scan_pending_q,
184873b01e57SJeff Chen 					    struct cmd_ctrl_node, list);
184973b01e57SJeff Chen 		list_del(&cmd_node->list);
185073b01e57SJeff Chen 		spin_unlock_bh(&adapter->scan_pending_q_lock);
185173b01e57SJeff Chen 		nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node);
185273b01e57SJeff Chen 	}
185373b01e57SJeff Chen }
185473b01e57SJeff Chen 
nxpwifi_cancel_scan(struct nxpwifi_adapter * adapter)185573b01e57SJeff Chen void nxpwifi_cancel_scan(struct nxpwifi_adapter *adapter)
185673b01e57SJeff Chen {
185773b01e57SJeff Chen 	struct nxpwifi_private *priv;
185873b01e57SJeff Chen 	int i;
185973b01e57SJeff Chen 
186073b01e57SJeff Chen 	nxpwifi_cancel_pending_scan_cmd(adapter);
186173b01e57SJeff Chen 
186273b01e57SJeff Chen 	if (adapter->scan_processing) {
186373b01e57SJeff Chen 		spin_lock_bh(&adapter->nxpwifi_cmd_lock);
186473b01e57SJeff Chen 		adapter->scan_processing = false;
186573b01e57SJeff Chen 		spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
186673b01e57SJeff Chen 		for (i = 0; i < adapter->priv_num; i++) {
186773b01e57SJeff Chen 			priv = adapter->priv[i];
186873b01e57SJeff Chen 			if (priv->scan_request) {
186973b01e57SJeff Chen 				struct cfg80211_scan_info info = {
187073b01e57SJeff Chen 					.aborted = true,
187173b01e57SJeff Chen 				};
187273b01e57SJeff Chen 
187373b01e57SJeff Chen 				nxpwifi_dbg(adapter, INFO,
187473b01e57SJeff Chen 					    "info: aborting scan\n");
187573b01e57SJeff Chen 				cfg80211_scan_done(priv->scan_request, &info);
187673b01e57SJeff Chen 				priv->scan_request = NULL;
187773b01e57SJeff Chen 				priv->scan_aborting = false;
187873b01e57SJeff Chen 			}
187973b01e57SJeff Chen 		}
188073b01e57SJeff Chen 	}
188173b01e57SJeff Chen }
188273b01e57SJeff Chen 
188373b01e57SJeff Chen /*
188473b01e57SJeff Chen  * Handle the scan command response.
188573b01e57SJeff Chen  *
188673b01e57SJeff Chen  * The scan response buffer has the following layout:
188773b01e57SJeff Chen  *
188873b01e57SJeff Chen  *   -------------------------------------------------------------
188973b01e57SJeff Chen  *   | Header (4 * t_u16): standard command response header       |
189073b01e57SJeff Chen  *   -------------------------------------------------------------
189173b01e57SJeff Chen  *   | BufSize (t_u16): size of the BSS description data         |
189273b01e57SJeff Chen  *   -------------------------------------------------------------
189373b01e57SJeff Chen  *   | NumOfSet (t_u8): number of returned BSS descriptions      |
189473b01e57SJeff Chen  *   -------------------------------------------------------------
189573b01e57SJeff Chen  *   | BSS description data (variable, size = BufSize)           |
189673b01e57SJeff Chen  *   -------------------------------------------------------------
189773b01e57SJeff Chen  *   | TLV data (variable, size = cmd_size - fixed fields)       |
189873b01e57SJeff Chen  *   -------------------------------------------------------------
189973b01e57SJeff Chen  */
nxpwifi_ret_802_11_scan(struct nxpwifi_private * priv,struct host_cmd_ds_command * resp)190073b01e57SJeff Chen int nxpwifi_ret_802_11_scan(struct nxpwifi_private *priv,
190173b01e57SJeff Chen 			    struct host_cmd_ds_command *resp)
190273b01e57SJeff Chen {
190373b01e57SJeff Chen 	int ret = 0;
190473b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
190573b01e57SJeff Chen 	struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
190673b01e57SJeff Chen 	u8 *tlv_data;
190773b01e57SJeff Chen 	const struct nxpwifi_ie_types_tsf_timestamp *tsf_tlv;
190873b01e57SJeff Chen 	u8 *bss_info;
190973b01e57SJeff Chen 	u32 scan_resp_size;
191073b01e57SJeff Chen 	u32 bytes_left;
191173b01e57SJeff Chen 	u32 idx;
191273b01e57SJeff Chen 	u32 tlv_buf_size;
191373b01e57SJeff Chen 	const struct nxpwifi_ie_types_chan_band_list_param_set *chan_band_tlv;
191473b01e57SJeff Chen 	const struct chan_band_param_set *chan_band;
191573b01e57SJeff Chen 	u8 is_bgscan_resp;
191673b01e57SJeff Chen 	__le64 fw_tsf = 0;
191773b01e57SJeff Chen 	const u8 *radio_type;
191873b01e57SJeff Chen 	struct cfg80211_wowlan_nd_match *pmatch;
191973b01e57SJeff Chen 	struct cfg80211_sched_scan_request *nd_config = NULL;
192073b01e57SJeff Chen 
192173b01e57SJeff Chen 	is_bgscan_resp = (le16_to_cpu(resp->command)
192273b01e57SJeff Chen 			  == HOST_CMD_802_11_BG_SCAN_QUERY);
192373b01e57SJeff Chen 	if (is_bgscan_resp)
192473b01e57SJeff Chen 		scan_rsp = &resp->params.bg_scan_query_resp.scan_resp;
192573b01e57SJeff Chen 	else
192673b01e57SJeff Chen 		scan_rsp = &resp->params.scan_resp;
192773b01e57SJeff Chen 
192873b01e57SJeff Chen 	if (scan_rsp->number_of_sets > NXPWIFI_MAX_AP) {
192973b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
193073b01e57SJeff Chen 			    "SCAN_RESP: too many AP returned (%d)\n",
193173b01e57SJeff Chen 			    scan_rsp->number_of_sets);
193273b01e57SJeff Chen 		ret = -EINVAL;
193373b01e57SJeff Chen 		goto check_next_scan;
193473b01e57SJeff Chen 	}
193573b01e57SJeff Chen 
193673b01e57SJeff Chen 	/* Check csa channel expiry before parsing scan response */
193773b01e57SJeff Chen 	nxpwifi_11h_get_csa_closed_channel(priv);
193873b01e57SJeff Chen 
193973b01e57SJeff Chen 	bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
194073b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
194173b01e57SJeff Chen 		    "info: SCAN_RESP: bss_descript_size %d\n",
194273b01e57SJeff Chen 		    bytes_left);
194373b01e57SJeff Chen 
194473b01e57SJeff Chen 	scan_resp_size = le16_to_cpu(resp->size);
194573b01e57SJeff Chen 
194673b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
194773b01e57SJeff Chen 		    "info: SCAN_RESP: returned %d APs before parsing\n",
194873b01e57SJeff Chen 		    scan_rsp->number_of_sets);
194973b01e57SJeff Chen 
195073b01e57SJeff Chen 	bss_info = scan_rsp->bss_desc_and_tlv_buffer;
195173b01e57SJeff Chen 
195273b01e57SJeff Chen 	/*
195373b01e57SJeff Chen 	 * TLV buffer size = scan_resp_size minus the fixed fields, BSS
195473b01e57SJeff Chen 	 * description data, and the command response header (S_DS_GEN).
195573b01e57SJeff Chen 	 */
195673b01e57SJeff Chen 	tlv_buf_size = scan_resp_size - (bytes_left
195773b01e57SJeff Chen 					 + sizeof(scan_rsp->bss_descript_size)
195873b01e57SJeff Chen 					 + sizeof(scan_rsp->number_of_sets)
195973b01e57SJeff Chen 					 + S_DS_GEN);
196073b01e57SJeff Chen 
196173b01e57SJeff Chen 	tlv_data = (scan_rsp->bss_desc_and_tlv_buffer +
196273b01e57SJeff Chen 					  bytes_left);
196373b01e57SJeff Chen 
196473b01e57SJeff Chen 	/* Find timestamp TLV */
196573b01e57SJeff Chen 	{
196673b01e57SJeff Chen 		const struct nxpwifi_tlv *t;
196773b01e57SJeff Chen 
196873b01e57SJeff Chen 		t = nxpwifi_find_tlv(TLV_TYPE_TSFTIMESTAMP, tlv_data, tlv_buf_size);
196973b01e57SJeff Chen 		tsf_tlv = (const struct nxpwifi_ie_types_tsf_timestamp *)t;
197073b01e57SJeff Chen 	}
197173b01e57SJeff Chen 
197273b01e57SJeff Chen 	/* Find channel-band list TLV */
197373b01e57SJeff Chen 	{
197473b01e57SJeff Chen 		const struct nxpwifi_tlv *t;
197573b01e57SJeff Chen 
197673b01e57SJeff Chen 		t = nxpwifi_find_tlv(TLV_TYPE_CHANNELBANDLIST, tlv_data,
197773b01e57SJeff Chen 				     tlv_buf_size);
197873b01e57SJeff Chen 		chan_band_tlv =
197973b01e57SJeff Chen 		    (const struct nxpwifi_ie_types_chan_band_list_param_set *)t;
198073b01e57SJeff Chen 	}
198173b01e57SJeff Chen 
198273b01e57SJeff Chen #ifdef CONFIG_PM
198373b01e57SJeff Chen 	if (priv->wdev.wiphy->wowlan_config)
198473b01e57SJeff Chen 		nd_config = priv->wdev.wiphy->wowlan_config->nd_config;
198573b01e57SJeff Chen #endif
198673b01e57SJeff Chen 
198773b01e57SJeff Chen 	if (nd_config) {
198873b01e57SJeff Chen 		adapter->nd_info =
198973b01e57SJeff Chen 			kzalloc_flex(*adapter->nd_info, matches,
199073b01e57SJeff Chen 				     scan_rsp->number_of_sets, GFP_ATOMIC);
199173b01e57SJeff Chen 
199273b01e57SJeff Chen 		if (adapter->nd_info)
199373b01e57SJeff Chen 			adapter->nd_info->n_matches = scan_rsp->number_of_sets;
199473b01e57SJeff Chen 	}
199573b01e57SJeff Chen 
199673b01e57SJeff Chen 	for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
199773b01e57SJeff Chen 		/*
199873b01e57SJeff Chen 		 * If a TSF TLV is present, save its TSF value in fw_tsf. This
199973b01e57SJeff Chen 		 * is the firmware TSF at the time the beacon or probe response
200073b01e57SJeff Chen 		 * was received.
200173b01e57SJeff Chen 		 */
200273b01e57SJeff Chen 		if (tsf_tlv)
200373b01e57SJeff Chen 			memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
200473b01e57SJeff Chen 			       sizeof(fw_tsf));
200573b01e57SJeff Chen 
200673b01e57SJeff Chen 		if (chan_band_tlv) {
200773b01e57SJeff Chen 			chan_band = &chan_band_tlv->chan_band_param[idx];
200873b01e57SJeff Chen 			radio_type = &chan_band->radio_type;
200973b01e57SJeff Chen 		} else {
201073b01e57SJeff Chen 			radio_type = NULL;
201173b01e57SJeff Chen 		}
201273b01e57SJeff Chen 
201373b01e57SJeff Chen 		if (chan_band_tlv && adapter->nd_info) {
201473b01e57SJeff Chen 			adapter->nd_info->matches[idx] =
201573b01e57SJeff Chen 				kzalloc(sizeof(*pmatch) + sizeof(u32),
201673b01e57SJeff Chen 					GFP_ATOMIC);
201773b01e57SJeff Chen 
201873b01e57SJeff Chen 			pmatch = adapter->nd_info->matches[idx];
201973b01e57SJeff Chen 
202073b01e57SJeff Chen 			if (pmatch) {
202173b01e57SJeff Chen 				pmatch->n_channels = 1;
202273b01e57SJeff Chen 				pmatch->channels[0] = chan_band->chan_number;
202373b01e57SJeff Chen 			}
202473b01e57SJeff Chen 		}
202573b01e57SJeff Chen 
202673b01e57SJeff Chen 		ret = nxpwifi_parse_single_response_buf(priv, &bss_info,
202773b01e57SJeff Chen 							&bytes_left,
202873b01e57SJeff Chen 							le64_to_cpu(fw_tsf),
202973b01e57SJeff Chen 							radio_type, false, 0);
203073b01e57SJeff Chen 		if (ret)
203173b01e57SJeff Chen 			goto check_next_scan;
203273b01e57SJeff Chen 	}
203373b01e57SJeff Chen 
203473b01e57SJeff Chen check_next_scan:
203573b01e57SJeff Chen 	nxpwifi_check_next_scan_command(priv);
203673b01e57SJeff Chen 	return ret;
203773b01e57SJeff Chen }
203873b01e57SJeff Chen 
203973b01e57SJeff Chen /*
204073b01e57SJeff Chen  * Prepare the extended scan command using the provided scan configuration
204173b01e57SJeff Chen  * and build the structure to be sent to firmware.
204273b01e57SJeff Chen  */
nxpwifi_cmd_802_11_scan_ext(struct nxpwifi_private * priv,struct host_cmd_ds_command * cmd,void * data_buf)204373b01e57SJeff Chen int nxpwifi_cmd_802_11_scan_ext(struct nxpwifi_private *priv,
204473b01e57SJeff Chen 				struct host_cmd_ds_command *cmd,
204573b01e57SJeff Chen 				void *data_buf)
204673b01e57SJeff Chen {
204773b01e57SJeff Chen 	struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan;
204873b01e57SJeff Chen 	struct nxpwifi_scan_cmd_config *scan_cfg = data_buf;
204973b01e57SJeff Chen 
205073b01e57SJeff Chen 	memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
205173b01e57SJeff Chen 
205273b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_802_11_SCAN_EXT);
205373b01e57SJeff Chen 
205473b01e57SJeff Chen 	/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
205573b01e57SJeff Chen 	cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved)
205673b01e57SJeff Chen 				      + scan_cfg->tlv_buf_len + S_DS_GEN));
205773b01e57SJeff Chen 
205873b01e57SJeff Chen 	return 0;
205973b01e57SJeff Chen }
206073b01e57SJeff Chen 
206173b01e57SJeff Chen /* Prepare the background scan config command to send to firmware. */
nxpwifi_cmd_802_11_bg_scan_config(struct nxpwifi_private * priv,struct host_cmd_ds_command * cmd,void * data_buf)206273b01e57SJeff Chen int nxpwifi_cmd_802_11_bg_scan_config(struct nxpwifi_private *priv,
206373b01e57SJeff Chen 				      struct host_cmd_ds_command *cmd,
206473b01e57SJeff Chen 				      void *data_buf)
206573b01e57SJeff Chen {
206673b01e57SJeff Chen 	struct host_cmd_ds_802_11_bg_scan_config *bgscan_config =
206773b01e57SJeff Chen 					&cmd->params.bg_scan_config;
206873b01e57SJeff Chen 	struct nxpwifi_bg_scan_cfg *bgscan_cfg_in = data_buf;
206973b01e57SJeff Chen 	u8 *tlv_pos = bgscan_config->tlv;
207073b01e57SJeff Chen 	u8 num_probes;
207173b01e57SJeff Chen 	u32 ssid_len, chan_idx, scan_time, scan_type, scan_dur, chan_num;
207273b01e57SJeff Chen 	int i;
207373b01e57SJeff Chen 	struct nxpwifi_ie_types_num_probes *num_probes_tlv;
207473b01e57SJeff Chen 	struct nxpwifi_ie_types_repeat_count *repeat_count_tlv;
207573b01e57SJeff Chen 	struct nxpwifi_ie_types_min_rssi_threshold *rssi_threshold_tlv;
207673b01e57SJeff Chen 	struct nxpwifi_ie_types_bgscan_start_later *start_later_tlv;
207773b01e57SJeff Chen 	struct nxpwifi_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
207873b01e57SJeff Chen 	struct nxpwifi_ie_types_chan_list_param_set *tlv_l;
207973b01e57SJeff Chen 	struct nxpwifi_chan_scan_param_set *temp_chan;
208073b01e57SJeff Chen 
208173b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_802_11_BG_SCAN_CONFIG);
208273b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN);
208373b01e57SJeff Chen 
208473b01e57SJeff Chen 	bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action);
208573b01e57SJeff Chen 	bgscan_config->enable = bgscan_cfg_in->enable;
208673b01e57SJeff Chen 	bgscan_config->bss_type = bgscan_cfg_in->bss_type;
208773b01e57SJeff Chen 	bgscan_config->scan_interval =
208873b01e57SJeff Chen 		cpu_to_le32(bgscan_cfg_in->scan_interval);
208973b01e57SJeff Chen 	bgscan_config->report_condition =
209073b01e57SJeff Chen 		cpu_to_le32(bgscan_cfg_in->report_condition);
209173b01e57SJeff Chen 
209273b01e57SJeff Chen 	/*  stop sched scan  */
209373b01e57SJeff Chen 	if (!bgscan_config->enable)
209473b01e57SJeff Chen 		return 0;
209573b01e57SJeff Chen 
209673b01e57SJeff Chen 	bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan;
209773b01e57SJeff Chen 
209873b01e57SJeff Chen 	num_probes = (bgscan_cfg_in->num_probes ?
209973b01e57SJeff Chen 		bgscan_cfg_in->num_probes : priv->adapter->scan_probes);
210073b01e57SJeff Chen 
210173b01e57SJeff Chen 	if (num_probes) {
210273b01e57SJeff Chen 		num_probes_tlv = (struct nxpwifi_ie_types_num_probes *)tlv_pos;
210373b01e57SJeff Chen 		num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
210473b01e57SJeff Chen 		num_probes_tlv->header.len =
210573b01e57SJeff Chen 			cpu_to_le16(sizeof(num_probes_tlv->num_probes));
210673b01e57SJeff Chen 		num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes);
210773b01e57SJeff Chen 
210873b01e57SJeff Chen 		tlv_pos += sizeof(num_probes_tlv->header) +
210973b01e57SJeff Chen 			le16_to_cpu(num_probes_tlv->header.len);
211073b01e57SJeff Chen 	}
211173b01e57SJeff Chen 
211273b01e57SJeff Chen 	if (bgscan_cfg_in->repeat_count) {
211373b01e57SJeff Chen 		repeat_count_tlv =
211473b01e57SJeff Chen 			(struct nxpwifi_ie_types_repeat_count *)tlv_pos;
211573b01e57SJeff Chen 		repeat_count_tlv->header.type =
211673b01e57SJeff Chen 			cpu_to_le16(TLV_TYPE_REPEAT_COUNT);
211773b01e57SJeff Chen 		repeat_count_tlv->header.len =
211873b01e57SJeff Chen 			cpu_to_le16(sizeof(repeat_count_tlv->repeat_count));
211973b01e57SJeff Chen 		repeat_count_tlv->repeat_count =
212073b01e57SJeff Chen 			cpu_to_le16(bgscan_cfg_in->repeat_count);
212173b01e57SJeff Chen 
212273b01e57SJeff Chen 		tlv_pos += sizeof(repeat_count_tlv->header) +
212373b01e57SJeff Chen 			le16_to_cpu(repeat_count_tlv->header.len);
212473b01e57SJeff Chen 	}
212573b01e57SJeff Chen 
212673b01e57SJeff Chen 	if (bgscan_cfg_in->rssi_threshold) {
212773b01e57SJeff Chen 		rssi_threshold_tlv =
212873b01e57SJeff Chen 			(struct nxpwifi_ie_types_min_rssi_threshold *)tlv_pos;
212973b01e57SJeff Chen 		rssi_threshold_tlv->header.type =
213073b01e57SJeff Chen 			cpu_to_le16(TLV_TYPE_RSSI_LOW);
213173b01e57SJeff Chen 		rssi_threshold_tlv->header.len =
213273b01e57SJeff Chen 			cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold));
213373b01e57SJeff Chen 		rssi_threshold_tlv->rssi_threshold =
213473b01e57SJeff Chen 			cpu_to_le16(bgscan_cfg_in->rssi_threshold);
213573b01e57SJeff Chen 
213673b01e57SJeff Chen 		tlv_pos += sizeof(rssi_threshold_tlv->header) +
213773b01e57SJeff Chen 			le16_to_cpu(rssi_threshold_tlv->header.len);
213873b01e57SJeff Chen 	}
213973b01e57SJeff Chen 
214073b01e57SJeff Chen 	for (i = 0; i < bgscan_cfg_in->num_ssids; i++) {
214173b01e57SJeff Chen 		ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len;
214273b01e57SJeff Chen 
214373b01e57SJeff Chen 		wildcard_ssid_tlv =
214473b01e57SJeff Chen 			(struct nxpwifi_ie_types_wildcard_ssid_params *)tlv_pos;
214573b01e57SJeff Chen 		wildcard_ssid_tlv->header.type =
214673b01e57SJeff Chen 				cpu_to_le16(TLV_TYPE_WILDCARDSSID);
214773b01e57SJeff Chen 		wildcard_ssid_tlv->header.len =
214873b01e57SJeff Chen 			cpu_to_le16((u16)(ssid_len + sizeof(u8)));
214973b01e57SJeff Chen 
215073b01e57SJeff Chen 		/*
215173b01e57SJeff Chen 		 * max_ssid_length = 0 tells firmware to scan only for the given
215273b01e57SJeff Chen 		 * SSID. max_ssid_length = IEEE80211_MAX_SSID_LEN triggers a
215373b01e57SJeff Chen 		 * wildcard scan.
215473b01e57SJeff Chen 		 */
215573b01e57SJeff Chen 		if (ssid_len)
215673b01e57SJeff Chen 			wildcard_ssid_tlv->max_ssid_length = 0;
215773b01e57SJeff Chen 		else
215873b01e57SJeff Chen 			wildcard_ssid_tlv->max_ssid_length =
215973b01e57SJeff Chen 						IEEE80211_MAX_SSID_LEN;
216073b01e57SJeff Chen 
216173b01e57SJeff Chen 		memcpy(wildcard_ssid_tlv->ssid,
216273b01e57SJeff Chen 		       bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len);
216373b01e57SJeff Chen 
216473b01e57SJeff Chen 		tlv_pos += (sizeof(wildcard_ssid_tlv->header) +
216573b01e57SJeff Chen 			le16_to_cpu(wildcard_ssid_tlv->header.len));
216673b01e57SJeff Chen 	}
216773b01e57SJeff Chen 
216873b01e57SJeff Chen 	tlv_l = (struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos;
216973b01e57SJeff Chen 
217073b01e57SJeff Chen 	if (bgscan_cfg_in->chan_list[0].chan_number) {
217173b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO, "info: bgscan: Using supplied channel list\n");
217273b01e57SJeff Chen 
217373b01e57SJeff Chen 		tlv_l->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
217473b01e57SJeff Chen 
217573b01e57SJeff Chen 		for (chan_idx = 0;
217673b01e57SJeff Chen 		     chan_idx < NXPWIFI_BG_SCAN_CHAN_MAX &&
217773b01e57SJeff Chen 		     bgscan_cfg_in->chan_list[chan_idx].chan_number;
217873b01e57SJeff Chen 		     chan_idx++) {
217973b01e57SJeff Chen 			temp_chan = &tlv_l->chan_scan_param[chan_idx];
218073b01e57SJeff Chen 
218173b01e57SJeff Chen 			/* Increment the TLV header length by size appended */
218273b01e57SJeff Chen 			le16_unaligned_add_cpu(&tlv_l->header.len,
218373b01e57SJeff Chen 					       sizeof(*tlv_l->chan_scan_param));
218473b01e57SJeff Chen 
218573b01e57SJeff Chen 			temp_chan->chan_number =
218673b01e57SJeff Chen 				bgscan_cfg_in->chan_list[chan_idx].chan_number;
218773b01e57SJeff Chen 			temp_chan->band_cfg =
218873b01e57SJeff Chen 				bgscan_cfg_in->chan_list[chan_idx].radio_type;
218973b01e57SJeff Chen 
219073b01e57SJeff Chen 			scan_type =
219173b01e57SJeff Chen 				bgscan_cfg_in->chan_list[chan_idx].scan_type;
219273b01e57SJeff Chen 
219373b01e57SJeff Chen 			if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE)
219473b01e57SJeff Chen 				temp_chan->chan_scan_mode_bmap |=
219573b01e57SJeff Chen 					NXPWIFI_PASSIVE_SCAN;
219673b01e57SJeff Chen 			else
219773b01e57SJeff Chen 				temp_chan->chan_scan_mode_bmap &=
219873b01e57SJeff Chen 					~NXPWIFI_PASSIVE_SCAN;
219973b01e57SJeff Chen 
220073b01e57SJeff Chen 			scan_time = bgscan_cfg_in->chan_list[chan_idx].scan_time;
220173b01e57SJeff Chen 
220273b01e57SJeff Chen 			if (scan_time) {
220373b01e57SJeff Chen 				scan_dur = (u16)scan_time;
220473b01e57SJeff Chen 			} else {
220573b01e57SJeff Chen 				scan_dur = (scan_type ==
220673b01e57SJeff Chen 					    NXPWIFI_SCAN_TYPE_PASSIVE) ?
220773b01e57SJeff Chen 					    priv->adapter->passive_scan_time :
220873b01e57SJeff Chen 					    priv->adapter->specific_scan_time;
220973b01e57SJeff Chen 			}
221073b01e57SJeff Chen 
221173b01e57SJeff Chen 			temp_chan->min_scan_time = cpu_to_le16(scan_dur);
221273b01e57SJeff Chen 			temp_chan->max_scan_time = cpu_to_le16(scan_dur);
221373b01e57SJeff Chen 		}
221473b01e57SJeff Chen 	} else {
221573b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, INFO,
221673b01e57SJeff Chen 			    "info: bgscan: Creating full region channel list\n");
221773b01e57SJeff Chen 		chan_num =
221873b01e57SJeff Chen 			nxpwifi_bgscan_create_channel_list
221973b01e57SJeff Chen 			(priv, bgscan_cfg_in,
222073b01e57SJeff Chen 			 tlv_l->chan_scan_param);
222173b01e57SJeff Chen 		le16_unaligned_add_cpu(&tlv_l->header.len,
222273b01e57SJeff Chen 				       chan_num *
222373b01e57SJeff Chen 				       sizeof(*tlv_l->chan_scan_param));
222473b01e57SJeff Chen 	}
222573b01e57SJeff Chen 
222673b01e57SJeff Chen 	tlv_pos += (sizeof(tlv_l->header)
222773b01e57SJeff Chen 			+ le16_to_cpu(tlv_l->header.len));
222873b01e57SJeff Chen 
222973b01e57SJeff Chen 	if (bgscan_cfg_in->start_later) {
223073b01e57SJeff Chen 		start_later_tlv =
223173b01e57SJeff Chen 			(struct nxpwifi_ie_types_bgscan_start_later *)tlv_pos;
223273b01e57SJeff Chen 		start_later_tlv->header.type =
223373b01e57SJeff Chen 			cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER);
223473b01e57SJeff Chen 		start_later_tlv->header.len =
223573b01e57SJeff Chen 			cpu_to_le16(sizeof(start_later_tlv->start_later));
223673b01e57SJeff Chen 		start_later_tlv->start_later =
223773b01e57SJeff Chen 			cpu_to_le16(bgscan_cfg_in->start_later);
223873b01e57SJeff Chen 
223973b01e57SJeff Chen 		tlv_pos += sizeof(start_later_tlv->header) +
224073b01e57SJeff Chen 			le16_to_cpu(start_later_tlv->header.len);
224173b01e57SJeff Chen 	}
224273b01e57SJeff Chen 
224373b01e57SJeff Chen 	/* Append vendor specific element TLV */
224473b01e57SJeff Chen 	nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_BGSCAN, &tlv_pos);
224573b01e57SJeff Chen 
224673b01e57SJeff Chen 	le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv);
224773b01e57SJeff Chen 
224873b01e57SJeff Chen 	return 0;
224973b01e57SJeff Chen }
225073b01e57SJeff Chen 
nxpwifi_stop_bg_scan(struct nxpwifi_private * priv)225173b01e57SJeff Chen int nxpwifi_stop_bg_scan(struct nxpwifi_private *priv)
225273b01e57SJeff Chen {
225373b01e57SJeff Chen 	struct nxpwifi_bg_scan_cfg *bgscan_cfg;
225473b01e57SJeff Chen 	int ret;
225573b01e57SJeff Chen 
225673b01e57SJeff Chen 	if (!priv->sched_scanning) {
225773b01e57SJeff Chen 		nxpwifi_dbg(priv->adapter, MSG, "bgscan already stopped!\n");
225873b01e57SJeff Chen 		return 0;
225973b01e57SJeff Chen 	}
226073b01e57SJeff Chen 
226173b01e57SJeff Chen 	bgscan_cfg = kzalloc_obj(*bgscan_cfg, GFP_KERNEL);
226273b01e57SJeff Chen 	if (!bgscan_cfg)
226373b01e57SJeff Chen 		return -ENOMEM;
226473b01e57SJeff Chen 
226573b01e57SJeff Chen 	bgscan_cfg->bss_type = NXPWIFI_BSS_MODE_INFRA;
226673b01e57SJeff Chen 	bgscan_cfg->action = NXPWIFI_BGSCAN_ACT_SET;
226773b01e57SJeff Chen 	bgscan_cfg->enable = false;
226873b01e57SJeff Chen 
226973b01e57SJeff Chen 	ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG,
227073b01e57SJeff Chen 			       HOST_ACT_GEN_SET, 0, bgscan_cfg, true);
227173b01e57SJeff Chen 	if (!ret)
227273b01e57SJeff Chen 		priv->sched_scanning = false;
227373b01e57SJeff Chen 
227473b01e57SJeff Chen 	kfree(bgscan_cfg);
227573b01e57SJeff Chen 	return ret;
227673b01e57SJeff Chen }
227773b01e57SJeff Chen 
227873b01e57SJeff Chen static void
nxpwifi_update_chan_statistics(struct nxpwifi_private * priv,struct nxpwifi_ietypes_chanstats * tlv_stat)227973b01e57SJeff Chen nxpwifi_update_chan_statistics(struct nxpwifi_private *priv,
228073b01e57SJeff Chen 			       struct nxpwifi_ietypes_chanstats *tlv_stat)
228173b01e57SJeff Chen {
228273b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
228373b01e57SJeff Chen 	u8 i, num_chan;
228473b01e57SJeff Chen 	struct nxpwifi_fw_chan_stats *fw_chan_stats;
228573b01e57SJeff Chen 	struct nxpwifi_chan_stats chan_stats;
228673b01e57SJeff Chen 
228773b01e57SJeff Chen 	fw_chan_stats = (void *)((u8 *)tlv_stat +
228873b01e57SJeff Chen 			      sizeof(struct nxpwifi_ie_types_header));
228973b01e57SJeff Chen 	num_chan = le16_to_cpu(tlv_stat->header.len) /
229073b01e57SJeff Chen 					      sizeof(struct nxpwifi_chan_stats);
229173b01e57SJeff Chen 
229273b01e57SJeff Chen 	for (i = 0 ; i < num_chan; i++) {
229373b01e57SJeff Chen 		if (adapter->survey_idx >= adapter->num_in_chan_stats) {
229473b01e57SJeff Chen 			nxpwifi_dbg(adapter, WARN,
229573b01e57SJeff Chen 				    "FW reported too many channel results (max %d)\n",
229673b01e57SJeff Chen 				    adapter->num_in_chan_stats);
229773b01e57SJeff Chen 			return;
229873b01e57SJeff Chen 		}
229973b01e57SJeff Chen 		chan_stats.chan_num = fw_chan_stats->chan_num;
230073b01e57SJeff Chen 		chan_stats.bandcfg = fw_chan_stats->bandcfg;
230173b01e57SJeff Chen 		chan_stats.flags = fw_chan_stats->flags;
230273b01e57SJeff Chen 		chan_stats.noise = fw_chan_stats->noise;
230373b01e57SJeff Chen 		chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss);
230473b01e57SJeff Chen 		chan_stats.cca_scan_dur =
230573b01e57SJeff Chen 				       le16_to_cpu(fw_chan_stats->cca_scan_dur);
230673b01e57SJeff Chen 		chan_stats.cca_busy_dur =
230773b01e57SJeff Chen 				       le16_to_cpu(fw_chan_stats->cca_busy_dur);
230873b01e57SJeff Chen 		nxpwifi_dbg(adapter, INFO,
230973b01e57SJeff Chen 			    "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n",
231073b01e57SJeff Chen 			    chan_stats.chan_num,
231173b01e57SJeff Chen 			    chan_stats.noise,
231273b01e57SJeff Chen 			    chan_stats.total_bss,
231373b01e57SJeff Chen 			    chan_stats.cca_scan_dur,
231473b01e57SJeff Chen 			    chan_stats.cca_busy_dur);
231573b01e57SJeff Chen 		memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats,
231673b01e57SJeff Chen 		       sizeof(struct nxpwifi_chan_stats));
231773b01e57SJeff Chen 		fw_chan_stats++;
231873b01e57SJeff Chen 	}
231973b01e57SJeff Chen }
232073b01e57SJeff Chen 
232173b01e57SJeff Chen /* Handle the extended scan command response. */
nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private * priv,struct host_cmd_ds_command * resp)232273b01e57SJeff Chen int nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private *priv,
232373b01e57SJeff Chen 				struct host_cmd_ds_command *resp)
232473b01e57SJeff Chen {
232573b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
232673b01e57SJeff Chen 	struct host_cmd_ds_802_11_scan_ext *ext_scan_resp;
232773b01e57SJeff Chen 	struct nxpwifi_ie_types_header *tlv;
232873b01e57SJeff Chen 	struct nxpwifi_ietypes_chanstats *tlv_stat;
232973b01e57SJeff Chen 	u16 buf_left, type, len;
233073b01e57SJeff Chen 
233173b01e57SJeff Chen 	struct host_cmd_ds_command *cmd_ptr;
233273b01e57SJeff Chen 	struct cmd_ctrl_node *cmd_node;
233373b01e57SJeff Chen 	bool complete_scan = false;
233473b01e57SJeff Chen 
233573b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO, "info: EXT scan returns successfully\n");
233673b01e57SJeff Chen 
233773b01e57SJeff Chen 	ext_scan_resp = &resp->params.ext_scan;
233873b01e57SJeff Chen 
233973b01e57SJeff Chen 	tlv = (void *)ext_scan_resp->tlv_buffer;
234073b01e57SJeff Chen 	buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN);
234173b01e57SJeff Chen 
234273b01e57SJeff Chen 	while (buf_left >= sizeof(struct nxpwifi_ie_types_header)) {
234373b01e57SJeff Chen 		type = le16_to_cpu(tlv->type);
234473b01e57SJeff Chen 		len = le16_to_cpu(tlv->len);
234573b01e57SJeff Chen 
234673b01e57SJeff Chen 		if (buf_left < (sizeof(struct nxpwifi_ie_types_header) + len)) {
234773b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
234873b01e57SJeff Chen 				    "error processing scan response TLVs");
234973b01e57SJeff Chen 			break;
235073b01e57SJeff Chen 		}
235173b01e57SJeff Chen 
235273b01e57SJeff Chen 		switch (type) {
235373b01e57SJeff Chen 		case TLV_TYPE_CHANNEL_STATS:
235473b01e57SJeff Chen 			tlv_stat = (void *)tlv;
235573b01e57SJeff Chen 			nxpwifi_update_chan_statistics(priv, tlv_stat);
235673b01e57SJeff Chen 			break;
235773b01e57SJeff Chen 		default:
235873b01e57SJeff Chen 			break;
235973b01e57SJeff Chen 		}
236073b01e57SJeff Chen 
236173b01e57SJeff Chen 		buf_left -= len + sizeof(struct nxpwifi_ie_types_header);
236273b01e57SJeff Chen 		tlv = (void *)((u8 *)tlv + len +
236373b01e57SJeff Chen 			       sizeof(struct nxpwifi_ie_types_header));
236473b01e57SJeff Chen 	}
236573b01e57SJeff Chen 
236673b01e57SJeff Chen 	spin_lock_bh(&adapter->cmd_pending_q_lock);
236773b01e57SJeff Chen 	spin_lock_bh(&adapter->scan_pending_q_lock);
236873b01e57SJeff Chen 	if (list_empty(&adapter->scan_pending_q)) {
236973b01e57SJeff Chen 		complete_scan = true;
237073b01e57SJeff Chen 		list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) {
237173b01e57SJeff Chen 			cmd_ptr = (void *)cmd_node->cmd_skb->data;
237273b01e57SJeff Chen 			if (le16_to_cpu(cmd_ptr->command) ==
237373b01e57SJeff Chen 			    HOST_CMD_802_11_SCAN_EXT) {
237473b01e57SJeff Chen 				nxpwifi_dbg(adapter, INFO,
237573b01e57SJeff Chen 					    "Scan pending in command pending list");
237673b01e57SJeff Chen 				complete_scan = false;
237773b01e57SJeff Chen 				break;
237873b01e57SJeff Chen 			}
237973b01e57SJeff Chen 		}
238073b01e57SJeff Chen 	}
238173b01e57SJeff Chen 	spin_unlock_bh(&adapter->scan_pending_q_lock);
238273b01e57SJeff Chen 	spin_unlock_bh(&adapter->cmd_pending_q_lock);
238373b01e57SJeff Chen 
238473b01e57SJeff Chen 	if (complete_scan)
238573b01e57SJeff Chen 		nxpwifi_complete_scan(priv);
238673b01e57SJeff Chen 
238773b01e57SJeff Chen 	return 0;
238873b01e57SJeff Chen }
238973b01e57SJeff Chen 
239073b01e57SJeff Chen /*
239173b01e57SJeff Chen  * Handle the extended scan report event: parse the results and notify
239273b01e57SJeff Chen  * cfg80211.
239373b01e57SJeff Chen  */
nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private * priv,void * buf)239473b01e57SJeff Chen int nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private *priv,
239573b01e57SJeff Chen 					 void *buf)
239673b01e57SJeff Chen {
239773b01e57SJeff Chen 	int ret = 0;
239873b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
239973b01e57SJeff Chen 	u8 *bss_info;
240073b01e57SJeff Chen 	u32 bytes_left, bytes_left_for_tlv, idx;
240173b01e57SJeff Chen 	u16 type, len;
240273b01e57SJeff Chen 	struct nxpwifi_ie_types_data *tlv;
240373b01e57SJeff Chen 	struct nxpwifi_ie_types_scan_rsp *scan_rsp_tlv;
240473b01e57SJeff Chen 	struct nxpwifi_ie_types_scan_inf *scan_info_tlv;
240573b01e57SJeff Chen 	u8 *radio_type;
240673b01e57SJeff Chen 	u64 fw_tsf = 0;
240773b01e57SJeff Chen 	s32 rssi = 0;
240873b01e57SJeff Chen 	struct nxpwifi_event_scan_result *event_scan = buf;
240973b01e57SJeff Chen 	u8 num_of_set = event_scan->num_of_set;
241073b01e57SJeff Chen 	u8 *scan_resp = buf + sizeof(struct nxpwifi_event_scan_result);
241173b01e57SJeff Chen 	u16 scan_resp_size = le16_to_cpu(event_scan->buf_size);
241273b01e57SJeff Chen 
241373b01e57SJeff Chen 	if (num_of_set > NXPWIFI_MAX_AP) {
241473b01e57SJeff Chen 		nxpwifi_dbg(adapter, ERROR,
241573b01e57SJeff Chen 			    "EXT_SCAN: Invalid number of AP returned (%d)!!\n",
241673b01e57SJeff Chen 			    num_of_set);
241773b01e57SJeff Chen 		ret = -EINVAL;
241873b01e57SJeff Chen 		goto check_next_scan;
241973b01e57SJeff Chen 	}
242073b01e57SJeff Chen 
242173b01e57SJeff Chen 	bytes_left = scan_resp_size;
242273b01e57SJeff Chen 	nxpwifi_dbg(adapter, INFO,
242373b01e57SJeff Chen 		    "EXT_SCAN: size %d, returned %d APs...",
242473b01e57SJeff Chen 		    scan_resp_size, num_of_set);
242573b01e57SJeff Chen 	nxpwifi_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf,
242673b01e57SJeff Chen 			 scan_resp_size +
242773b01e57SJeff Chen 			 sizeof(struct nxpwifi_event_scan_result));
242873b01e57SJeff Chen 
242973b01e57SJeff Chen 	tlv = (struct nxpwifi_ie_types_data *)scan_resp;
243073b01e57SJeff Chen 
243173b01e57SJeff Chen 	for (idx = 0; idx < num_of_set && bytes_left; idx++) {
243273b01e57SJeff Chen 		type = le16_to_cpu(tlv->header.type);
243373b01e57SJeff Chen 		len = le16_to_cpu(tlv->header.len);
243473b01e57SJeff Chen 		if (bytes_left < sizeof(struct nxpwifi_ie_types_header) + len) {
243573b01e57SJeff Chen 			nxpwifi_dbg(adapter, ERROR,
243673b01e57SJeff Chen 				    "EXT_SCAN: Error bytes left < TLV length\n");
243773b01e57SJeff Chen 			break;
243873b01e57SJeff Chen 		}
243973b01e57SJeff Chen 		scan_rsp_tlv = NULL;
244073b01e57SJeff Chen 		scan_info_tlv = NULL;
244173b01e57SJeff Chen 		bytes_left_for_tlv = bytes_left;
244273b01e57SJeff Chen 
244373b01e57SJeff Chen 		/*
244473b01e57SJeff Chen 		 * BSS response TLV with beacon or probe response buffer
244573b01e57SJeff Chen 		 * at the initial position of each descriptor
244673b01e57SJeff Chen 		 */
244773b01e57SJeff Chen 		if (type != TLV_TYPE_BSS_SCAN_RSP)
244873b01e57SJeff Chen 			break;
244973b01e57SJeff Chen 
245073b01e57SJeff Chen 		bss_info = (u8 *)tlv;
245173b01e57SJeff Chen 		scan_rsp_tlv = (struct nxpwifi_ie_types_scan_rsp *)tlv;
245273b01e57SJeff Chen 		tlv = (struct nxpwifi_ie_types_data *)(tlv->data + len);
245373b01e57SJeff Chen 		bytes_left_for_tlv -=
245473b01e57SJeff Chen 			(len + sizeof(struct nxpwifi_ie_types_header));
245573b01e57SJeff Chen 
245673b01e57SJeff Chen 		while (bytes_left_for_tlv >=
245773b01e57SJeff Chen 		       sizeof(struct nxpwifi_ie_types_header) &&
245873b01e57SJeff Chen 		       le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) {
245973b01e57SJeff Chen 			type = le16_to_cpu(tlv->header.type);
246073b01e57SJeff Chen 			len = le16_to_cpu(tlv->header.len);
246173b01e57SJeff Chen 			if (bytes_left_for_tlv <
246273b01e57SJeff Chen 			    sizeof(struct nxpwifi_ie_types_header) + len) {
246373b01e57SJeff Chen 				nxpwifi_dbg(adapter, ERROR,
246473b01e57SJeff Chen 					    "EXT_SCAN: Error in processing TLV,\t"
246573b01e57SJeff Chen 					    "bytes left < TLV length\n");
246673b01e57SJeff Chen 				scan_rsp_tlv = NULL;
246773b01e57SJeff Chen 				bytes_left_for_tlv = 0;
246873b01e57SJeff Chen 				continue;
246973b01e57SJeff Chen 			}
247073b01e57SJeff Chen 			switch (type) {
247173b01e57SJeff Chen 			case TLV_TYPE_BSS_SCAN_INFO:
247273b01e57SJeff Chen 				scan_info_tlv =
247373b01e57SJeff Chen 					(struct nxpwifi_ie_types_scan_inf *)tlv;
247473b01e57SJeff Chen 				if (len !=
247573b01e57SJeff Chen 				 sizeof(struct nxpwifi_ie_types_scan_inf) -
247673b01e57SJeff Chen 				 sizeof(struct nxpwifi_ie_types_header)) {
247773b01e57SJeff Chen 					bytes_left_for_tlv = 0;
247873b01e57SJeff Chen 					continue;
247973b01e57SJeff Chen 				}
248073b01e57SJeff Chen 				break;
248173b01e57SJeff Chen 			default:
248273b01e57SJeff Chen 				break;
248373b01e57SJeff Chen 			}
248473b01e57SJeff Chen 			tlv = (struct nxpwifi_ie_types_data *)(tlv->data + len);
248573b01e57SJeff Chen 			bytes_left -=
248673b01e57SJeff Chen 				(len + sizeof(struct nxpwifi_ie_types_header));
248773b01e57SJeff Chen 			bytes_left_for_tlv -=
248873b01e57SJeff Chen 				(len + sizeof(struct nxpwifi_ie_types_header));
248973b01e57SJeff Chen 		}
249073b01e57SJeff Chen 
249173b01e57SJeff Chen 		if (!scan_rsp_tlv)
249273b01e57SJeff Chen 			break;
249373b01e57SJeff Chen 
249473b01e57SJeff Chen 		/*
249573b01e57SJeff Chen 		 * Advance pointer to the beacon buffer length and
249673b01e57SJeff Chen 		 * update the bytes count so that the function
249773b01e57SJeff Chen 		 * wlan_interpret_bss_desc_with_ie() can handle the
249873b01e57SJeff Chen 		 * scan buffer withut any change
249973b01e57SJeff Chen 		 */
250073b01e57SJeff Chen 		bss_info += sizeof(u16);
250173b01e57SJeff Chen 		bytes_left -= sizeof(u16);
250273b01e57SJeff Chen 
250373b01e57SJeff Chen 		if (scan_info_tlv) {
250473b01e57SJeff Chen 			rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi));
250573b01e57SJeff Chen 			rssi *= 100;           /* Convert dBm to mBm */
250673b01e57SJeff Chen 			nxpwifi_dbg(adapter, INFO,
250773b01e57SJeff Chen 				    "info: InterpretIE: RSSI=%d\n", rssi);
250873b01e57SJeff Chen 			fw_tsf = le64_to_cpu(scan_info_tlv->tsf);
250973b01e57SJeff Chen 			radio_type = &scan_info_tlv->radio_type;
251073b01e57SJeff Chen 		} else {
251173b01e57SJeff Chen 			radio_type = NULL;
251273b01e57SJeff Chen 		}
251373b01e57SJeff Chen 		ret = nxpwifi_parse_single_response_buf(priv, &bss_info,
251473b01e57SJeff Chen 							&bytes_left, fw_tsf,
251573b01e57SJeff Chen 							radio_type, true, rssi);
251673b01e57SJeff Chen 		if (ret)
251773b01e57SJeff Chen 			goto check_next_scan;
251873b01e57SJeff Chen 	}
251973b01e57SJeff Chen 
252073b01e57SJeff Chen check_next_scan:
252173b01e57SJeff Chen 	if (!event_scan->more_event)
252273b01e57SJeff Chen 		nxpwifi_check_next_scan_command(priv);
252373b01e57SJeff Chen 
252473b01e57SJeff Chen 	return ret;
252573b01e57SJeff Chen }
252673b01e57SJeff Chen 
252773b01e57SJeff Chen /*
252873b01e57SJeff Chen  * Prepare the background scan query command. Sets the command ID, size,
252973b01e57SJeff Chen  * flush parameter, and fixes endianness.
253073b01e57SJeff Chen  */
nxpwifi_cmd_802_11_bg_scan_query(struct host_cmd_ds_command * cmd)253173b01e57SJeff Chen int nxpwifi_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd)
253273b01e57SJeff Chen {
253373b01e57SJeff Chen 	struct host_cmd_ds_802_11_bg_scan_query *bg_query =
253473b01e57SJeff Chen 		&cmd->params.bg_scan_query;
253573b01e57SJeff Chen 
253673b01e57SJeff Chen 	cmd->command = cpu_to_le16(HOST_CMD_802_11_BG_SCAN_QUERY);
253773b01e57SJeff Chen 	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query)
253873b01e57SJeff Chen 				+ S_DS_GEN);
253973b01e57SJeff Chen 
254073b01e57SJeff Chen 	bg_query->flush = 1;
254173b01e57SJeff Chen 
254273b01e57SJeff Chen 	return 0;
254373b01e57SJeff Chen }
254473b01e57SJeff Chen 
254573b01e57SJeff Chen /* Insert a scan command node into the scan_pending_q. */
254673b01e57SJeff Chen void
nxpwifi_queue_scan_cmd(struct nxpwifi_private * priv,struct cmd_ctrl_node * cmd_node)254773b01e57SJeff Chen nxpwifi_queue_scan_cmd(struct nxpwifi_private *priv,
254873b01e57SJeff Chen 		       struct cmd_ctrl_node *cmd_node)
254973b01e57SJeff Chen {
255073b01e57SJeff Chen 	struct nxpwifi_adapter *adapter = priv->adapter;
255173b01e57SJeff Chen 
255273b01e57SJeff Chen 	cmd_node->wait_q_enabled = true;
255373b01e57SJeff Chen 	cmd_node->condition = &adapter->scan_wait_q_woken;
255473b01e57SJeff Chen 	spin_lock_bh(&adapter->scan_pending_q_lock);
255573b01e57SJeff Chen 	list_add_tail(&cmd_node->list, &adapter->scan_pending_q);
255673b01e57SJeff Chen 	spin_unlock_bh(&adapter->scan_pending_q_lock);
255773b01e57SJeff Chen }
255873b01e57SJeff Chen 
255973b01e57SJeff Chen /* Append a vendor-specific element TLV to the buffer. */
256073b01e57SJeff Chen int
nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private * priv,u16 vsie_mask,u8 ** buffer)256173b01e57SJeff Chen nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private *priv,
256273b01e57SJeff Chen 			    u16 vsie_mask, u8 **buffer)
256373b01e57SJeff Chen {
256473b01e57SJeff Chen 	int id, ret_len = 0;
256573b01e57SJeff Chen 	struct nxpwifi_ie_types_vendor_param_set *vs_param_set;
256673b01e57SJeff Chen 
256773b01e57SJeff Chen 	if (!buffer)
256873b01e57SJeff Chen 		return 0;
256973b01e57SJeff Chen 	if (!(*buffer))
257073b01e57SJeff Chen 		return 0;
257173b01e57SJeff Chen 
257273b01e57SJeff Chen 	/*
257373b01e57SJeff Chen 	 * Traverse through the saved vendor specific element array and append
257473b01e57SJeff Chen 	 * the selected(scan/assoc) element as TLV to the command
257573b01e57SJeff Chen 	 */
257673b01e57SJeff Chen 	for (id = 0; id < NXPWIFI_MAX_VSIE_NUM; id++) {
257773b01e57SJeff Chen 		if (priv->vs_ie[id].mask & vsie_mask) {
257873b01e57SJeff Chen 			vs_param_set =
257973b01e57SJeff Chen 				(struct nxpwifi_ie_types_vendor_param_set *)
258073b01e57SJeff Chen 				*buffer;
258173b01e57SJeff Chen 			vs_param_set->header.type =
258273b01e57SJeff Chen 				cpu_to_le16(TLV_TYPE_PASSTHROUGH);
258373b01e57SJeff Chen 			vs_param_set->header.len =
258473b01e57SJeff Chen 				cpu_to_le16((((u16)priv->vs_ie[id].ie[1])
258573b01e57SJeff Chen 				& 0x00FF) + 2);
258673b01e57SJeff Chen 			if (le16_to_cpu(vs_param_set->header.len) >
258773b01e57SJeff Chen 				NXPWIFI_MAX_VSIE_LEN) {
258873b01e57SJeff Chen 				nxpwifi_dbg(priv->adapter, ERROR,
258973b01e57SJeff Chen 					    "Invalid param length!\n");
259073b01e57SJeff Chen 				break;
259173b01e57SJeff Chen 			}
259273b01e57SJeff Chen 
259373b01e57SJeff Chen 			memcpy(vs_param_set->ie, priv->vs_ie[id].ie,
259473b01e57SJeff Chen 			       le16_to_cpu(vs_param_set->header.len));
259573b01e57SJeff Chen 			*buffer += le16_to_cpu(vs_param_set->header.len) +
259673b01e57SJeff Chen 				   sizeof(struct nxpwifi_ie_types_header);
259773b01e57SJeff Chen 			ret_len += le16_to_cpu(vs_param_set->header.len) +
259873b01e57SJeff Chen 				   sizeof(struct nxpwifi_ie_types_header);
259973b01e57SJeff Chen 		}
260073b01e57SJeff Chen 	}
260173b01e57SJeff Chen 	return ret_len;
260273b01e57SJeff Chen }
260373b01e57SJeff Chen 
260473b01e57SJeff Chen /*
260573b01e57SJeff Chen  * Save the beacon buffer of the current BSS descriptor.
260673b01e57SJeff Chen  *
260773b01e57SJeff Chen  * The buffer is preserved so it can be restored when the current SSID's
260873b01e57SJeff Chen  * beacon is missing, such as when:
260973b01e57SJeff Chen  *  - the SSID was not found in the latest scan, or
261073b01e57SJeff Chen  *  - the SSID was the last entry in the scan table and was overwritten.
261173b01e57SJeff Chen  */
261273b01e57SJeff Chen void
nxpwifi_save_curr_bcn(struct nxpwifi_private * priv)261373b01e57SJeff Chen nxpwifi_save_curr_bcn(struct nxpwifi_private *priv)
261473b01e57SJeff Chen {
261573b01e57SJeff Chen 	struct nxpwifi_bssdescriptor *curr_bss =
261673b01e57SJeff Chen 		&priv->curr_bss_params.bss_descriptor;
261773b01e57SJeff Chen 
261873b01e57SJeff Chen 	if (!curr_bss->beacon_buf_size)
261973b01e57SJeff Chen 		return;
262073b01e57SJeff Chen 
262173b01e57SJeff Chen 	/* allocate beacon buffer at 1st time; or if it's size has changed */
262273b01e57SJeff Chen 	if (!priv->curr_bcn_buf ||
262373b01e57SJeff Chen 	    priv->curr_bcn_size != curr_bss->beacon_buf_size) {
262473b01e57SJeff Chen 		priv->curr_bcn_size = curr_bss->beacon_buf_size;
262573b01e57SJeff Chen 
262673b01e57SJeff Chen 		kfree(priv->curr_bcn_buf);
262773b01e57SJeff Chen 		priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size,
262873b01e57SJeff Chen 					     GFP_ATOMIC);
262973b01e57SJeff Chen 		if (!priv->curr_bcn_buf)
263073b01e57SJeff Chen 			return;
263173b01e57SJeff Chen 	}
263273b01e57SJeff Chen 
263373b01e57SJeff Chen 	memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf,
263473b01e57SJeff Chen 	       curr_bss->beacon_buf_size);
263573b01e57SJeff Chen 	nxpwifi_dbg(priv->adapter, INFO,
263673b01e57SJeff Chen 		    "info: current beacon saved %d\n",
263773b01e57SJeff Chen 		    priv->curr_bcn_size);
263873b01e57SJeff Chen 
263973b01e57SJeff Chen 	curr_bss->beacon_buf = priv->curr_bcn_buf;
264073b01e57SJeff Chen 
264173b01e57SJeff Chen 	/* adjust the pointers in the current BSS descriptor */
264273b01e57SJeff Chen 	if (curr_bss->bcn_wpa_ie)
264373b01e57SJeff Chen 		curr_bss->bcn_wpa_ie =
264473b01e57SJeff Chen 			(struct ieee_types_vendor_specific *)
264573b01e57SJeff Chen 			(curr_bss->beacon_buf +
264673b01e57SJeff Chen 			 curr_bss->wpa_offset);
264773b01e57SJeff Chen 
264873b01e57SJeff Chen 	if (curr_bss->bcn_rsn_ie)
264973b01e57SJeff Chen 		curr_bss->bcn_rsn_ie =
265073b01e57SJeff Chen 			(struct element *)(curr_bss->beacon_buf +
265173b01e57SJeff Chen 					   curr_bss->rsn_offset);
265273b01e57SJeff Chen 
265373b01e57SJeff Chen 	if (curr_bss->bcn_ht_cap)
265473b01e57SJeff Chen 		curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *)
265573b01e57SJeff Chen 			(curr_bss->beacon_buf +
265673b01e57SJeff Chen 			 curr_bss->ht_cap_offset);
265773b01e57SJeff Chen 
265873b01e57SJeff Chen 	if (curr_bss->bcn_ht_oper)
265973b01e57SJeff Chen 		curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *)
266073b01e57SJeff Chen 			(curr_bss->beacon_buf +
266173b01e57SJeff Chen 			 curr_bss->ht_info_offset);
266273b01e57SJeff Chen 
266373b01e57SJeff Chen 	if (curr_bss->bcn_vht_cap)
266473b01e57SJeff Chen 		curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf +
266573b01e57SJeff Chen 						 curr_bss->vht_cap_offset);
266673b01e57SJeff Chen 
266773b01e57SJeff Chen 	if (curr_bss->bcn_vht_oper)
266873b01e57SJeff Chen 		curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf +
266973b01e57SJeff Chen 						  curr_bss->vht_info_offset);
267073b01e57SJeff Chen 
267173b01e57SJeff Chen 	if (curr_bss->bcn_he_cap)
267273b01e57SJeff Chen 		curr_bss->bcn_he_cap = (void *)(curr_bss->beacon_buf +
267373b01e57SJeff Chen 						curr_bss->he_cap_offset);
267473b01e57SJeff Chen 
267573b01e57SJeff Chen 	if (curr_bss->bcn_he_oper)
267673b01e57SJeff Chen 		curr_bss->bcn_he_oper = (void *)(curr_bss->beacon_buf +
267773b01e57SJeff Chen 						 curr_bss->he_info_offset);
267873b01e57SJeff Chen 
267973b01e57SJeff Chen 	if (curr_bss->bcn_bss_co_2040)
268073b01e57SJeff Chen 		curr_bss->bcn_bss_co_2040 =
268173b01e57SJeff Chen 			(curr_bss->beacon_buf + curr_bss->bss_co_2040_offset);
268273b01e57SJeff Chen 
268373b01e57SJeff Chen 	if (curr_bss->bcn_ext_cap)
268473b01e57SJeff Chen 		curr_bss->bcn_ext_cap = curr_bss->beacon_buf +
268573b01e57SJeff Chen 			curr_bss->ext_cap_offset;
268673b01e57SJeff Chen 
268773b01e57SJeff Chen 	if (curr_bss->oper_mode)
268873b01e57SJeff Chen 		curr_bss->oper_mode = (void *)(curr_bss->beacon_buf +
268973b01e57SJeff Chen 					       curr_bss->oper_mode_offset);
269073b01e57SJeff Chen }
269173b01e57SJeff Chen 
269273b01e57SJeff Chen /* Free the beacon buffer in the current BSS descriptor. */
269373b01e57SJeff Chen void
nxpwifi_free_curr_bcn(struct nxpwifi_private * priv)269473b01e57SJeff Chen nxpwifi_free_curr_bcn(struct nxpwifi_private *priv)
269573b01e57SJeff Chen {
269673b01e57SJeff Chen 	kfree(priv->curr_bcn_buf);
269773b01e57SJeff Chen 	priv->curr_bcn_buf = NULL;
269873b01e57SJeff Chen }
2699