xref: /openbmc/linux/drivers/net/wireless/marvell/mwifiex/scan.c (revision fac59652993f075d57860769c99045b3ca18780d)
1828c91f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2277b024eSKalle Valo /*
3932183aaSGanapathi Bhat  * NXP Wireless LAN device driver: scan ioctl and command handling
4277b024eSKalle Valo  *
5932183aaSGanapathi Bhat  * Copyright 2011-2020 NXP
6277b024eSKalle Valo  */
7277b024eSKalle Valo 
8277b024eSKalle Valo #include "decl.h"
9277b024eSKalle Valo #include "ioctl.h"
10277b024eSKalle Valo #include "util.h"
11277b024eSKalle Valo #include "fw.h"
12277b024eSKalle Valo #include "main.h"
13277b024eSKalle Valo #include "11n.h"
14277b024eSKalle Valo #include "cfg80211.h"
15277b024eSKalle Valo 
16277b024eSKalle Valo /* The maximum number of channels the firmware can scan per command */
17277b024eSKalle Valo #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN   14
18277b024eSKalle Valo 
19277b024eSKalle Valo #define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD	4
20277b024eSKalle Valo 
21277b024eSKalle Valo /* Memory needed to store a max sized Channel List TLV for a firmware scan */
22277b024eSKalle Valo #define CHAN_TLV_MAX_SIZE  (sizeof(struct mwifiex_ie_types_header)         \
23277b024eSKalle Valo 				+ (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN     \
24277b024eSKalle Valo 				*sizeof(struct mwifiex_chan_scan_param_set)))
25277b024eSKalle Valo 
26277b024eSKalle Valo /* Memory needed to store supported rate */
27277b024eSKalle Valo #define RATE_TLV_MAX_SIZE   (sizeof(struct mwifiex_ie_types_rates_param_set) \
28277b024eSKalle Valo 				+ HOSTCMD_SUPPORTED_RATES)
29277b024eSKalle Valo 
30277b024eSKalle Valo /* Memory needed to store a max number/size WildCard SSID TLV for a firmware
31277b024eSKalle Valo 	scan */
32277b024eSKalle Valo #define WILDCARD_SSID_TLV_MAX_SIZE  \
33277b024eSKalle Valo 	(MWIFIEX_MAX_SSID_LIST_LENGTH *					\
34277b024eSKalle Valo 		(sizeof(struct mwifiex_ie_types_wildcard_ssid_params)	\
35277b024eSKalle Valo 			+ IEEE80211_MAX_SSID_LEN))
36277b024eSKalle Valo 
37277b024eSKalle Valo /* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */
38277b024eSKalle Valo #define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config)        \
39277b024eSKalle Valo 				+ sizeof(struct mwifiex_ie_types_num_probes)   \
40277b024eSKalle Valo 				+ sizeof(struct mwifiex_ie_types_htcap)       \
41277b024eSKalle Valo 				+ CHAN_TLV_MAX_SIZE                 \
42277b024eSKalle Valo 				+ RATE_TLV_MAX_SIZE                 \
43277b024eSKalle Valo 				+ WILDCARD_SSID_TLV_MAX_SIZE)
44277b024eSKalle Valo 
45277b024eSKalle Valo 
46277b024eSKalle Valo union mwifiex_scan_cmd_config_tlv {
47277b024eSKalle Valo 	/* Scan configuration (variable length) */
48277b024eSKalle Valo 	struct mwifiex_scan_cmd_config config;
49277b024eSKalle Valo 	/* Max allocated block */
50277b024eSKalle Valo 	u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC];
51277b024eSKalle Valo };
52277b024eSKalle Valo 
53277b024eSKalle Valo enum cipher_suite {
54277b024eSKalle Valo 	CIPHER_SUITE_TKIP,
55277b024eSKalle Valo 	CIPHER_SUITE_CCMP,
56277b024eSKalle Valo 	CIPHER_SUITE_MAX
57277b024eSKalle Valo };
58277b024eSKalle Valo static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = {
59277b024eSKalle Valo 	{ 0x00, 0x50, 0xf2, 0x02 },	/* TKIP */
60277b024eSKalle Valo 	{ 0x00, 0x50, 0xf2, 0x04 },	/* AES  */
61277b024eSKalle Valo };
62277b024eSKalle Valo static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = {
63277b024eSKalle Valo 	{ 0x00, 0x0f, 0xac, 0x02 },	/* TKIP */
64277b024eSKalle Valo 	{ 0x00, 0x0f, 0xac, 0x04 },	/* AES  */
65277b024eSKalle Valo };
66277b024eSKalle Valo 
67679b687bSAndreas Fenkart static void
_dbg_security_flags(int log_level,const char * func,const char * desc,struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)68679b687bSAndreas Fenkart _dbg_security_flags(int log_level, const char *func, const char *desc,
69679b687bSAndreas Fenkart 		    struct mwifiex_private *priv,
70679b687bSAndreas Fenkart 		    struct mwifiex_bssdescriptor *bss_desc)
71679b687bSAndreas Fenkart {
72679b687bSAndreas Fenkart 	_mwifiex_dbg(priv->adapter, log_level,
73679b687bSAndreas Fenkart 		     "info: %s: %s:\twpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\tEncMode=%#x privacy=%#x\n",
74679b687bSAndreas Fenkart 		     func, desc,
75679b687bSAndreas Fenkart 		     bss_desc->bcn_wpa_ie ?
76679b687bSAndreas Fenkart 		     bss_desc->bcn_wpa_ie->vend_hdr.element_id : 0,
77679b687bSAndreas Fenkart 		     bss_desc->bcn_rsn_ie ?
78679b687bSAndreas Fenkart 		     bss_desc->bcn_rsn_ie->ieee_hdr.element_id : 0,
79679b687bSAndreas Fenkart 		     priv->sec_info.wep_enabled ? "e" : "d",
80679b687bSAndreas Fenkart 		     priv->sec_info.wpa_enabled ? "e" : "d",
81679b687bSAndreas Fenkart 		     priv->sec_info.wpa2_enabled ? "e" : "d",
82679b687bSAndreas Fenkart 		     priv->sec_info.encryption_mode,
83679b687bSAndreas Fenkart 		     bss_desc->privacy);
84679b687bSAndreas Fenkart }
85679b687bSAndreas Fenkart #define dbg_security_flags(mask, desc, priv, bss_desc) \
86679b687bSAndreas Fenkart 	_dbg_security_flags(MWIFIEX_DBG_##mask, desc, __func__, priv, bss_desc)
87679b687bSAndreas Fenkart 
8838329568SAndreas Fenkart static bool
has_ieee_hdr(struct ieee_types_generic * ie,u8 key)8938329568SAndreas Fenkart has_ieee_hdr(struct ieee_types_generic *ie, u8 key)
9038329568SAndreas Fenkart {
9138329568SAndreas Fenkart 	return (ie && ie->ieee_hdr.element_id == key);
9238329568SAndreas Fenkart }
9338329568SAndreas Fenkart 
9438329568SAndreas Fenkart static bool
has_vendor_hdr(struct ieee_types_vendor_specific * ie,u8 key)9538329568SAndreas Fenkart has_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key)
9638329568SAndreas Fenkart {
9738329568SAndreas Fenkart 	return (ie && ie->vend_hdr.element_id == key);
9838329568SAndreas Fenkart }
9938329568SAndreas Fenkart 
100277b024eSKalle Valo /*
101277b024eSKalle Valo  * This function parses a given IE for a given OUI.
102277b024eSKalle Valo  *
103277b024eSKalle Valo  * This is used to parse a WPA/RSN IE to find if it has
104277b024eSKalle Valo  * a given oui in PTK.
105277b024eSKalle Valo  */
106277b024eSKalle Valo static u8
mwifiex_search_oui_in_ie(struct ie_body * iebody,u8 * oui)107277b024eSKalle Valo mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
108277b024eSKalle Valo {
109277b024eSKalle Valo 	u8 count;
110277b024eSKalle Valo 
111277b024eSKalle Valo 	count = iebody->ptk_cnt[0];
112277b024eSKalle Valo 
113277b024eSKalle Valo 	/* There could be multiple OUIs for PTK hence
114277b024eSKalle Valo 	   1) Take the length.
115277b024eSKalle Valo 	   2) Check all the OUIs for AES.
116277b024eSKalle Valo 	   3) If one of them is AES then pass success. */
117277b024eSKalle Valo 	while (count) {
118277b024eSKalle Valo 		if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body)))
119277b024eSKalle Valo 			return MWIFIEX_OUI_PRESENT;
120277b024eSKalle Valo 
121277b024eSKalle Valo 		--count;
122277b024eSKalle Valo 		if (count)
123277b024eSKalle Valo 			iebody = (struct ie_body *) ((u8 *) iebody +
124277b024eSKalle Valo 						sizeof(iebody->ptk_body));
125277b024eSKalle Valo 	}
126277b024eSKalle Valo 
127277b024eSKalle Valo 	pr_debug("info: %s: OUI is not found in PTK\n", __func__);
128277b024eSKalle Valo 	return MWIFIEX_OUI_NOT_PRESENT;
129277b024eSKalle Valo }
130277b024eSKalle Valo 
131277b024eSKalle Valo /*
132277b024eSKalle Valo  * This function checks if a given OUI is present in a RSN IE.
133277b024eSKalle Valo  *
134277b024eSKalle Valo  * The function first checks if a RSN IE is present or not in the
135277b024eSKalle Valo  * BSS descriptor. It tries to locate the OUI only if such an IE is
136277b024eSKalle Valo  * present.
137277b024eSKalle Valo  */
138277b024eSKalle Valo static u8
mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor * bss_desc,u32 cipher)139277b024eSKalle Valo mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
140277b024eSKalle Valo {
141277b024eSKalle Valo 	u8 *oui;
142277b024eSKalle Valo 	struct ie_body *iebody;
143277b024eSKalle Valo 	u8 ret = MWIFIEX_OUI_NOT_PRESENT;
144277b024eSKalle Valo 
14538329568SAndreas Fenkart 	if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
146277b024eSKalle Valo 		iebody = (struct ie_body *)
147277b024eSKalle Valo 			 (((u8 *) bss_desc->bcn_rsn_ie->data) +
148277b024eSKalle Valo 			  RSN_GTK_OUI_OFFSET);
149277b024eSKalle Valo 		oui = &mwifiex_rsn_oui[cipher][0];
150277b024eSKalle Valo 		ret = mwifiex_search_oui_in_ie(iebody, oui);
151277b024eSKalle Valo 		if (ret)
152277b024eSKalle Valo 			return ret;
153277b024eSKalle Valo 	}
154277b024eSKalle Valo 	return ret;
155277b024eSKalle Valo }
156277b024eSKalle Valo 
157277b024eSKalle Valo /*
158277b024eSKalle Valo  * This function checks if a given OUI is present in a WPA IE.
159277b024eSKalle Valo  *
160277b024eSKalle Valo  * The function first checks if a WPA IE is present or not in the
161277b024eSKalle Valo  * BSS descriptor. It tries to locate the OUI only if such an IE is
162277b024eSKalle Valo  * present.
163277b024eSKalle Valo  */
164277b024eSKalle Valo static u8
mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor * bss_desc,u32 cipher)165277b024eSKalle Valo mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
166277b024eSKalle Valo {
167277b024eSKalle Valo 	u8 *oui;
168277b024eSKalle Valo 	struct ie_body *iebody;
169277b024eSKalle Valo 	u8 ret = MWIFIEX_OUI_NOT_PRESENT;
170277b024eSKalle Valo 
17138329568SAndreas Fenkart 	if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) {
172df612421SBrian Norris 		iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
173df612421SBrian Norris 					    WPA_GTK_OUI_OFFSET);
174277b024eSKalle Valo 		oui = &mwifiex_wpa_oui[cipher][0];
175277b024eSKalle Valo 		ret = mwifiex_search_oui_in_ie(iebody, oui);
176277b024eSKalle Valo 		if (ret)
177277b024eSKalle Valo 			return ret;
178277b024eSKalle Valo 	}
179277b024eSKalle Valo 	return ret;
180277b024eSKalle Valo }
181277b024eSKalle Valo 
182277b024eSKalle Valo /*
183277b024eSKalle Valo  * This function compares two SSIDs and checks if they match.
184277b024eSKalle Valo  */
185277b024eSKalle Valo s32
mwifiex_ssid_cmp(struct cfg80211_ssid * ssid1,struct cfg80211_ssid * ssid2)186277b024eSKalle Valo mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2)
187277b024eSKalle Valo {
188277b024eSKalle Valo 	if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len))
189277b024eSKalle Valo 		return -1;
190277b024eSKalle Valo 	return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len);
191277b024eSKalle Valo }
192277b024eSKalle Valo 
193277b024eSKalle Valo /*
194277b024eSKalle Valo  * This function checks if wapi is enabled in driver and scanned network is
195277b024eSKalle Valo  * compatible with it.
196277b024eSKalle Valo  */
197277b024eSKalle Valo static bool
mwifiex_is_bss_wapi(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)198277b024eSKalle Valo mwifiex_is_bss_wapi(struct mwifiex_private *priv,
199277b024eSKalle Valo 		    struct mwifiex_bssdescriptor *bss_desc)
200277b024eSKalle Valo {
201277b024eSKalle Valo 	if (priv->sec_info.wapi_enabled &&
20238329568SAndreas Fenkart 	    has_ieee_hdr(bss_desc->bcn_wapi_ie, WLAN_EID_BSS_AC_ACCESS_DELAY))
203277b024eSKalle Valo 		return true;
204277b024eSKalle Valo 	return false;
205277b024eSKalle Valo }
206277b024eSKalle Valo 
207277b024eSKalle Valo /*
208277b024eSKalle Valo  * This function checks if driver is configured with no security mode and
209277b024eSKalle Valo  * scanned network is compatible with it.
210277b024eSKalle Valo  */
211277b024eSKalle Valo static bool
mwifiex_is_bss_no_sec(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)212277b024eSKalle Valo mwifiex_is_bss_no_sec(struct mwifiex_private *priv,
213277b024eSKalle Valo 		      struct mwifiex_bssdescriptor *bss_desc)
214277b024eSKalle Valo {
215277b024eSKalle Valo 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
2160a233c98SAndreas Fenkart 	    !priv->sec_info.wpa2_enabled &&
21738329568SAndreas Fenkart 	    !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
21838329568SAndreas Fenkart 	    !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
219277b024eSKalle Valo 	    !priv->sec_info.encryption_mode && !bss_desc->privacy) {
220277b024eSKalle Valo 		return true;
221277b024eSKalle Valo 	}
222277b024eSKalle Valo 	return false;
223277b024eSKalle Valo }
224277b024eSKalle Valo 
225277b024eSKalle Valo /*
226277b024eSKalle Valo  * This function checks if static WEP is enabled in driver and scanned network
227277b024eSKalle Valo  * is compatible with it.
228277b024eSKalle Valo  */
229277b024eSKalle Valo static bool
mwifiex_is_bss_static_wep(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)230277b024eSKalle Valo mwifiex_is_bss_static_wep(struct mwifiex_private *priv,
231277b024eSKalle Valo 			  struct mwifiex_bssdescriptor *bss_desc)
232277b024eSKalle Valo {
233277b024eSKalle Valo 	if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
234277b024eSKalle Valo 	    !priv->sec_info.wpa2_enabled && bss_desc->privacy) {
235277b024eSKalle Valo 		return true;
236277b024eSKalle Valo 	}
237277b024eSKalle Valo 	return false;
238277b024eSKalle Valo }
239277b024eSKalle Valo 
240277b024eSKalle Valo /*
241277b024eSKalle Valo  * This function checks if wpa is enabled in driver and scanned network is
242277b024eSKalle Valo  * compatible with it.
243277b024eSKalle Valo  */
244277b024eSKalle Valo static bool
mwifiex_is_bss_wpa(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)245277b024eSKalle Valo mwifiex_is_bss_wpa(struct mwifiex_private *priv,
246277b024eSKalle Valo 		   struct mwifiex_bssdescriptor *bss_desc)
247277b024eSKalle Valo {
248277b024eSKalle Valo 	if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled &&
2490a233c98SAndreas Fenkart 	    !priv->sec_info.wpa2_enabled &&
25038329568SAndreas Fenkart 	    has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)
251277b024eSKalle Valo 	   /*
252277b024eSKalle Valo 	    * Privacy bit may NOT be set in some APs like
253277b024eSKalle Valo 	    * LinkSys WRT54G && bss_desc->privacy
254277b024eSKalle Valo 	    */
255277b024eSKalle Valo 	 ) {
256679b687bSAndreas Fenkart 		dbg_security_flags(INFO, "WPA", priv, bss_desc);
257277b024eSKalle Valo 		return true;
258277b024eSKalle Valo 	}
259277b024eSKalle Valo 	return false;
260277b024eSKalle Valo }
261277b024eSKalle Valo 
262277b024eSKalle Valo /*
263277b024eSKalle Valo  * This function checks if wpa2 is enabled in driver and scanned network is
264277b024eSKalle Valo  * compatible with it.
265277b024eSKalle Valo  */
266277b024eSKalle Valo static bool
mwifiex_is_bss_wpa2(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)267277b024eSKalle Valo mwifiex_is_bss_wpa2(struct mwifiex_private *priv,
268277b024eSKalle Valo 		    struct mwifiex_bssdescriptor *bss_desc)
269277b024eSKalle Valo {
2700a233c98SAndreas Fenkart 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
271277b024eSKalle Valo 	    priv->sec_info.wpa2_enabled &&
27238329568SAndreas Fenkart 	    has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
273277b024eSKalle Valo 		/*
274277b024eSKalle Valo 		 * Privacy bit may NOT be set in some APs like
275277b024eSKalle Valo 		 * LinkSys WRT54G && bss_desc->privacy
276277b024eSKalle Valo 		 */
277679b687bSAndreas Fenkart 		dbg_security_flags(INFO, "WAP2", priv, bss_desc);
278277b024eSKalle Valo 		return true;
279277b024eSKalle Valo 	}
280277b024eSKalle Valo 	return false;
281277b024eSKalle Valo }
282277b024eSKalle Valo 
283277b024eSKalle Valo /*
284277b024eSKalle Valo  * This function checks if adhoc AES is enabled in driver and scanned network is
285277b024eSKalle Valo  * compatible with it.
286277b024eSKalle Valo  */
287277b024eSKalle Valo static bool
mwifiex_is_bss_adhoc_aes(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)288277b024eSKalle Valo mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv,
289277b024eSKalle Valo 			 struct mwifiex_bssdescriptor *bss_desc)
290277b024eSKalle Valo {
291277b024eSKalle Valo 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
292277b024eSKalle Valo 	    !priv->sec_info.wpa2_enabled &&
29338329568SAndreas Fenkart 	    !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
29438329568SAndreas Fenkart 	    !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
295277b024eSKalle Valo 	    !priv->sec_info.encryption_mode && bss_desc->privacy) {
296277b024eSKalle Valo 		return true;
297277b024eSKalle Valo 	}
298277b024eSKalle Valo 	return false;
299277b024eSKalle Valo }
300277b024eSKalle Valo 
301277b024eSKalle Valo /*
302277b024eSKalle Valo  * This function checks if dynamic WEP is enabled in driver and scanned network
303277b024eSKalle Valo  * is compatible with it.
304277b024eSKalle Valo  */
305277b024eSKalle Valo static bool
mwifiex_is_bss_dynamic_wep(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)306277b024eSKalle Valo mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv,
307277b024eSKalle Valo 			   struct mwifiex_bssdescriptor *bss_desc)
308277b024eSKalle Valo {
309277b024eSKalle Valo 	if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
310277b024eSKalle Valo 	    !priv->sec_info.wpa2_enabled &&
31138329568SAndreas Fenkart 	    !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
31238329568SAndreas Fenkart 	    !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
313277b024eSKalle Valo 	    priv->sec_info.encryption_mode && bss_desc->privacy) {
314679b687bSAndreas Fenkart 		dbg_security_flags(INFO, "dynamic", priv, bss_desc);
315277b024eSKalle Valo 		return true;
316277b024eSKalle Valo 	}
317277b024eSKalle Valo 	return false;
318277b024eSKalle Valo }
319277b024eSKalle Valo 
320277b024eSKalle Valo /*
321277b024eSKalle Valo  * This function checks if a scanned network is compatible with the driver
322277b024eSKalle Valo  * settings.
323277b024eSKalle Valo  *
324277b024eSKalle Valo  *   WEP     WPA    WPA2   ad-hoc encrypt                  Network
325277b024eSKalle Valo  * enabled enabled enabled  AES    mode   Privacy WPA WPA2 Compatible
326277b024eSKalle Valo  *    0       0       0      0     NONE      0     0   0   yes No security
327277b024eSKalle Valo  *    0       1       0      0      x        1x    1   x   yes WPA (disable
328277b024eSKalle Valo  *                                                         HT if no AES)
329277b024eSKalle Valo  *    0       0       1      0      x        1x    x   1   yes WPA2 (disable
330277b024eSKalle Valo  *                                                         HT if no AES)
331277b024eSKalle Valo  *    0       0       0      1     NONE      1     0   0   yes Ad-hoc AES
332277b024eSKalle Valo  *    1       0       0      0     NONE      1     0   0   yes Static WEP
333277b024eSKalle Valo  *                                                         (disable HT)
334277b024eSKalle Valo  *    0       0       0      0    !=NONE     1     0   0   yes Dynamic WEP
335277b024eSKalle Valo  *
336277b024eSKalle Valo  * Compatibility is not matched while roaming, except for mode.
337277b024eSKalle Valo  */
338277b024eSKalle Valo static s32
mwifiex_is_network_compatible(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc,u32 mode)339277b024eSKalle Valo mwifiex_is_network_compatible(struct mwifiex_private *priv,
340277b024eSKalle Valo 			      struct mwifiex_bssdescriptor *bss_desc, u32 mode)
341277b024eSKalle Valo {
342277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
343277b024eSKalle Valo 
344277b024eSKalle Valo 	bss_desc->disable_11n = false;
345277b024eSKalle Valo 
346277b024eSKalle Valo 	/* Don't check for compatibility if roaming */
347277b024eSKalle Valo 	if (priv->media_connected &&
348277b024eSKalle Valo 	    (priv->bss_mode == NL80211_IFTYPE_STATION) &&
349277b024eSKalle Valo 	    (bss_desc->bss_mode == NL80211_IFTYPE_STATION))
350277b024eSKalle Valo 		return 0;
351277b024eSKalle Valo 
352277b024eSKalle Valo 	if (priv->wps.session_enable) {
353277b024eSKalle Valo 		mwifiex_dbg(adapter, IOCTL,
354277b024eSKalle Valo 			    "info: return success directly in WPS period\n");
355277b024eSKalle Valo 		return 0;
356277b024eSKalle Valo 	}
357277b024eSKalle Valo 
358277b024eSKalle Valo 	if (bss_desc->chan_sw_ie_present) {
359277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
360277b024eSKalle Valo 			    "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
361277b024eSKalle Valo 		return -1;
362277b024eSKalle Valo 	}
363277b024eSKalle Valo 
364277b024eSKalle Valo 	if (mwifiex_is_bss_wapi(priv, bss_desc)) {
365277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
366277b024eSKalle Valo 			    "info: return success for WAPI AP\n");
367277b024eSKalle Valo 		return 0;
368277b024eSKalle Valo 	}
369277b024eSKalle Valo 
370277b024eSKalle Valo 	if (bss_desc->bss_mode == mode) {
371277b024eSKalle Valo 		if (mwifiex_is_bss_no_sec(priv, bss_desc)) {
372277b024eSKalle Valo 			/* No security */
373277b024eSKalle Valo 			return 0;
374277b024eSKalle Valo 		} else if (mwifiex_is_bss_static_wep(priv, bss_desc)) {
375277b024eSKalle Valo 			/* Static WEP enabled */
376277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
377277b024eSKalle Valo 				    "info: Disable 11n in WEP mode.\n");
378277b024eSKalle Valo 			bss_desc->disable_11n = true;
379277b024eSKalle Valo 			return 0;
380277b024eSKalle Valo 		} else if (mwifiex_is_bss_wpa(priv, bss_desc)) {
381277b024eSKalle Valo 			/* WPA enabled */
382277b024eSKalle Valo 			if (((priv->adapter->config_bands & BAND_GN ||
383277b024eSKalle Valo 			      priv->adapter->config_bands & BAND_AN) &&
384277b024eSKalle Valo 			     bss_desc->bcn_ht_cap) &&
385277b024eSKalle Valo 			    !mwifiex_is_wpa_oui_present(bss_desc,
386277b024eSKalle Valo 							 CIPHER_SUITE_CCMP)) {
387277b024eSKalle Valo 
388277b024eSKalle Valo 				if (mwifiex_is_wpa_oui_present
389277b024eSKalle Valo 						(bss_desc, CIPHER_SUITE_TKIP)) {
390277b024eSKalle Valo 					mwifiex_dbg(adapter, INFO,
391277b024eSKalle Valo 						    "info: Disable 11n if AES\t"
392277b024eSKalle Valo 						    "is not supported by AP\n");
393277b024eSKalle Valo 					bss_desc->disable_11n = true;
394277b024eSKalle Valo 				} else {
395277b024eSKalle Valo 					return -1;
396277b024eSKalle Valo 				}
397277b024eSKalle Valo 			}
398277b024eSKalle Valo 			return 0;
399277b024eSKalle Valo 		} else if (mwifiex_is_bss_wpa2(priv, bss_desc)) {
400277b024eSKalle Valo 			/* WPA2 enabled */
401277b024eSKalle Valo 			if (((priv->adapter->config_bands & BAND_GN ||
402277b024eSKalle Valo 			      priv->adapter->config_bands & BAND_AN) &&
403277b024eSKalle Valo 			     bss_desc->bcn_ht_cap) &&
404277b024eSKalle Valo 			    !mwifiex_is_rsn_oui_present(bss_desc,
405277b024eSKalle Valo 							CIPHER_SUITE_CCMP)) {
406277b024eSKalle Valo 
407277b024eSKalle Valo 				if (mwifiex_is_rsn_oui_present
408277b024eSKalle Valo 						(bss_desc, CIPHER_SUITE_TKIP)) {
409277b024eSKalle Valo 					mwifiex_dbg(adapter, INFO,
410277b024eSKalle Valo 						    "info: Disable 11n if AES\t"
411277b024eSKalle Valo 						    "is not supported by AP\n");
412277b024eSKalle Valo 					bss_desc->disable_11n = true;
413277b024eSKalle Valo 				} else {
414277b024eSKalle Valo 					return -1;
415277b024eSKalle Valo 				}
416277b024eSKalle Valo 			}
417277b024eSKalle Valo 			return 0;
418277b024eSKalle Valo 		} else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) {
419277b024eSKalle Valo 			/* Ad-hoc AES enabled */
420277b024eSKalle Valo 			return 0;
421277b024eSKalle Valo 		} else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) {
422277b024eSKalle Valo 			/* Dynamic WEP enabled */
423277b024eSKalle Valo 			return 0;
424277b024eSKalle Valo 		}
425277b024eSKalle Valo 
426277b024eSKalle Valo 		/* Security doesn't match */
427679b687bSAndreas Fenkart 		dbg_security_flags(ERROR, "failed", priv, bss_desc);
428277b024eSKalle Valo 		return -1;
429277b024eSKalle Valo 	}
430277b024eSKalle Valo 
431277b024eSKalle Valo 	/* Mode doesn't match */
432277b024eSKalle Valo 	return -1;
433277b024eSKalle Valo }
434277b024eSKalle Valo 
435277b024eSKalle Valo /*
436277b024eSKalle Valo  * This function creates a channel list for the driver to scan, based
437277b024eSKalle Valo  * on region/band information.
438277b024eSKalle Valo  *
439277b024eSKalle Valo  * This routine is used for any scan that is not provided with a
440277b024eSKalle Valo  * specific channel list to scan.
441277b024eSKalle Valo  */
442277b024eSKalle Valo static int
mwifiex_scan_create_channel_list(struct mwifiex_private * priv,const struct mwifiex_user_scan_cfg * user_scan_in,struct mwifiex_chan_scan_param_set * scan_chan_list,u8 filtered_scan)443277b024eSKalle Valo mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
444277b024eSKalle Valo 				 const struct mwifiex_user_scan_cfg
445277b024eSKalle Valo 							*user_scan_in,
446277b024eSKalle Valo 				 struct mwifiex_chan_scan_param_set
447277b024eSKalle Valo 							*scan_chan_list,
448277b024eSKalle Valo 				 u8 filtered_scan)
449277b024eSKalle Valo {
45057fbcce3SJohannes Berg 	enum nl80211_band band;
451277b024eSKalle Valo 	struct ieee80211_supported_band *sband;
452277b024eSKalle Valo 	struct ieee80211_channel *ch;
453277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
454277b024eSKalle Valo 	int chan_idx = 0, i;
455277b024eSKalle Valo 
45657fbcce3SJohannes Berg 	for (band = 0; (band < NUM_NL80211_BANDS) ; band++) {
457277b024eSKalle Valo 
458277b024eSKalle Valo 		if (!priv->wdev.wiphy->bands[band])
459277b024eSKalle Valo 			continue;
460277b024eSKalle Valo 
461277b024eSKalle Valo 		sband = priv->wdev.wiphy->bands[band];
462277b024eSKalle Valo 
463277b024eSKalle Valo 		for (i = 0; (i < sband->n_channels) ; i++) {
464277b024eSKalle Valo 			ch = &sband->channels[i];
465277b024eSKalle Valo 			if (ch->flags & IEEE80211_CHAN_DISABLED)
466277b024eSKalle Valo 				continue;
467277b024eSKalle Valo 			scan_chan_list[chan_idx].radio_type = band;
468277b024eSKalle Valo 
469277b024eSKalle Valo 			if (user_scan_in &&
470277b024eSKalle Valo 			    user_scan_in->chan_list[0].scan_time)
471277b024eSKalle Valo 				scan_chan_list[chan_idx].max_scan_time =
472277b024eSKalle Valo 					cpu_to_le16((u16) user_scan_in->
473277b024eSKalle Valo 					chan_list[0].scan_time);
474db69f4e0SXinming Hu 			else if ((ch->flags & IEEE80211_CHAN_NO_IR) ||
475db69f4e0SXinming Hu 				 (ch->flags & IEEE80211_CHAN_RADAR))
476277b024eSKalle Valo 				scan_chan_list[chan_idx].max_scan_time =
477277b024eSKalle Valo 					cpu_to_le16(adapter->passive_scan_time);
478277b024eSKalle Valo 			else
479277b024eSKalle Valo 				scan_chan_list[chan_idx].max_scan_time =
480277b024eSKalle Valo 					cpu_to_le16(adapter->active_scan_time);
481277b024eSKalle Valo 
482277b024eSKalle Valo 			if (ch->flags & IEEE80211_CHAN_NO_IR)
483277b024eSKalle Valo 				scan_chan_list[chan_idx].chan_scan_mode_bitmap
484277b024eSKalle Valo 					|= (MWIFIEX_PASSIVE_SCAN |
485277b024eSKalle Valo 					    MWIFIEX_HIDDEN_SSID_REPORT);
486277b024eSKalle Valo 			else
487277b024eSKalle Valo 				scan_chan_list[chan_idx].chan_scan_mode_bitmap
488277b024eSKalle Valo 					&= ~MWIFIEX_PASSIVE_SCAN;
489277b024eSKalle Valo 			scan_chan_list[chan_idx].chan_number =
490277b024eSKalle Valo 							(u32) ch->hw_value;
4911b499cb7SAmitkumar Karwar 
4921b499cb7SAmitkumar Karwar 			scan_chan_list[chan_idx].chan_scan_mode_bitmap
4931b499cb7SAmitkumar Karwar 					|= MWIFIEX_DISABLE_CHAN_FILT;
4941b499cb7SAmitkumar Karwar 
495db69f4e0SXinming Hu 			if (filtered_scan &&
496db69f4e0SXinming Hu 			    !((ch->flags & IEEE80211_CHAN_NO_IR) ||
497db69f4e0SXinming Hu 			      (ch->flags & IEEE80211_CHAN_RADAR)))
498277b024eSKalle Valo 				scan_chan_list[chan_idx].max_scan_time =
499277b024eSKalle Valo 				cpu_to_le16(adapter->specific_scan_time);
500db69f4e0SXinming Hu 
501277b024eSKalle Valo 			chan_idx++;
502277b024eSKalle Valo 		}
503277b024eSKalle Valo 
504277b024eSKalle Valo 	}
505277b024eSKalle Valo 	return chan_idx;
506277b024eSKalle Valo }
507277b024eSKalle Valo 
5080c9b7f22SXinming Hu /* This function creates a channel list tlv for bgscan config, based
5090c9b7f22SXinming Hu  * on region/band information.
5100c9b7f22SXinming Hu  */
5110c9b7f22SXinming Hu static int
mwifiex_bgscan_create_channel_list(struct mwifiex_private * priv,const struct mwifiex_bg_scan_cfg * bgscan_cfg_in,struct mwifiex_chan_scan_param_set * scan_chan_list)5120c9b7f22SXinming Hu mwifiex_bgscan_create_channel_list(struct mwifiex_private *priv,
5130c9b7f22SXinming Hu 				   const struct mwifiex_bg_scan_cfg
5140c9b7f22SXinming Hu 						*bgscan_cfg_in,
5150c9b7f22SXinming Hu 				   struct mwifiex_chan_scan_param_set
5160c9b7f22SXinming Hu 						*scan_chan_list)
5170c9b7f22SXinming Hu {
51857fbcce3SJohannes Berg 	enum nl80211_band band;
5190c9b7f22SXinming Hu 	struct ieee80211_supported_band *sband;
5200c9b7f22SXinming Hu 	struct ieee80211_channel *ch;
5210c9b7f22SXinming Hu 	struct mwifiex_adapter *adapter = priv->adapter;
5220c9b7f22SXinming Hu 	int chan_idx = 0, i;
5230c9b7f22SXinming Hu 
52457fbcce3SJohannes Berg 	for (band = 0; (band < NUM_NL80211_BANDS); band++) {
5250c9b7f22SXinming Hu 		if (!priv->wdev.wiphy->bands[band])
5260c9b7f22SXinming Hu 			continue;
5270c9b7f22SXinming Hu 
5280c9b7f22SXinming Hu 		sband = priv->wdev.wiphy->bands[band];
5290c9b7f22SXinming Hu 
5300c9b7f22SXinming Hu 		for (i = 0; (i < sband->n_channels) ; i++) {
5310c9b7f22SXinming Hu 			ch = &sband->channels[i];
5320c9b7f22SXinming Hu 			if (ch->flags & IEEE80211_CHAN_DISABLED)
5330c9b7f22SXinming Hu 				continue;
5340c9b7f22SXinming Hu 			scan_chan_list[chan_idx].radio_type = band;
5350c9b7f22SXinming Hu 
5360c9b7f22SXinming Hu 			if (bgscan_cfg_in->chan_list[0].scan_time)
5370c9b7f22SXinming Hu 				scan_chan_list[chan_idx].max_scan_time =
5380c9b7f22SXinming Hu 					cpu_to_le16((u16)bgscan_cfg_in->
5390c9b7f22SXinming Hu 					chan_list[0].scan_time);
5400c9b7f22SXinming Hu 			else if (ch->flags & IEEE80211_CHAN_NO_IR)
5410c9b7f22SXinming Hu 				scan_chan_list[chan_idx].max_scan_time =
5420c9b7f22SXinming Hu 					cpu_to_le16(adapter->passive_scan_time);
5430c9b7f22SXinming Hu 			else
5440c9b7f22SXinming Hu 				scan_chan_list[chan_idx].max_scan_time =
5450c9b7f22SXinming Hu 					cpu_to_le16(adapter->
5460c9b7f22SXinming Hu 						    specific_scan_time);
5470c9b7f22SXinming Hu 
5480c9b7f22SXinming Hu 			if (ch->flags & IEEE80211_CHAN_NO_IR)
5490c9b7f22SXinming Hu 				scan_chan_list[chan_idx].chan_scan_mode_bitmap
5500c9b7f22SXinming Hu 					|= MWIFIEX_PASSIVE_SCAN;
5510c9b7f22SXinming Hu 			else
5520c9b7f22SXinming Hu 				scan_chan_list[chan_idx].chan_scan_mode_bitmap
5530c9b7f22SXinming Hu 					&= ~MWIFIEX_PASSIVE_SCAN;
5540c9b7f22SXinming Hu 
5550c9b7f22SXinming Hu 			scan_chan_list[chan_idx].chan_number =
5560c9b7f22SXinming Hu 							(u32)ch->hw_value;
5570c9b7f22SXinming Hu 			chan_idx++;
5580c9b7f22SXinming Hu 		}
5590c9b7f22SXinming Hu 	}
5600c9b7f22SXinming Hu 	return chan_idx;
5610c9b7f22SXinming Hu }
5620c9b7f22SXinming Hu 
563277b024eSKalle Valo /* This function appends rate TLV to scan config command. */
564277b024eSKalle Valo static int
mwifiex_append_rate_tlv(struct mwifiex_private * priv,struct mwifiex_scan_cmd_config * scan_cfg_out,u8 radio)565277b024eSKalle Valo mwifiex_append_rate_tlv(struct mwifiex_private *priv,
566277b024eSKalle Valo 			struct mwifiex_scan_cmd_config *scan_cfg_out,
567277b024eSKalle Valo 			u8 radio)
568277b024eSKalle Valo {
569277b024eSKalle Valo 	struct mwifiex_ie_types_rates_param_set *rates_tlv;
570277b024eSKalle Valo 	u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos;
571277b024eSKalle Valo 	u32 rates_size;
572277b024eSKalle Valo 
573277b024eSKalle Valo 	memset(rates, 0, sizeof(rates));
574277b024eSKalle Valo 
575277b024eSKalle Valo 	tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
576277b024eSKalle Valo 
577277b024eSKalle Valo 	if (priv->scan_request)
578277b024eSKalle Valo 		rates_size = mwifiex_get_rates_from_cfg80211(priv, rates,
579277b024eSKalle Valo 							     radio);
580277b024eSKalle Valo 	else
581277b024eSKalle Valo 		rates_size = mwifiex_get_supported_rates(priv, rates);
582277b024eSKalle Valo 
583277b024eSKalle Valo 	mwifiex_dbg(priv->adapter, CMD,
584277b024eSKalle Valo 		    "info: SCAN_CMD: Rates size = %d\n",
585277b024eSKalle Valo 		rates_size);
586277b024eSKalle Valo 	rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos;
587277b024eSKalle Valo 	rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
588277b024eSKalle Valo 	rates_tlv->header.len = cpu_to_le16((u16) rates_size);
589277b024eSKalle Valo 	memcpy(rates_tlv->rates, rates, rates_size);
590277b024eSKalle Valo 	scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
591277b024eSKalle Valo 
592277b024eSKalle Valo 	return rates_size;
593277b024eSKalle Valo }
594277b024eSKalle Valo 
595277b024eSKalle Valo /*
596277b024eSKalle Valo  * This function constructs and sends multiple scan config commands to
597277b024eSKalle Valo  * the firmware.
598277b024eSKalle Valo  *
599277b024eSKalle Valo  * Previous routines in the code flow have created a scan command configuration
600277b024eSKalle Valo  * with any requested TLVs.  This function splits the channel TLV into maximum
601277b024eSKalle Valo  * channels supported per scan lists and sends the portion of the channel TLV,
602277b024eSKalle Valo  * along with the other TLVs, to the firmware.
603277b024eSKalle Valo  */
604277b024eSKalle Valo static int
mwifiex_scan_channel_list(struct mwifiex_private * priv,u32 max_chan_per_scan,u8 filtered_scan,struct mwifiex_scan_cmd_config * scan_cfg_out,struct mwifiex_ie_types_chan_list_param_set * chan_tlv_out,struct mwifiex_chan_scan_param_set * scan_chan_list)605277b024eSKalle Valo mwifiex_scan_channel_list(struct mwifiex_private *priv,
606277b024eSKalle Valo 			  u32 max_chan_per_scan, u8 filtered_scan,
607277b024eSKalle Valo 			  struct mwifiex_scan_cmd_config *scan_cfg_out,
608277b024eSKalle Valo 			  struct mwifiex_ie_types_chan_list_param_set
609277b024eSKalle Valo 			  *chan_tlv_out,
610277b024eSKalle Valo 			  struct mwifiex_chan_scan_param_set *scan_chan_list)
611277b024eSKalle Valo {
612277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
613277b024eSKalle Valo 	int ret = 0;
614277b024eSKalle Valo 	struct mwifiex_chan_scan_param_set *tmp_chan_list;
615277b024eSKalle Valo 	u32 tlv_idx, rates_size, cmd_no;
616277b024eSKalle Valo 	u32 total_scan_time;
617277b024eSKalle Valo 	u32 done_early;
618277b024eSKalle Valo 	u8 radio_type;
619277b024eSKalle Valo 
620277b024eSKalle Valo 	if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) {
621277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
622277b024eSKalle Valo 			    "info: Scan: Null detect: %p, %p, %p\n",
623277b024eSKalle Valo 			    scan_cfg_out, chan_tlv_out, scan_chan_list);
624277b024eSKalle Valo 		return -1;
625277b024eSKalle Valo 	}
626277b024eSKalle Valo 
627277b024eSKalle Valo 	/* Check csa channel expiry before preparing scan list */
628277b024eSKalle Valo 	mwifiex_11h_get_csa_closed_channel(priv);
629277b024eSKalle Valo 
630277b024eSKalle Valo 	chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
631277b024eSKalle Valo 
632277b024eSKalle Valo 	/* Set the temp channel struct pointer to the start of the desired
633277b024eSKalle Valo 	   list */
634277b024eSKalle Valo 	tmp_chan_list = scan_chan_list;
635277b024eSKalle Valo 
636277b024eSKalle Valo 	/* Loop through the desired channel list, sending a new firmware scan
637277b024eSKalle Valo 	   commands for each max_chan_per_scan channels (or for 1,6,11
638277b024eSKalle Valo 	   individually if configured accordingly) */
639277b024eSKalle Valo 	while (tmp_chan_list->chan_number) {
640277b024eSKalle Valo 
641277b024eSKalle Valo 		tlv_idx = 0;
642277b024eSKalle Valo 		total_scan_time = 0;
643277b024eSKalle Valo 		radio_type = 0;
644277b024eSKalle Valo 		chan_tlv_out->header.len = 0;
645277b024eSKalle Valo 		done_early = false;
646277b024eSKalle Valo 
647277b024eSKalle Valo 		/*
648277b024eSKalle Valo 		 * Construct the Channel TLV for the scan command.  Continue to
649277b024eSKalle Valo 		 * insert channel TLVs until:
650277b024eSKalle Valo 		 *   - the tlv_idx hits the maximum configured per scan command
651277b024eSKalle Valo 		 *   - the next channel to insert is 0 (end of desired channel
652277b024eSKalle Valo 		 *     list)
653277b024eSKalle Valo 		 *   - done_early is set (controlling individual scanning of
654277b024eSKalle Valo 		 *     1,6,11)
655277b024eSKalle Valo 		 */
656277b024eSKalle Valo 		while (tlv_idx < max_chan_per_scan &&
657277b024eSKalle Valo 		       tmp_chan_list->chan_number && !done_early) {
658277b024eSKalle Valo 
659277b024eSKalle Valo 			if (tmp_chan_list->chan_number == priv->csa_chan) {
660277b024eSKalle Valo 				tmp_chan_list++;
661277b024eSKalle Valo 				continue;
662277b024eSKalle Valo 			}
663277b024eSKalle Valo 
664277b024eSKalle Valo 			radio_type = tmp_chan_list->radio_type;
665277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, INFO,
666277b024eSKalle Valo 				    "info: Scan: Chan(%3d), Radio(%d),\t"
667277b024eSKalle Valo 				    "Mode(%d, %d), Dur(%d)\n",
668277b024eSKalle Valo 				    tmp_chan_list->chan_number,
669277b024eSKalle Valo 				    tmp_chan_list->radio_type,
670277b024eSKalle Valo 				    tmp_chan_list->chan_scan_mode_bitmap
671277b024eSKalle Valo 				    & MWIFIEX_PASSIVE_SCAN,
672277b024eSKalle Valo 				    (tmp_chan_list->chan_scan_mode_bitmap
673277b024eSKalle Valo 				    & MWIFIEX_DISABLE_CHAN_FILT) >> 1,
674277b024eSKalle Valo 				    le16_to_cpu(tmp_chan_list->max_scan_time));
675277b024eSKalle Valo 
676277b024eSKalle Valo 			/* Copy the current channel TLV to the command being
677277b024eSKalle Valo 			   prepared */
678277b024eSKalle Valo 			memcpy(chan_tlv_out->chan_scan_param + tlv_idx,
679277b024eSKalle Valo 			       tmp_chan_list,
680277b024eSKalle Valo 			       sizeof(chan_tlv_out->chan_scan_param));
681277b024eSKalle Valo 
682277b024eSKalle Valo 			/* Increment the TLV header length by the size
683277b024eSKalle Valo 			   appended */
6845653c646SDaniel Mentz 			le16_unaligned_add_cpu(&chan_tlv_out->header.len,
6855653c646SDaniel Mentz 					       sizeof(
6865653c646SDaniel Mentz 						chan_tlv_out->chan_scan_param));
687277b024eSKalle Valo 
688277b024eSKalle Valo 			/*
689277b024eSKalle Valo 			 * The tlv buffer length is set to the number of bytes
690277b024eSKalle Valo 			 * of the between the channel tlv pointer and the start
691277b024eSKalle Valo 			 * of the tlv buffer.  This compensates for any TLVs
692277b024eSKalle Valo 			 * that were appended before the channel list.
693277b024eSKalle Valo 			 */
694277b024eSKalle Valo 			scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out -
695277b024eSKalle Valo 							scan_cfg_out->tlv_buf);
696277b024eSKalle Valo 
697277b024eSKalle Valo 			/* Add the size of the channel tlv header and the data
698277b024eSKalle Valo 			   length */
699277b024eSKalle Valo 			scan_cfg_out->tlv_buf_len +=
700277b024eSKalle Valo 				(sizeof(chan_tlv_out->header)
701277b024eSKalle Valo 				 + le16_to_cpu(chan_tlv_out->header.len));
702277b024eSKalle Valo 
703277b024eSKalle Valo 			/* Increment the index to the channel tlv we are
704277b024eSKalle Valo 			   constructing */
705277b024eSKalle Valo 			tlv_idx++;
706277b024eSKalle Valo 
707277b024eSKalle Valo 			/* Count the total scan time per command */
708277b024eSKalle Valo 			total_scan_time +=
709277b024eSKalle Valo 				le16_to_cpu(tmp_chan_list->max_scan_time);
710277b024eSKalle Valo 
711277b024eSKalle Valo 			done_early = false;
712277b024eSKalle Valo 
713277b024eSKalle Valo 			/* Stop the loop if the *current* channel is in the
714277b024eSKalle Valo 			   1,6,11 set and we are not filtering on a BSSID
715277b024eSKalle Valo 			   or SSID. */
716277b024eSKalle Valo 			if (!filtered_scan &&
717277b024eSKalle Valo 			    (tmp_chan_list->chan_number == 1 ||
718277b024eSKalle Valo 			     tmp_chan_list->chan_number == 6 ||
719277b024eSKalle Valo 			     tmp_chan_list->chan_number == 11))
720277b024eSKalle Valo 				done_early = true;
721277b024eSKalle Valo 
722277b024eSKalle Valo 			/* Increment the tmp pointer to the next channel to
723277b024eSKalle Valo 			   be scanned */
724277b024eSKalle Valo 			tmp_chan_list++;
725277b024eSKalle Valo 
726277b024eSKalle Valo 			/* Stop the loop if the *next* channel is in the 1,6,11
727277b024eSKalle Valo 			   set.  This will cause it to be the only channel
728277b024eSKalle Valo 			   scanned on the next interation */
729277b024eSKalle Valo 			if (!filtered_scan &&
730277b024eSKalle Valo 			    (tmp_chan_list->chan_number == 1 ||
731277b024eSKalle Valo 			     tmp_chan_list->chan_number == 6 ||
732277b024eSKalle Valo 			     tmp_chan_list->chan_number == 11))
733277b024eSKalle Valo 				done_early = true;
734277b024eSKalle Valo 		}
735277b024eSKalle Valo 
736277b024eSKalle Valo 		/* The total scan time should be less than scan command timeout
737277b024eSKalle Valo 		   value */
738277b024eSKalle Valo 		if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) {
739277b024eSKalle Valo 			mwifiex_dbg(priv->adapter, ERROR,
740277b024eSKalle Valo 				    "total scan time %dms\t"
741277b024eSKalle Valo 				    "is over limit (%dms), scan skipped\n",
742277b024eSKalle Valo 				    total_scan_time,
743277b024eSKalle Valo 				    MWIFIEX_MAX_TOTAL_SCAN_TIME);
744277b024eSKalle Valo 			ret = -1;
745277b024eSKalle Valo 			break;
746277b024eSKalle Valo 		}
747277b024eSKalle Valo 
748277b024eSKalle Valo 		rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out,
749277b024eSKalle Valo 						     radio_type);
750277b024eSKalle Valo 
751277b024eSKalle Valo 		/* Send the scan command to the firmware with the specified
752277b024eSKalle Valo 		   cfg */
753277b024eSKalle Valo 		if (priv->adapter->ext_scan)
754277b024eSKalle Valo 			cmd_no = HostCmd_CMD_802_11_SCAN_EXT;
755277b024eSKalle Valo 		else
756277b024eSKalle Valo 			cmd_no = HostCmd_CMD_802_11_SCAN;
757277b024eSKalle Valo 
758277b024eSKalle Valo 		ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET,
759277b024eSKalle Valo 				       0, scan_cfg_out, false);
760277b024eSKalle Valo 
761277b024eSKalle Valo 		/* rate IE is updated per scan command but same starting
762277b024eSKalle Valo 		 * pointer is used each time so that rate IE from earlier
763277b024eSKalle Valo 		 * scan_cfg_out->buf is overwritten with new one.
764277b024eSKalle Valo 		 */
765277b024eSKalle Valo 		scan_cfg_out->tlv_buf_len -=
766277b024eSKalle Valo 			    sizeof(struct mwifiex_ie_types_header) + rates_size;
767277b024eSKalle Valo 
768277b024eSKalle Valo 		if (ret) {
769c70ca8cbSAndreas Fenkart 			mwifiex_cancel_pending_scan_cmd(adapter);
770277b024eSKalle Valo 			break;
771277b024eSKalle Valo 		}
772277b024eSKalle Valo 	}
773277b024eSKalle Valo 
774277b024eSKalle Valo 	if (ret)
775277b024eSKalle Valo 		return -1;
776277b024eSKalle Valo 
777277b024eSKalle Valo 	return 0;
778277b024eSKalle Valo }
779277b024eSKalle Valo 
780277b024eSKalle Valo /*
781277b024eSKalle Valo  * This function constructs a scan command configuration structure to use
782277b024eSKalle Valo  * in scan commands.
783277b024eSKalle Valo  *
784277b024eSKalle Valo  * Application layer or other functions can invoke network scanning
785277b024eSKalle Valo  * with a scan configuration supplied in a user scan configuration structure.
786277b024eSKalle Valo  * This structure is used as the basis of one or many scan command configuration
787277b024eSKalle Valo  * commands that are sent to the command processing module and eventually to the
788277b024eSKalle Valo  * firmware.
789277b024eSKalle Valo  *
790277b024eSKalle Valo  * This function creates a scan command configuration structure  based on the
791277b024eSKalle Valo  * following user supplied parameters (if present):
792277b024eSKalle Valo  *      - SSID filter
793277b024eSKalle Valo  *      - BSSID filter
794277b024eSKalle Valo  *      - Number of Probes to be sent
795277b024eSKalle Valo  *      - Channel list
796277b024eSKalle Valo  *
797277b024eSKalle Valo  * If the SSID or BSSID filter is not present, the filter is disabled/cleared.
798277b024eSKalle Valo  * If the number of probes is not set, adapter default setting is used.
799277b024eSKalle Valo  */
800277b024eSKalle Valo static void
mwifiex_config_scan(struct mwifiex_private * priv,const struct mwifiex_user_scan_cfg * user_scan_in,struct mwifiex_scan_cmd_config * scan_cfg_out,struct mwifiex_ie_types_chan_list_param_set ** chan_list_out,struct mwifiex_chan_scan_param_set * scan_chan_list,u8 * max_chan_per_scan,u8 * filtered_scan,u8 * scan_current_only)801277b024eSKalle Valo mwifiex_config_scan(struct mwifiex_private *priv,
802277b024eSKalle Valo 		    const struct mwifiex_user_scan_cfg *user_scan_in,
803277b024eSKalle Valo 		    struct mwifiex_scan_cmd_config *scan_cfg_out,
804277b024eSKalle Valo 		    struct mwifiex_ie_types_chan_list_param_set **chan_list_out,
805277b024eSKalle Valo 		    struct mwifiex_chan_scan_param_set *scan_chan_list,
806277b024eSKalle Valo 		    u8 *max_chan_per_scan, u8 *filtered_scan,
807277b024eSKalle Valo 		    u8 *scan_current_only)
808277b024eSKalle Valo {
809277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
810277b024eSKalle Valo 	struct mwifiex_ie_types_num_probes *num_probes_tlv;
811277b024eSKalle Valo 	struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv;
812c2a8f0ffSGanapathi Bhat 	struct mwifiex_ie_types_random_mac *random_mac_tlv;
813277b024eSKalle Valo 	struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
814277b024eSKalle Valo 	struct mwifiex_ie_types_bssid_list *bssid_tlv;
815277b024eSKalle Valo 	u8 *tlv_pos;
816277b024eSKalle Valo 	u32 num_probes;
817277b024eSKalle Valo 	u32 ssid_len;
818277b024eSKalle Valo 	u32 chan_idx;
819277b024eSKalle Valo 	u32 scan_type;
820277b024eSKalle Valo 	u16 scan_dur;
821277b024eSKalle Valo 	u8 channel;
822277b024eSKalle Valo 	u8 radio_type;
823277b024eSKalle Valo 	int i;
824277b024eSKalle Valo 	u8 ssid_filter;
825277b024eSKalle Valo 	struct mwifiex_ie_types_htcap *ht_cap;
826277b024eSKalle Valo 	struct mwifiex_ie_types_bss_mode *bss_mode;
827277b024eSKalle Valo 
828277b024eSKalle Valo 	/* The tlv_buf_len is calculated for each scan command.  The TLVs added
829277b024eSKalle Valo 	   in this routine will be preserved since the routine that sends the
830277b024eSKalle Valo 	   command will append channelTLVs at *chan_list_out.  The difference
831277b024eSKalle Valo 	   between the *chan_list_out and the tlv_buf start will be used to
832277b024eSKalle Valo 	   calculate the size of anything we add in this routine. */
833277b024eSKalle Valo 	scan_cfg_out->tlv_buf_len = 0;
834277b024eSKalle Valo 
835277b024eSKalle Valo 	/* Running tlv pointer.  Assigned to chan_list_out at end of function
836277b024eSKalle Valo 	   so later routines know where channels can be added to the command
837277b024eSKalle Valo 	   buf */
838277b024eSKalle Valo 	tlv_pos = scan_cfg_out->tlv_buf;
839277b024eSKalle Valo 
840277b024eSKalle Valo 	/* Initialize the scan as un-filtered; the flag is later set to TRUE
841277b024eSKalle Valo 	   below if a SSID or BSSID filter is sent in the command */
842277b024eSKalle Valo 	*filtered_scan = false;
843277b024eSKalle Valo 
844277b024eSKalle Valo 	/* Initialize the scan as not being only on the current channel.  If
845277b024eSKalle Valo 	   the channel list is customized, only contains one channel, and is
846277b024eSKalle Valo 	   the active channel, this is set true and data flow is not halted. */
847277b024eSKalle Valo 	*scan_current_only = false;
848277b024eSKalle Valo 
849277b024eSKalle Valo 	if (user_scan_in) {
8505653c646SDaniel Mentz 		u8 tmpaddr[ETH_ALEN];
851277b024eSKalle Valo 
852277b024eSKalle Valo 		/* Default the ssid_filter flag to TRUE, set false under
853277b024eSKalle Valo 		   certain wildcard conditions and qualified by the existence
854277b024eSKalle Valo 		   of an SSID list before marking the scan as filtered */
855277b024eSKalle Valo 		ssid_filter = true;
856277b024eSKalle Valo 
857277b024eSKalle Valo 		/* Set the BSS type scan filter, use Adapter setting if
858277b024eSKalle Valo 		   unset */
859277b024eSKalle Valo 		scan_cfg_out->bss_mode =
8602ccf7cefSAndreas Fenkart 			(u8)(user_scan_in->bss_mode ?: adapter->scan_mode);
861277b024eSKalle Valo 
862277b024eSKalle Valo 		/* Set the number of probes to send, use Adapter setting
863277b024eSKalle Valo 		   if unset */
8642ccf7cefSAndreas Fenkart 		num_probes = user_scan_in->num_probes ?: adapter->scan_probes;
865277b024eSKalle Valo 
866277b024eSKalle Valo 		/*
867277b024eSKalle Valo 		 * Set the BSSID filter to the incoming configuration,
868277b024eSKalle Valo 		 * if non-zero.  If not set, it will remain disabled
869277b024eSKalle Valo 		 * (all zeros).
870277b024eSKalle Valo 		 */
871277b024eSKalle Valo 		memcpy(scan_cfg_out->specific_bssid,
872277b024eSKalle Valo 		       user_scan_in->specific_bssid,
873277b024eSKalle Valo 		       sizeof(scan_cfg_out->specific_bssid));
874277b024eSKalle Valo 
8755653c646SDaniel Mentz 		memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
8765653c646SDaniel Mentz 
877277b024eSKalle Valo 		if (adapter->ext_scan &&
8785653c646SDaniel Mentz 		    !is_zero_ether_addr(tmpaddr)) {
879277b024eSKalle Valo 			bssid_tlv =
880277b024eSKalle Valo 				(struct mwifiex_ie_types_bssid_list *)tlv_pos;
881277b024eSKalle Valo 			bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID);
882277b024eSKalle Valo 			bssid_tlv->header.len = cpu_to_le16(ETH_ALEN);
883277b024eSKalle Valo 			memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid,
884277b024eSKalle Valo 			       ETH_ALEN);
885277b024eSKalle Valo 			tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list);
886277b024eSKalle Valo 		}
887277b024eSKalle Valo 
888277b024eSKalle Valo 		for (i = 0; i < user_scan_in->num_ssids; i++) {
889277b024eSKalle Valo 			ssid_len = user_scan_in->ssid_list[i].ssid_len;
890277b024eSKalle Valo 
891277b024eSKalle Valo 			wildcard_ssid_tlv =
892277b024eSKalle Valo 				(struct mwifiex_ie_types_wildcard_ssid_params *)
893277b024eSKalle Valo 				tlv_pos;
894277b024eSKalle Valo 			wildcard_ssid_tlv->header.type =
895277b024eSKalle Valo 				cpu_to_le16(TLV_TYPE_WILDCARDSSID);
896277b024eSKalle Valo 			wildcard_ssid_tlv->header.len = cpu_to_le16(
897277b024eSKalle Valo 				(u16) (ssid_len + sizeof(wildcard_ssid_tlv->
898277b024eSKalle Valo 							 max_ssid_length)));
899277b024eSKalle Valo 
900277b024eSKalle Valo 			/*
901277b024eSKalle Valo 			 * max_ssid_length = 0 tells firmware to perform
902277b024eSKalle Valo 			 * specific scan for the SSID filled, whereas
903277b024eSKalle Valo 			 * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
904277b024eSKalle Valo 			 * wildcard scan.
905277b024eSKalle Valo 			 */
906277b024eSKalle Valo 			if (ssid_len)
907277b024eSKalle Valo 				wildcard_ssid_tlv->max_ssid_length = 0;
908277b024eSKalle Valo 			else
909277b024eSKalle Valo 				wildcard_ssid_tlv->max_ssid_length =
910277b024eSKalle Valo 							IEEE80211_MAX_SSID_LEN;
911277b024eSKalle Valo 
912277b024eSKalle Valo 			if (!memcmp(user_scan_in->ssid_list[i].ssid,
913277b024eSKalle Valo 				    "DIRECT-", 7))
914277b024eSKalle Valo 				wildcard_ssid_tlv->max_ssid_length = 0xfe;
915277b024eSKalle Valo 
916277b024eSKalle Valo 			memcpy(wildcard_ssid_tlv->ssid,
917277b024eSKalle Valo 			       user_scan_in->ssid_list[i].ssid, ssid_len);
918277b024eSKalle Valo 
919277b024eSKalle Valo 			tlv_pos += (sizeof(wildcard_ssid_tlv->header)
920277b024eSKalle Valo 				+ le16_to_cpu(wildcard_ssid_tlv->header.len));
921277b024eSKalle Valo 
922277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
923277b024eSKalle Valo 				    "info: scan: ssid[%d]: %s, %d\n",
924277b024eSKalle Valo 				    i, wildcard_ssid_tlv->ssid,
925277b024eSKalle Valo 				    wildcard_ssid_tlv->max_ssid_length);
926277b024eSKalle Valo 
927277b024eSKalle Valo 			/* Empty wildcard ssid with a maxlen will match many or
928277b024eSKalle Valo 			   potentially all SSIDs (maxlen == 32), therefore do
929277b024eSKalle Valo 			   not treat the scan as
930277b024eSKalle Valo 			   filtered. */
931277b024eSKalle Valo 			if (!ssid_len && wildcard_ssid_tlv->max_ssid_length)
932277b024eSKalle Valo 				ssid_filter = false;
933277b024eSKalle Valo 		}
934277b024eSKalle Valo 
935277b024eSKalle Valo 		/*
936277b024eSKalle Valo 		 *  The default number of channels sent in the command is low to
937277b024eSKalle Valo 		 *  ensure the response buffer from the firmware does not
938277b024eSKalle Valo 		 *  truncate scan results.  That is not an issue with an SSID
939277b024eSKalle Valo 		 *  or BSSID filter applied to the scan results in the firmware.
940277b024eSKalle Valo 		 */
9415653c646SDaniel Mentz 		memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
942277b024eSKalle Valo 		if ((i && ssid_filter) ||
9435653c646SDaniel Mentz 		    !is_zero_ether_addr(tmpaddr))
944277b024eSKalle Valo 			*filtered_scan = true;
945277b024eSKalle Valo 
946277b024eSKalle Valo 		if (user_scan_in->scan_chan_gap) {
947277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
948277b024eSKalle Valo 				    "info: scan: channel gap = %d\n",
949277b024eSKalle Valo 				    user_scan_in->scan_chan_gap);
950277b024eSKalle Valo 			*max_chan_per_scan =
951277b024eSKalle Valo 					MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN;
952277b024eSKalle Valo 
953277b024eSKalle Valo 			chan_gap_tlv = (void *)tlv_pos;
954277b024eSKalle Valo 			chan_gap_tlv->header.type =
955277b024eSKalle Valo 					 cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP);
956277b024eSKalle Valo 			chan_gap_tlv->header.len =
957277b024eSKalle Valo 				    cpu_to_le16(sizeof(chan_gap_tlv->chan_gap));
958277b024eSKalle Valo 			chan_gap_tlv->chan_gap =
959277b024eSKalle Valo 				     cpu_to_le16((user_scan_in->scan_chan_gap));
960277b024eSKalle Valo 			tlv_pos +=
961277b024eSKalle Valo 				  sizeof(struct mwifiex_ie_types_scan_chan_gap);
962277b024eSKalle Valo 		}
963c2a8f0ffSGanapathi Bhat 
964a9be1864SRuan Jinjie 		if (!is_zero_ether_addr(user_scan_in->random_mac)) {
965c2a8f0ffSGanapathi Bhat 			random_mac_tlv = (void *)tlv_pos;
966c2a8f0ffSGanapathi Bhat 			random_mac_tlv->header.type =
967c2a8f0ffSGanapathi Bhat 					 cpu_to_le16(TLV_TYPE_RANDOM_MAC);
968c2a8f0ffSGanapathi Bhat 			random_mac_tlv->header.len =
969c2a8f0ffSGanapathi Bhat 				    cpu_to_le16(sizeof(random_mac_tlv->mac));
970c2a8f0ffSGanapathi Bhat 			ether_addr_copy(random_mac_tlv->mac,
971c2a8f0ffSGanapathi Bhat 					user_scan_in->random_mac);
972c2a8f0ffSGanapathi Bhat 			tlv_pos +=
973c2a8f0ffSGanapathi Bhat 				  sizeof(struct mwifiex_ie_types_random_mac);
974c2a8f0ffSGanapathi Bhat 		}
975277b024eSKalle Valo 	} else {
976277b024eSKalle Valo 		scan_cfg_out->bss_mode = (u8) adapter->scan_mode;
977277b024eSKalle Valo 		num_probes = adapter->scan_probes;
978277b024eSKalle Valo 	}
979277b024eSKalle Valo 
980277b024eSKalle Valo 	/*
981277b024eSKalle Valo 	 *  If a specific BSSID or SSID is used, the number of channels in the
982277b024eSKalle Valo 	 *  scan command will be increased to the absolute maximum.
983277b024eSKalle Valo 	 */
9840a5cc497SAmitkumar Karwar 	if (*filtered_scan) {
985277b024eSKalle Valo 		*max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN;
9860a5cc497SAmitkumar Karwar 	} else {
9870a5cc497SAmitkumar Karwar 		if (!priv->media_connected)
988277b024eSKalle Valo 			*max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD;
9890a5cc497SAmitkumar Karwar 		else
9900a5cc497SAmitkumar Karwar 			*max_chan_per_scan =
9910a5cc497SAmitkumar Karwar 					MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD / 2;
9920a5cc497SAmitkumar Karwar 	}
993277b024eSKalle Valo 
994277b024eSKalle Valo 	if (adapter->ext_scan) {
995277b024eSKalle Valo 		bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos;
996277b024eSKalle Valo 		bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE);
997277b024eSKalle Valo 		bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode));
998277b024eSKalle Valo 		bss_mode->bss_mode = scan_cfg_out->bss_mode;
999277b024eSKalle Valo 		tlv_pos += sizeof(bss_mode->header) +
1000277b024eSKalle Valo 			   le16_to_cpu(bss_mode->header.len);
1001277b024eSKalle Valo 	}
1002277b024eSKalle Valo 
1003277b024eSKalle Valo 	/* If the input config or adapter has the number of Probes set,
1004277b024eSKalle Valo 	   add tlv */
1005277b024eSKalle Valo 	if (num_probes) {
1006277b024eSKalle Valo 
1007277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
1008277b024eSKalle Valo 			    "info: scan: num_probes = %d\n",
1009277b024eSKalle Valo 			    num_probes);
1010277b024eSKalle Valo 
1011277b024eSKalle Valo 		num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos;
1012277b024eSKalle Valo 		num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
1013277b024eSKalle Valo 		num_probes_tlv->header.len =
1014277b024eSKalle Valo 			cpu_to_le16(sizeof(num_probes_tlv->num_probes));
1015277b024eSKalle Valo 		num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes);
1016277b024eSKalle Valo 
1017277b024eSKalle Valo 		tlv_pos += sizeof(num_probes_tlv->header) +
1018277b024eSKalle Valo 			le16_to_cpu(num_probes_tlv->header.len);
1019277b024eSKalle Valo 
1020277b024eSKalle Valo 	}
1021277b024eSKalle Valo 
1022277b024eSKalle Valo 	if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
1023277b024eSKalle Valo 	    (priv->adapter->config_bands & BAND_GN ||
1024277b024eSKalle Valo 	     priv->adapter->config_bands & BAND_AN)) {
1025277b024eSKalle Valo 		ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos;
1026277b024eSKalle Valo 		memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap));
1027277b024eSKalle Valo 		ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
1028277b024eSKalle Valo 		ht_cap->header.len =
1029277b024eSKalle Valo 				cpu_to_le16(sizeof(struct ieee80211_ht_cap));
1030277b024eSKalle Valo 		radio_type =
1031277b024eSKalle Valo 			mwifiex_band_to_radio_type(priv->adapter->config_bands);
1032277b024eSKalle Valo 		mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
1033277b024eSKalle Valo 		tlv_pos += sizeof(struct mwifiex_ie_types_htcap);
1034277b024eSKalle Valo 	}
1035277b024eSKalle Valo 
1036277b024eSKalle Valo 	/* Append vendor specific IE TLV */
1037277b024eSKalle Valo 	mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos);
1038277b024eSKalle Valo 
1039277b024eSKalle Valo 	/*
1040277b024eSKalle Valo 	 * Set the output for the channel TLV to the address in the tlv buffer
1041277b024eSKalle Valo 	 *   past any TLVs that were added in this function (SSID, num_probes).
1042277b024eSKalle Valo 	 *   Channel TLVs will be added past this for each scan command,
1043277b024eSKalle Valo 	 *   preserving the TLVs that were previously added.
1044277b024eSKalle Valo 	 */
1045277b024eSKalle Valo 	*chan_list_out =
1046277b024eSKalle Valo 		(struct mwifiex_ie_types_chan_list_param_set *) tlv_pos;
1047277b024eSKalle Valo 
1048277b024eSKalle Valo 	if (user_scan_in && user_scan_in->chan_list[0].chan_number) {
1049277b024eSKalle Valo 
1050277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
1051277b024eSKalle Valo 			    "info: Scan: Using supplied channel list\n");
1052277b024eSKalle Valo 
1053277b024eSKalle Valo 		for (chan_idx = 0;
1054277b024eSKalle Valo 		     chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX &&
1055277b024eSKalle Valo 		     user_scan_in->chan_list[chan_idx].chan_number;
1056277b024eSKalle Valo 		     chan_idx++) {
1057277b024eSKalle Valo 
1058277b024eSKalle Valo 			channel = user_scan_in->chan_list[chan_idx].chan_number;
1059948ad6b3SAndreas Fenkart 			scan_chan_list[chan_idx].chan_number = channel;
1060277b024eSKalle Valo 
1061277b024eSKalle Valo 			radio_type =
1062277b024eSKalle Valo 				user_scan_in->chan_list[chan_idx].radio_type;
1063948ad6b3SAndreas Fenkart 			scan_chan_list[chan_idx].radio_type = radio_type;
1064277b024eSKalle Valo 
1065277b024eSKalle Valo 			scan_type = user_scan_in->chan_list[chan_idx].scan_type;
1066277b024eSKalle Valo 
1067277b024eSKalle Valo 			if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
1068948ad6b3SAndreas Fenkart 				scan_chan_list[chan_idx].chan_scan_mode_bitmap
1069277b024eSKalle Valo 					|= (MWIFIEX_PASSIVE_SCAN |
1070277b024eSKalle Valo 					    MWIFIEX_HIDDEN_SSID_REPORT);
1071277b024eSKalle Valo 			else
1072948ad6b3SAndreas Fenkart 				scan_chan_list[chan_idx].chan_scan_mode_bitmap
1073277b024eSKalle Valo 					&= ~MWIFIEX_PASSIVE_SCAN;
1074277b024eSKalle Valo 
1075948ad6b3SAndreas Fenkart 			scan_chan_list[chan_idx].chan_scan_mode_bitmap
1076277b024eSKalle Valo 				|= MWIFIEX_DISABLE_CHAN_FILT;
1077277b024eSKalle Valo 
1078277b024eSKalle Valo 			if (user_scan_in->chan_list[chan_idx].scan_time) {
1079277b024eSKalle Valo 				scan_dur = (u16) user_scan_in->
1080277b024eSKalle Valo 					chan_list[chan_idx].scan_time;
1081277b024eSKalle Valo 			} else {
1082277b024eSKalle Valo 				if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
1083277b024eSKalle Valo 					scan_dur = adapter->passive_scan_time;
1084277b024eSKalle Valo 				else if (*filtered_scan)
1085277b024eSKalle Valo 					scan_dur = adapter->specific_scan_time;
1086277b024eSKalle Valo 				else
1087277b024eSKalle Valo 					scan_dur = adapter->active_scan_time;
1088277b024eSKalle Valo 			}
1089277b024eSKalle Valo 
1090948ad6b3SAndreas Fenkart 			scan_chan_list[chan_idx].min_scan_time =
1091277b024eSKalle Valo 				cpu_to_le16(scan_dur);
1092948ad6b3SAndreas Fenkart 			scan_chan_list[chan_idx].max_scan_time =
1093277b024eSKalle Valo 				cpu_to_le16(scan_dur);
1094277b024eSKalle Valo 		}
1095277b024eSKalle Valo 
1096277b024eSKalle Valo 		/* Check if we are only scanning the current channel */
1097277b024eSKalle Valo 		if ((chan_idx == 1) &&
1098277b024eSKalle Valo 		    (user_scan_in->chan_list[0].chan_number ==
1099277b024eSKalle Valo 		     priv->curr_bss_params.bss_descriptor.channel)) {
1100277b024eSKalle Valo 			*scan_current_only = true;
1101277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
1102277b024eSKalle Valo 				    "info: Scan: Scanning current channel only\n");
1103277b024eSKalle Valo 		}
1104277b024eSKalle Valo 	} else {
1105277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
1106277b024eSKalle Valo 			    "info: Scan: Creating full region channel list\n");
11078ac91341SKirtika Ruchandani 		mwifiex_scan_create_channel_list(priv, user_scan_in,
1108277b024eSKalle Valo 						 scan_chan_list,
1109277b024eSKalle Valo 						 *filtered_scan);
1110277b024eSKalle Valo 	}
1111277b024eSKalle Valo 
1112277b024eSKalle Valo }
1113277b024eSKalle Valo 
1114277b024eSKalle Valo /*
1115277b024eSKalle Valo  * This function inspects the scan response buffer for pointers to
1116277b024eSKalle Valo  * expected TLVs.
1117277b024eSKalle Valo  *
1118277b024eSKalle Valo  * TLVs can be included at the end of the scan response BSS information.
1119277b024eSKalle Valo  *
1120277b024eSKalle Valo  * Data in the buffer is parsed pointers to TLVs that can potentially
1121277b024eSKalle Valo  * be passed back in the response.
1122277b024eSKalle Valo  */
1123277b024eSKalle Valo static void
mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter * adapter,struct mwifiex_ie_types_data * tlv,u32 tlv_buf_size,u32 req_tlv_type,struct mwifiex_ie_types_data ** tlv_data)1124277b024eSKalle Valo mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter,
1125277b024eSKalle Valo 				     struct mwifiex_ie_types_data *tlv,
1126277b024eSKalle Valo 				     u32 tlv_buf_size, u32 req_tlv_type,
1127277b024eSKalle Valo 				     struct mwifiex_ie_types_data **tlv_data)
1128277b024eSKalle Valo {
1129277b024eSKalle Valo 	struct mwifiex_ie_types_data *current_tlv;
1130277b024eSKalle Valo 	u32 tlv_buf_left;
1131277b024eSKalle Valo 	u32 tlv_type;
1132277b024eSKalle Valo 	u32 tlv_len;
1133277b024eSKalle Valo 
1134277b024eSKalle Valo 	current_tlv = tlv;
1135277b024eSKalle Valo 	tlv_buf_left = tlv_buf_size;
1136277b024eSKalle Valo 	*tlv_data = NULL;
1137277b024eSKalle Valo 
1138277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
1139277b024eSKalle Valo 		    "info: SCAN_RESP: tlv_buf_size = %d\n",
1140277b024eSKalle Valo 		    tlv_buf_size);
1141277b024eSKalle Valo 
1142277b024eSKalle Valo 	while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) {
1143277b024eSKalle Valo 
1144277b024eSKalle Valo 		tlv_type = le16_to_cpu(current_tlv->header.type);
1145277b024eSKalle Valo 		tlv_len = le16_to_cpu(current_tlv->header.len);
1146277b024eSKalle Valo 
1147277b024eSKalle Valo 		if (sizeof(tlv->header) + tlv_len > tlv_buf_left) {
1148277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
1149277b024eSKalle Valo 				    "SCAN_RESP: TLV buffer corrupt\n");
1150277b024eSKalle Valo 			break;
1151277b024eSKalle Valo 		}
1152277b024eSKalle Valo 
1153277b024eSKalle Valo 		if (req_tlv_type == tlv_type) {
1154277b024eSKalle Valo 			switch (tlv_type) {
1155277b024eSKalle Valo 			case TLV_TYPE_TSFTIMESTAMP:
1156277b024eSKalle Valo 				mwifiex_dbg(adapter, INFO,
1157277b024eSKalle Valo 					    "info: SCAN_RESP: TSF\t"
1158277b024eSKalle Valo 					    "timestamp TLV, len = %d\n",
1159277b024eSKalle Valo 					    tlv_len);
1160277b024eSKalle Valo 				*tlv_data = current_tlv;
1161277b024eSKalle Valo 				break;
1162277b024eSKalle Valo 			case TLV_TYPE_CHANNELBANDLIST:
1163277b024eSKalle Valo 				mwifiex_dbg(adapter, INFO,
1164277b024eSKalle Valo 					    "info: SCAN_RESP: channel\t"
1165277b024eSKalle Valo 					    "band list TLV, len = %d\n",
1166277b024eSKalle Valo 					    tlv_len);
1167277b024eSKalle Valo 				*tlv_data = current_tlv;
1168277b024eSKalle Valo 				break;
1169277b024eSKalle Valo 			default:
1170277b024eSKalle Valo 				mwifiex_dbg(adapter, ERROR,
1171277b024eSKalle Valo 					    "SCAN_RESP: unhandled TLV = %d\n",
1172277b024eSKalle Valo 					    tlv_type);
1173277b024eSKalle Valo 				/* Give up, this seems corrupted */
1174277b024eSKalle Valo 				return;
1175277b024eSKalle Valo 			}
1176277b024eSKalle Valo 		}
1177277b024eSKalle Valo 
1178277b024eSKalle Valo 		if (*tlv_data)
1179277b024eSKalle Valo 			break;
1180277b024eSKalle Valo 
1181277b024eSKalle Valo 
1182277b024eSKalle Valo 		tlv_buf_left -= (sizeof(tlv->header) + tlv_len);
1183277b024eSKalle Valo 		current_tlv =
1184277b024eSKalle Valo 			(struct mwifiex_ie_types_data *) (current_tlv->data +
1185277b024eSKalle Valo 							  tlv_len);
1186277b024eSKalle Valo 
1187277b024eSKalle Valo 	}			/* while */
1188277b024eSKalle Valo }
1189277b024eSKalle Valo 
1190277b024eSKalle Valo /*
1191277b024eSKalle Valo  * This function parses provided beacon buffer and updates
1192277b024eSKalle Valo  * respective fields in bss descriptor structure.
1193277b024eSKalle Valo  */
mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter * adapter,struct mwifiex_bssdescriptor * bss_entry)1194277b024eSKalle Valo int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
1195277b024eSKalle Valo 				    struct mwifiex_bssdescriptor *bss_entry)
1196277b024eSKalle Valo {
1197277b024eSKalle Valo 	u8 element_id;
1198277b024eSKalle Valo 	struct ieee_types_fh_param_set *fh_param_set;
1199277b024eSKalle Valo 	struct ieee_types_ds_param_set *ds_param_set;
1200277b024eSKalle Valo 	struct ieee_types_cf_param_set *cf_param_set;
1201277b024eSKalle Valo 	struct ieee_types_ibss_param_set *ibss_param_set;
1202277b024eSKalle Valo 	u8 *current_ptr;
1203277b024eSKalle Valo 	u8 *rate;
1204277b024eSKalle Valo 	u8 element_len;
1205277b024eSKalle Valo 	u16 total_ie_len;
1206277b024eSKalle Valo 	u8 bytes_to_copy;
1207277b024eSKalle Valo 	u8 rate_size;
1208277b024eSKalle Valo 	u8 found_data_rate_ie;
1209277b024eSKalle Valo 	u32 bytes_left;
1210277b024eSKalle Valo 	struct ieee_types_vendor_specific *vendor_ie;
1211277b024eSKalle Valo 	const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
1212277b024eSKalle Valo 	const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
1213277b024eSKalle Valo 
1214277b024eSKalle Valo 	found_data_rate_ie = false;
1215277b024eSKalle Valo 	rate_size = 0;
1216277b024eSKalle Valo 	current_ptr = bss_entry->beacon_buf;
1217277b024eSKalle Valo 	bytes_left = bss_entry->beacon_buf_size;
1218277b024eSKalle Valo 
1219277b024eSKalle Valo 	/* Process variable IE */
1220277b024eSKalle Valo 	while (bytes_left >= 2) {
1221277b024eSKalle Valo 		element_id = *current_ptr;
1222277b024eSKalle Valo 		element_len = *(current_ptr + 1);
1223277b024eSKalle Valo 		total_ie_len = element_len + sizeof(struct ieee_types_header);
1224277b024eSKalle Valo 
1225277b024eSKalle Valo 		if (bytes_left < total_ie_len) {
1226277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
1227277b024eSKalle Valo 				    "err: InterpretIE: in processing\t"
1228277b024eSKalle Valo 				    "IE, bytes left < IE length\n");
12296334dea8SPavel Machek 			return -EINVAL;
1230277b024eSKalle Valo 		}
1231277b024eSKalle Valo 		switch (element_id) {
1232277b024eSKalle Valo 		case WLAN_EID_SSID:
123313ec7f10STakashi Iwai 			if (element_len > IEEE80211_MAX_SSID_LEN)
123413ec7f10STakashi Iwai 				return -EINVAL;
1235277b024eSKalle Valo 			bss_entry->ssid.ssid_len = element_len;
1236277b024eSKalle Valo 			memcpy(bss_entry->ssid.ssid, (current_ptr + 2),
1237277b024eSKalle Valo 			       element_len);
1238277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
1239277b024eSKalle Valo 				    "info: InterpretIE: ssid: %-32s\n",
1240277b024eSKalle Valo 				    bss_entry->ssid.ssid);
1241277b024eSKalle Valo 			break;
1242277b024eSKalle Valo 
1243277b024eSKalle Valo 		case WLAN_EID_SUPP_RATES:
124413ec7f10STakashi Iwai 			if (element_len > MWIFIEX_SUPPORTED_RATES)
124513ec7f10STakashi Iwai 				return -EINVAL;
1246277b024eSKalle Valo 			memcpy(bss_entry->data_rates, current_ptr + 2,
1247277b024eSKalle Valo 			       element_len);
1248277b024eSKalle Valo 			memcpy(bss_entry->supported_rates, current_ptr + 2,
1249277b024eSKalle Valo 			       element_len);
1250277b024eSKalle Valo 			rate_size = element_len;
1251277b024eSKalle Valo 			found_data_rate_ie = true;
1252277b024eSKalle Valo 			break;
1253277b024eSKalle Valo 
1254277b024eSKalle Valo 		case WLAN_EID_FH_PARAMS:
12550a3ce169SBrian Norris 			if (total_ie_len < sizeof(*fh_param_set))
1256685c9b77STakashi Iwai 				return -EINVAL;
1257277b024eSKalle Valo 			fh_param_set =
1258277b024eSKalle Valo 				(struct ieee_types_fh_param_set *) current_ptr;
1259277b024eSKalle Valo 			memcpy(&bss_entry->phy_param_set.fh_param_set,
1260277b024eSKalle Valo 			       fh_param_set,
1261277b024eSKalle Valo 			       sizeof(struct ieee_types_fh_param_set));
1262277b024eSKalle Valo 			break;
1263277b024eSKalle Valo 
1264277b024eSKalle Valo 		case WLAN_EID_DS_PARAMS:
12650a3ce169SBrian Norris 			if (total_ie_len < sizeof(*ds_param_set))
1266685c9b77STakashi Iwai 				return -EINVAL;
1267277b024eSKalle Valo 			ds_param_set =
1268277b024eSKalle Valo 				(struct ieee_types_ds_param_set *) current_ptr;
1269277b024eSKalle Valo 
1270277b024eSKalle Valo 			bss_entry->channel = ds_param_set->current_chan;
1271277b024eSKalle Valo 
1272277b024eSKalle Valo 			memcpy(&bss_entry->phy_param_set.ds_param_set,
1273277b024eSKalle Valo 			       ds_param_set,
1274277b024eSKalle Valo 			       sizeof(struct ieee_types_ds_param_set));
1275277b024eSKalle Valo 			break;
1276277b024eSKalle Valo 
1277277b024eSKalle Valo 		case WLAN_EID_CF_PARAMS:
12780a3ce169SBrian Norris 			if (total_ie_len < sizeof(*cf_param_set))
1279685c9b77STakashi Iwai 				return -EINVAL;
1280277b024eSKalle Valo 			cf_param_set =
1281277b024eSKalle Valo 				(struct ieee_types_cf_param_set *) current_ptr;
1282277b024eSKalle Valo 			memcpy(&bss_entry->ss_param_set.cf_param_set,
1283277b024eSKalle Valo 			       cf_param_set,
1284277b024eSKalle Valo 			       sizeof(struct ieee_types_cf_param_set));
1285277b024eSKalle Valo 			break;
1286277b024eSKalle Valo 
1287277b024eSKalle Valo 		case WLAN_EID_IBSS_PARAMS:
12880a3ce169SBrian Norris 			if (total_ie_len < sizeof(*ibss_param_set))
1289685c9b77STakashi Iwai 				return -EINVAL;
1290277b024eSKalle Valo 			ibss_param_set =
1291277b024eSKalle Valo 				(struct ieee_types_ibss_param_set *)
1292277b024eSKalle Valo 				current_ptr;
1293277b024eSKalle Valo 			memcpy(&bss_entry->ss_param_set.ibss_param_set,
1294277b024eSKalle Valo 			       ibss_param_set,
1295277b024eSKalle Valo 			       sizeof(struct ieee_types_ibss_param_set));
1296277b024eSKalle Valo 			break;
1297277b024eSKalle Valo 
1298277b024eSKalle Valo 		case WLAN_EID_ERP_INFO:
1299685c9b77STakashi Iwai 			if (!element_len)
1300685c9b77STakashi Iwai 				return -EINVAL;
1301277b024eSKalle Valo 			bss_entry->erp_flags = *(current_ptr + 2);
1302277b024eSKalle Valo 			break;
1303277b024eSKalle Valo 
1304277b024eSKalle Valo 		case WLAN_EID_PWR_CONSTRAINT:
1305685c9b77STakashi Iwai 			if (!element_len)
1306685c9b77STakashi Iwai 				return -EINVAL;
1307277b024eSKalle Valo 			bss_entry->local_constraint = *(current_ptr + 2);
1308277b024eSKalle Valo 			bss_entry->sensed_11h = true;
1309277b024eSKalle Valo 			break;
1310277b024eSKalle Valo 
1311277b024eSKalle Valo 		case WLAN_EID_CHANNEL_SWITCH:
1312277b024eSKalle Valo 			bss_entry->chan_sw_ie_present = true;
131332f44730SGustavo A. R. Silva 			fallthrough;
1314277b024eSKalle Valo 		case WLAN_EID_PWR_CAPABILITY:
1315277b024eSKalle Valo 		case WLAN_EID_TPC_REPORT:
1316277b024eSKalle Valo 		case WLAN_EID_QUIET:
1317277b024eSKalle Valo 			bss_entry->sensed_11h = true;
1318277b024eSKalle Valo 		    break;
1319277b024eSKalle Valo 
1320277b024eSKalle Valo 		case WLAN_EID_EXT_SUPP_RATES:
1321277b024eSKalle Valo 			/*
1322277b024eSKalle Valo 			 * Only process extended supported rate
1323277b024eSKalle Valo 			 * if data rate is already found.
1324277b024eSKalle Valo 			 * Data rate IE should come before
1325277b024eSKalle Valo 			 * extended supported rate IE
1326277b024eSKalle Valo 			 */
1327277b024eSKalle Valo 			if (found_data_rate_ie) {
1328277b024eSKalle Valo 				if ((element_len + rate_size) >
1329277b024eSKalle Valo 				    MWIFIEX_SUPPORTED_RATES)
1330277b024eSKalle Valo 					bytes_to_copy =
1331277b024eSKalle Valo 						(MWIFIEX_SUPPORTED_RATES -
1332277b024eSKalle Valo 						 rate_size);
1333277b024eSKalle Valo 				else
1334277b024eSKalle Valo 					bytes_to_copy = element_len;
1335277b024eSKalle Valo 
1336277b024eSKalle Valo 				rate = (u8 *) bss_entry->data_rates;
1337277b024eSKalle Valo 				rate += rate_size;
1338277b024eSKalle Valo 				memcpy(rate, current_ptr + 2, bytes_to_copy);
1339277b024eSKalle Valo 
1340277b024eSKalle Valo 				rate = (u8 *) bss_entry->supported_rates;
1341277b024eSKalle Valo 				rate += rate_size;
1342277b024eSKalle Valo 				memcpy(rate, current_ptr + 2, bytes_to_copy);
1343277b024eSKalle Valo 			}
1344277b024eSKalle Valo 			break;
1345277b024eSKalle Valo 
1346277b024eSKalle Valo 		case WLAN_EID_VENDOR_SPECIFIC:
1347277b024eSKalle Valo 			vendor_ie = (struct ieee_types_vendor_specific *)
1348277b024eSKalle Valo 					current_ptr;
1349277b024eSKalle Valo 
135063d7ef36SBrian Norris 			/* 802.11 requires at least 3-byte OUI. */
135163d7ef36SBrian Norris 			if (element_len < sizeof(vendor_ie->vend_hdr.oui.oui))
135263d7ef36SBrian Norris 				return -EINVAL;
135363d7ef36SBrian Norris 
135463d7ef36SBrian Norris 			/* Not long enough for a match? Skip it. */
135563d7ef36SBrian Norris 			if (element_len < sizeof(wpa_oui))
135663d7ef36SBrian Norris 				break;
135763d7ef36SBrian Norris 
135863d7ef36SBrian Norris 			if (!memcmp(&vendor_ie->vend_hdr.oui, wpa_oui,
1359277b024eSKalle Valo 				    sizeof(wpa_oui))) {
1360277b024eSKalle Valo 				bss_entry->bcn_wpa_ie =
1361277b024eSKalle Valo 					(struct ieee_types_vendor_specific *)
1362277b024eSKalle Valo 					current_ptr;
1363277b024eSKalle Valo 				bss_entry->wpa_offset = (u16)
1364277b024eSKalle Valo 					(current_ptr - bss_entry->beacon_buf);
136563d7ef36SBrian Norris 			} else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui,
1366277b024eSKalle Valo 				    sizeof(wmm_oui))) {
1367277b024eSKalle Valo 				if (total_ie_len ==
1368277b024eSKalle Valo 				    sizeof(struct ieee_types_wmm_parameter) ||
1369277b024eSKalle Valo 				    total_ie_len ==
1370277b024eSKalle Valo 				    sizeof(struct ieee_types_wmm_info))
1371277b024eSKalle Valo 					/*
1372277b024eSKalle Valo 					 * Only accept and copy the WMM IE if
1373277b024eSKalle Valo 					 * it matches the size expected for the
1374277b024eSKalle Valo 					 * WMM Info IE or the WMM Parameter IE.
1375277b024eSKalle Valo 					 */
1376277b024eSKalle Valo 					memcpy((u8 *) &bss_entry->wmm_ie,
1377277b024eSKalle Valo 					       current_ptr, total_ie_len);
1378277b024eSKalle Valo 			}
1379277b024eSKalle Valo 			break;
1380277b024eSKalle Valo 		case WLAN_EID_RSN:
1381277b024eSKalle Valo 			bss_entry->bcn_rsn_ie =
1382277b024eSKalle Valo 				(struct ieee_types_generic *) current_ptr;
1383277b024eSKalle Valo 			bss_entry->rsn_offset = (u16) (current_ptr -
1384277b024eSKalle Valo 							bss_entry->beacon_buf);
1385277b024eSKalle Valo 			break;
1386277b024eSKalle Valo 		case WLAN_EID_BSS_AC_ACCESS_DELAY:
1387277b024eSKalle Valo 			bss_entry->bcn_wapi_ie =
1388277b024eSKalle Valo 				(struct ieee_types_generic *) current_ptr;
1389277b024eSKalle Valo 			bss_entry->wapi_offset = (u16) (current_ptr -
1390277b024eSKalle Valo 							bss_entry->beacon_buf);
1391277b024eSKalle Valo 			break;
1392277b024eSKalle Valo 		case WLAN_EID_HT_CAPABILITY:
1393277b024eSKalle Valo 			bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *)
1394277b024eSKalle Valo 					(current_ptr +
1395277b024eSKalle Valo 					sizeof(struct ieee_types_header));
1396277b024eSKalle Valo 			bss_entry->ht_cap_offset = (u16) (current_ptr +
1397277b024eSKalle Valo 					sizeof(struct ieee_types_header) -
1398277b024eSKalle Valo 					bss_entry->beacon_buf);
1399277b024eSKalle Valo 			break;
1400277b024eSKalle Valo 		case WLAN_EID_HT_OPERATION:
1401277b024eSKalle Valo 			bss_entry->bcn_ht_oper =
1402277b024eSKalle Valo 				(struct ieee80211_ht_operation *)(current_ptr +
1403277b024eSKalle Valo 					sizeof(struct ieee_types_header));
1404277b024eSKalle Valo 			bss_entry->ht_info_offset = (u16) (current_ptr +
1405277b024eSKalle Valo 					sizeof(struct ieee_types_header) -
1406277b024eSKalle Valo 					bss_entry->beacon_buf);
1407277b024eSKalle Valo 			break;
1408277b024eSKalle Valo 		case WLAN_EID_VHT_CAPABILITY:
1409277b024eSKalle Valo 			bss_entry->disable_11ac = false;
1410277b024eSKalle Valo 			bss_entry->bcn_vht_cap =
1411277b024eSKalle Valo 				(void *)(current_ptr +
1412277b024eSKalle Valo 					 sizeof(struct ieee_types_header));
1413277b024eSKalle Valo 			bss_entry->vht_cap_offset =
1414277b024eSKalle Valo 					(u16)((u8 *)bss_entry->bcn_vht_cap -
1415277b024eSKalle Valo 					      bss_entry->beacon_buf);
1416277b024eSKalle Valo 			break;
1417277b024eSKalle Valo 		case WLAN_EID_VHT_OPERATION:
1418277b024eSKalle Valo 			bss_entry->bcn_vht_oper =
1419277b024eSKalle Valo 				(void *)(current_ptr +
1420277b024eSKalle Valo 					 sizeof(struct ieee_types_header));
1421277b024eSKalle Valo 			bss_entry->vht_info_offset =
1422277b024eSKalle Valo 					(u16)((u8 *)bss_entry->bcn_vht_oper -
1423277b024eSKalle Valo 					      bss_entry->beacon_buf);
1424277b024eSKalle Valo 			break;
1425277b024eSKalle Valo 		case WLAN_EID_BSS_COEX_2040:
1426277b024eSKalle Valo 			bss_entry->bcn_bss_co_2040 = current_ptr;
1427277b024eSKalle Valo 			bss_entry->bss_co_2040_offset =
1428277b024eSKalle Valo 				(u16) (current_ptr - bss_entry->beacon_buf);
1429277b024eSKalle Valo 			break;
1430277b024eSKalle Valo 		case WLAN_EID_EXT_CAPABILITY:
1431277b024eSKalle Valo 			bss_entry->bcn_ext_cap = current_ptr;
1432277b024eSKalle Valo 			bss_entry->ext_cap_offset =
1433277b024eSKalle Valo 				(u16) (current_ptr - bss_entry->beacon_buf);
1434277b024eSKalle Valo 			break;
1435277b024eSKalle Valo 		case WLAN_EID_OPMODE_NOTIF:
1436277b024eSKalle Valo 			bss_entry->oper_mode = (void *)current_ptr;
1437277b024eSKalle Valo 			bss_entry->oper_mode_offset =
1438277b024eSKalle Valo 					(u16)((u8 *)bss_entry->oper_mode -
1439277b024eSKalle Valo 					      bss_entry->beacon_buf);
1440277b024eSKalle Valo 			break;
1441277b024eSKalle Valo 		default:
1442277b024eSKalle Valo 			break;
1443277b024eSKalle Valo 		}
1444277b024eSKalle Valo 
14450a3ce169SBrian Norris 		current_ptr += total_ie_len;
14460a3ce169SBrian Norris 		bytes_left -= total_ie_len;
1447277b024eSKalle Valo 
1448277b024eSKalle Valo 	}	/* while (bytes_left > 2) */
1449c81852a4Szuoqilin 	return 0;
1450277b024eSKalle Valo }
1451277b024eSKalle Valo 
1452277b024eSKalle Valo /*
1453277b024eSKalle Valo  * This function converts radio type scan parameter to a band configuration
1454277b024eSKalle Valo  * to be used in join command.
1455277b024eSKalle Valo  */
1456277b024eSKalle Valo static u8
mwifiex_radio_type_to_band(u8 radio_type)1457277b024eSKalle Valo mwifiex_radio_type_to_band(u8 radio_type)
1458277b024eSKalle Valo {
1459277b024eSKalle Valo 	switch (radio_type) {
1460277b024eSKalle Valo 	case HostCmd_SCAN_RADIO_TYPE_A:
1461277b024eSKalle Valo 		return BAND_A;
1462277b024eSKalle Valo 	case HostCmd_SCAN_RADIO_TYPE_BG:
1463277b024eSKalle Valo 	default:
1464277b024eSKalle Valo 		return BAND_G;
1465277b024eSKalle Valo 	}
1466277b024eSKalle Valo }
1467277b024eSKalle Valo 
1468277b024eSKalle Valo /*
1469277b024eSKalle Valo  * This is an internal function used to start a scan based on an input
1470277b024eSKalle Valo  * configuration.
1471277b024eSKalle Valo  *
1472277b024eSKalle Valo  * This uses the input user scan configuration information when provided in
1473277b024eSKalle Valo  * order to send the appropriate scan commands to firmware to populate or
1474277b024eSKalle Valo  * update the internal driver scan table.
1475277b024eSKalle Valo  */
mwifiex_scan_networks(struct mwifiex_private * priv,const struct mwifiex_user_scan_cfg * user_scan_in)1476277b024eSKalle Valo int mwifiex_scan_networks(struct mwifiex_private *priv,
1477277b024eSKalle Valo 			  const struct mwifiex_user_scan_cfg *user_scan_in)
1478277b024eSKalle Valo {
1479277b024eSKalle Valo 	int ret;
1480277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
1481277b024eSKalle Valo 	struct cmd_ctrl_node *cmd_node;
1482277b024eSKalle Valo 	union mwifiex_scan_cmd_config_tlv *scan_cfg_out;
1483277b024eSKalle Valo 	struct mwifiex_ie_types_chan_list_param_set *chan_list_out;
1484277b024eSKalle Valo 	struct mwifiex_chan_scan_param_set *scan_chan_list;
1485277b024eSKalle Valo 	u8 filtered_scan;
1486277b024eSKalle Valo 	u8 scan_current_chan_only;
1487277b024eSKalle Valo 	u8 max_chan_per_scan;
1488277b024eSKalle Valo 
1489277b024eSKalle Valo 	if (adapter->scan_processing) {
1490277b024eSKalle Valo 		mwifiex_dbg(adapter, WARN,
1491277b024eSKalle Valo 			    "cmd: Scan already in process...\n");
1492277b024eSKalle Valo 		return -EBUSY;
1493277b024eSKalle Valo 	}
1494277b024eSKalle Valo 
1495277b024eSKalle Valo 	if (priv->scan_block) {
1496277b024eSKalle Valo 		mwifiex_dbg(adapter, WARN,
1497277b024eSKalle Valo 			    "cmd: Scan is blocked during association...\n");
1498277b024eSKalle Valo 		return -EBUSY;
1499277b024eSKalle Valo 	}
1500277b024eSKalle Valo 
1501fc3a2fcaSGanapathi Bhat 	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags) ||
1502fc3a2fcaSGanapathi Bhat 	    test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
1503277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
1504277b024eSKalle Valo 			    "Ignore scan. Card removed or firmware in bad state\n");
1505277b024eSKalle Valo 		return -EFAULT;
1506277b024eSKalle Valo 	}
1507277b024eSKalle Valo 
15088a7f9fd8SBrian Norris 	spin_lock_bh(&adapter->mwifiex_cmd_lock);
1509277b024eSKalle Valo 	adapter->scan_processing = true;
15108a7f9fd8SBrian Norris 	spin_unlock_bh(&adapter->mwifiex_cmd_lock);
1511277b024eSKalle Valo 
1512277b024eSKalle Valo 	scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv),
1513277b024eSKalle Valo 			       GFP_KERNEL);
1514277b024eSKalle Valo 	if (!scan_cfg_out) {
1515277b024eSKalle Valo 		ret = -ENOMEM;
1516277b024eSKalle Valo 		goto done;
1517277b024eSKalle Valo 	}
1518277b024eSKalle Valo 
1519277b024eSKalle Valo 	scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX,
1520277b024eSKalle Valo 				 sizeof(struct mwifiex_chan_scan_param_set),
1521277b024eSKalle Valo 				 GFP_KERNEL);
1522277b024eSKalle Valo 	if (!scan_chan_list) {
1523277b024eSKalle Valo 		kfree(scan_cfg_out);
1524277b024eSKalle Valo 		ret = -ENOMEM;
1525277b024eSKalle Valo 		goto done;
1526277b024eSKalle Valo 	}
1527277b024eSKalle Valo 
1528277b024eSKalle Valo 	mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config,
1529277b024eSKalle Valo 			    &chan_list_out, scan_chan_list, &max_chan_per_scan,
1530277b024eSKalle Valo 			    &filtered_scan, &scan_current_chan_only);
1531277b024eSKalle Valo 
1532277b024eSKalle Valo 	ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan,
1533277b024eSKalle Valo 					&scan_cfg_out->config, chan_list_out,
1534277b024eSKalle Valo 					scan_chan_list);
1535277b024eSKalle Valo 
1536277b024eSKalle Valo 	/* Get scan command from scan_pending_q and put to cmd_pending_q */
1537277b024eSKalle Valo 	if (!ret) {
15388a7f9fd8SBrian Norris 		spin_lock_bh(&adapter->scan_pending_q_lock);
1539277b024eSKalle Valo 		if (!list_empty(&adapter->scan_pending_q)) {
1540277b024eSKalle Valo 			cmd_node = list_first_entry(&adapter->scan_pending_q,
1541277b024eSKalle Valo 						    struct cmd_ctrl_node, list);
1542277b024eSKalle Valo 			list_del(&cmd_node->list);
15438a7f9fd8SBrian Norris 			spin_unlock_bh(&adapter->scan_pending_q_lock);
1544463df471SBrian Norris 			mwifiex_insert_cmd_to_pending_q(adapter, cmd_node);
1545277b024eSKalle Valo 			queue_work(adapter->workqueue, &adapter->main_work);
1546277b024eSKalle Valo 
1547277b024eSKalle Valo 			/* Perform internal scan synchronously */
1548277b024eSKalle Valo 			if (!priv->scan_request) {
1549277b024eSKalle Valo 				mwifiex_dbg(adapter, INFO,
1550277b024eSKalle Valo 					    "wait internal scan\n");
1551277b024eSKalle Valo 				mwifiex_wait_queue_complete(adapter, cmd_node);
1552277b024eSKalle Valo 			}
1553277b024eSKalle Valo 		} else {
15548a7f9fd8SBrian Norris 			spin_unlock_bh(&adapter->scan_pending_q_lock);
1555277b024eSKalle Valo 		}
1556277b024eSKalle Valo 	}
1557277b024eSKalle Valo 
1558277b024eSKalle Valo 	kfree(scan_cfg_out);
1559277b024eSKalle Valo 	kfree(scan_chan_list);
1560277b024eSKalle Valo done:
1561277b024eSKalle Valo 	if (ret) {
15628a7f9fd8SBrian Norris 		spin_lock_bh(&adapter->mwifiex_cmd_lock);
1563277b024eSKalle Valo 		adapter->scan_processing = false;
15648a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
1565277b024eSKalle Valo 	}
1566277b024eSKalle Valo 	return ret;
1567277b024eSKalle Valo }
1568277b024eSKalle Valo 
1569277b024eSKalle Valo /*
1570277b024eSKalle Valo  * This function prepares a scan command to be sent to the firmware.
1571277b024eSKalle Valo  *
1572277b024eSKalle Valo  * This uses the scan command configuration sent to the command processing
1573277b024eSKalle Valo  * module in command preparation stage to configure a scan command structure
1574277b024eSKalle Valo  * to send to firmware.
1575277b024eSKalle Valo  *
1576277b024eSKalle Valo  * The fixed fields specifying the BSS type and BSSID filters as well as a
1577277b024eSKalle Valo  * variable number/length of TLVs are sent in the command to firmware.
1578277b024eSKalle Valo  *
1579277b024eSKalle Valo  * Preparation also includes -
1580277b024eSKalle Valo  *      - Setting command ID, and proper size
1581277b024eSKalle Valo  *      - Ensuring correct endian-ness
1582277b024eSKalle Valo  */
mwifiex_cmd_802_11_scan(struct host_cmd_ds_command * cmd,struct mwifiex_scan_cmd_config * scan_cfg)1583277b024eSKalle Valo int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
1584277b024eSKalle Valo 			    struct mwifiex_scan_cmd_config *scan_cfg)
1585277b024eSKalle Valo {
1586277b024eSKalle Valo 	struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan;
1587277b024eSKalle Valo 
1588277b024eSKalle Valo 	/* Set fixed field variables in scan command */
1589277b024eSKalle Valo 	scan_cmd->bss_mode = scan_cfg->bss_mode;
1590277b024eSKalle Valo 	memcpy(scan_cmd->bssid, scan_cfg->specific_bssid,
1591277b024eSKalle Valo 	       sizeof(scan_cmd->bssid));
1592277b024eSKalle Valo 	memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
1593277b024eSKalle Valo 
1594277b024eSKalle Valo 	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN);
1595277b024eSKalle Valo 
1596277b024eSKalle Valo 	/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
1597277b024eSKalle Valo 	cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode)
1598277b024eSKalle Valo 					  + sizeof(scan_cmd->bssid)
1599277b024eSKalle Valo 					  + scan_cfg->tlv_buf_len + S_DS_GEN));
1600277b024eSKalle Valo 
1601277b024eSKalle Valo 	return 0;
1602277b024eSKalle Valo }
1603277b024eSKalle Valo 
1604277b024eSKalle Valo /*
1605277b024eSKalle Valo  * This function checks compatibility of requested network with current
1606277b024eSKalle Valo  * driver settings.
1607277b024eSKalle Valo  */
mwifiex_check_network_compatibility(struct mwifiex_private * priv,struct mwifiex_bssdescriptor * bss_desc)1608277b024eSKalle Valo int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
1609277b024eSKalle Valo 					struct mwifiex_bssdescriptor *bss_desc)
1610277b024eSKalle Valo {
1611277b024eSKalle Valo 	int ret = -1;
1612277b024eSKalle Valo 
1613277b024eSKalle Valo 	if (!bss_desc)
1614277b024eSKalle Valo 		return -1;
1615277b024eSKalle Valo 
1616277b024eSKalle Valo 	if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band,
1617277b024eSKalle Valo 			     (u16) bss_desc->channel, 0))) {
1618277b024eSKalle Valo 		switch (priv->bss_mode) {
1619277b024eSKalle Valo 		case NL80211_IFTYPE_STATION:
1620277b024eSKalle Valo 		case NL80211_IFTYPE_ADHOC:
1621277b024eSKalle Valo 			ret = mwifiex_is_network_compatible(priv, bss_desc,
1622277b024eSKalle Valo 							    priv->bss_mode);
1623277b024eSKalle Valo 			if (ret)
1624277b024eSKalle Valo 				mwifiex_dbg(priv->adapter, ERROR,
1625277b024eSKalle Valo 					    "Incompatible network settings\n");
1626277b024eSKalle Valo 			break;
1627277b024eSKalle Valo 		default:
1628277b024eSKalle Valo 			ret = 0;
1629277b024eSKalle Valo 		}
1630277b024eSKalle Valo 	}
1631277b024eSKalle Valo 
1632277b024eSKalle Valo 	return ret;
1633277b024eSKalle Valo }
1634277b024eSKalle Valo 
1635277b024eSKalle Valo /* This function checks if SSID string contains all zeroes or length is zero */
mwifiex_is_hidden_ssid(struct cfg80211_ssid * ssid)1636277b024eSKalle Valo static bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid)
1637277b024eSKalle Valo {
1638277b024eSKalle Valo 	int idx;
1639277b024eSKalle Valo 
1640277b024eSKalle Valo 	for (idx = 0; idx < ssid->ssid_len; idx++) {
1641277b024eSKalle Valo 		if (ssid->ssid[idx])
1642277b024eSKalle Valo 			return false;
1643277b024eSKalle Valo 	}
1644277b024eSKalle Valo 
1645277b024eSKalle Valo 	return true;
1646277b024eSKalle Valo }
1647277b024eSKalle Valo 
1648277b024eSKalle Valo /* This function checks if any hidden SSID found in passive scan channels
1649277b024eSKalle Valo  * and save those channels for specific SSID active scan
1650277b024eSKalle Valo  */
mwifiex_save_hidden_ssid_channels(struct mwifiex_private * priv,struct cfg80211_bss * bss)1651277b024eSKalle Valo static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv,
1652277b024eSKalle Valo 					     struct cfg80211_bss *bss)
1653277b024eSKalle Valo {
1654277b024eSKalle Valo 	struct mwifiex_bssdescriptor *bss_desc;
1655277b024eSKalle Valo 	int ret;
1656277b024eSKalle Valo 	int chid;
1657277b024eSKalle Valo 
1658277b024eSKalle Valo 	/* Allocate and fill new bss descriptor */
1659277b024eSKalle Valo 	bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL);
1660277b024eSKalle Valo 	if (!bss_desc)
1661277b024eSKalle Valo 		return -ENOMEM;
1662277b024eSKalle Valo 
1663277b024eSKalle Valo 	ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
1664277b024eSKalle Valo 	if (ret)
1665277b024eSKalle Valo 		goto done;
1666277b024eSKalle Valo 
1667277b024eSKalle Valo 	if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) {
1668277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n");
1669277b024eSKalle Valo 		for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) {
1670277b024eSKalle Valo 			if (priv->hidden_chan[chid].chan_number ==
1671277b024eSKalle Valo 			    bss->channel->hw_value)
1672277b024eSKalle Valo 				break;
1673277b024eSKalle Valo 
1674277b024eSKalle Valo 			if (!priv->hidden_chan[chid].chan_number) {
1675277b024eSKalle Valo 				priv->hidden_chan[chid].chan_number =
1676277b024eSKalle Valo 					bss->channel->hw_value;
1677277b024eSKalle Valo 				priv->hidden_chan[chid].radio_type =
1678277b024eSKalle Valo 					bss->channel->band;
1679277b024eSKalle Valo 				priv->hidden_chan[chid].scan_type =
1680277b024eSKalle Valo 					MWIFIEX_SCAN_TYPE_ACTIVE;
1681277b024eSKalle Valo 				break;
1682277b024eSKalle Valo 			}
1683277b024eSKalle Valo 		}
1684277b024eSKalle Valo 	}
1685277b024eSKalle Valo 
1686277b024eSKalle Valo done:
16875ff26222SRicky Liang 	/* beacon_ie buffer was allocated in function
16885ff26222SRicky Liang 	 * mwifiex_fill_new_bss_desc(). Free it now.
16895ff26222SRicky Liang 	 */
16905ff26222SRicky Liang 	kfree(bss_desc->beacon_buf);
1691277b024eSKalle Valo 	kfree(bss_desc);
1692277b024eSKalle Valo 	return 0;
1693277b024eSKalle Valo }
1694277b024eSKalle Valo 
mwifiex_update_curr_bss_params(struct mwifiex_private * priv,struct cfg80211_bss * bss)1695277b024eSKalle Valo static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv,
1696277b024eSKalle Valo 					  struct cfg80211_bss *bss)
1697277b024eSKalle Valo {
1698277b024eSKalle Valo 	struct mwifiex_bssdescriptor *bss_desc;
1699277b024eSKalle Valo 	int ret;
1700277b024eSKalle Valo 
1701277b024eSKalle Valo 	/* Allocate and fill new bss descriptor */
1702277b024eSKalle Valo 	bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL);
1703277b024eSKalle Valo 	if (!bss_desc)
1704277b024eSKalle Valo 		return -ENOMEM;
1705277b024eSKalle Valo 
1706277b024eSKalle Valo 	ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
1707277b024eSKalle Valo 	if (ret)
1708277b024eSKalle Valo 		goto done;
1709277b024eSKalle Valo 
1710277b024eSKalle Valo 	ret = mwifiex_check_network_compatibility(priv, bss_desc);
1711277b024eSKalle Valo 	if (ret)
1712277b024eSKalle Valo 		goto done;
1713277b024eSKalle Valo 
17148a7f9fd8SBrian Norris 	spin_lock_bh(&priv->curr_bcn_buf_lock);
1715277b024eSKalle Valo 	/* Make a copy of current BSSID descriptor */
1716277b024eSKalle Valo 	memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc,
1717277b024eSKalle Valo 	       sizeof(priv->curr_bss_params.bss_descriptor));
1718277b024eSKalle Valo 
1719277b024eSKalle Valo 	/* The contents of beacon_ie will be copied to its own buffer
1720277b024eSKalle Valo 	 * in mwifiex_save_curr_bcn()
1721277b024eSKalle Valo 	 */
1722277b024eSKalle Valo 	mwifiex_save_curr_bcn(priv);
17238a7f9fd8SBrian Norris 	spin_unlock_bh(&priv->curr_bcn_buf_lock);
1724277b024eSKalle Valo 
1725277b024eSKalle Valo done:
1726277b024eSKalle Valo 	/* beacon_ie buffer was allocated in function
1727277b024eSKalle Valo 	 * mwifiex_fill_new_bss_desc(). Free it now.
1728277b024eSKalle Valo 	 */
1729277b024eSKalle Valo 	kfree(bss_desc->beacon_buf);
1730277b024eSKalle Valo 	kfree(bss_desc);
1731277b024eSKalle Valo 	return 0;
1732277b024eSKalle Valo }
1733277b024eSKalle Valo 
1734277b024eSKalle Valo static int
mwifiex_parse_single_response_buf(struct mwifiex_private * priv,u8 ** bss_info,u32 * bytes_left,u64 fw_tsf,u8 * radio_type,bool ext_scan,s32 rssi_val)1735277b024eSKalle Valo mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
1736277b024eSKalle Valo 				  u32 *bytes_left, u64 fw_tsf, u8 *radio_type,
1737277b024eSKalle Valo 				  bool ext_scan, s32 rssi_val)
1738277b024eSKalle Valo {
1739277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
1740277b024eSKalle Valo 	struct mwifiex_chan_freq_power *cfp;
1741277b024eSKalle Valo 	struct cfg80211_bss *bss;
1742277b024eSKalle Valo 	u8 bssid[ETH_ALEN];
1743277b024eSKalle Valo 	s32 rssi;
1744277b024eSKalle Valo 	const u8 *ie_buf;
1745277b024eSKalle Valo 	size_t ie_len;
1746277b024eSKalle Valo 	u16 channel = 0;
1747277b024eSKalle Valo 	u16 beacon_size = 0;
1748277b024eSKalle Valo 	u32 curr_bcn_bytes;
1749277b024eSKalle Valo 	u32 freq;
1750277b024eSKalle Valo 	u16 beacon_period;
1751277b024eSKalle Valo 	u16 cap_info_bitmap;
1752277b024eSKalle Valo 	u8 *current_ptr;
1753277b024eSKalle Valo 	u64 timestamp;
1754277b024eSKalle Valo 	struct mwifiex_fixed_bcn_param *bcn_param;
1755277b024eSKalle Valo 	struct mwifiex_bss_priv *bss_priv;
1756277b024eSKalle Valo 
1757277b024eSKalle Valo 	if (*bytes_left >= sizeof(beacon_size)) {
1758277b024eSKalle Valo 		/* Extract & convert beacon size from command buffer */
17595653c646SDaniel Mentz 		beacon_size = get_unaligned_le16((*bss_info));
1760277b024eSKalle Valo 		*bytes_left -= sizeof(beacon_size);
1761277b024eSKalle Valo 		*bss_info += sizeof(beacon_size);
1762277b024eSKalle Valo 	}
1763277b024eSKalle Valo 
1764277b024eSKalle Valo 	if (!beacon_size || beacon_size > *bytes_left) {
1765277b024eSKalle Valo 		*bss_info += *bytes_left;
1766277b024eSKalle Valo 		*bytes_left = 0;
1767277b024eSKalle Valo 		return -EFAULT;
1768277b024eSKalle Valo 	}
1769277b024eSKalle Valo 
1770277b024eSKalle Valo 	/* Initialize the current working beacon pointer for this BSS
1771277b024eSKalle Valo 	 * iteration
1772277b024eSKalle Valo 	 */
1773277b024eSKalle Valo 	current_ptr = *bss_info;
1774277b024eSKalle Valo 
1775277b024eSKalle Valo 	/* Advance the return beacon pointer past the current beacon */
1776277b024eSKalle Valo 	*bss_info += beacon_size;
1777277b024eSKalle Valo 	*bytes_left -= beacon_size;
1778277b024eSKalle Valo 
1779277b024eSKalle Valo 	curr_bcn_bytes = beacon_size;
1780277b024eSKalle Valo 
1781277b024eSKalle Valo 	/* First 5 fields are bssid, RSSI(for legacy scan only),
1782277b024eSKalle Valo 	 * time stamp, beacon interval, and capability information
1783277b024eSKalle Valo 	 */
1784277b024eSKalle Valo 	if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) +
1785277b024eSKalle Valo 	    sizeof(struct mwifiex_fixed_bcn_param)) {
1786277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
1787277b024eSKalle Valo 			    "InterpretIE: not enough bytes left\n");
1788277b024eSKalle Valo 		return -EFAULT;
1789277b024eSKalle Valo 	}
1790277b024eSKalle Valo 
1791277b024eSKalle Valo 	memcpy(bssid, current_ptr, ETH_ALEN);
1792277b024eSKalle Valo 	current_ptr += ETH_ALEN;
1793277b024eSKalle Valo 	curr_bcn_bytes -= ETH_ALEN;
1794277b024eSKalle Valo 
1795277b024eSKalle Valo 	if (!ext_scan) {
1796277b024eSKalle Valo 		rssi = (s32) *current_ptr;
1797277b024eSKalle Valo 		rssi = (-rssi) * 100;		/* Convert dBm to mBm */
1798277b024eSKalle Valo 		current_ptr += sizeof(u8);
1799277b024eSKalle Valo 		curr_bcn_bytes -= sizeof(u8);
1800277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
1801277b024eSKalle Valo 			    "info: InterpretIE: RSSI=%d\n", rssi);
1802277b024eSKalle Valo 	} else {
1803277b024eSKalle Valo 		rssi = rssi_val;
1804277b024eSKalle Valo 	}
1805277b024eSKalle Valo 
1806277b024eSKalle Valo 	bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr;
1807277b024eSKalle Valo 	current_ptr += sizeof(*bcn_param);
1808277b024eSKalle Valo 	curr_bcn_bytes -= sizeof(*bcn_param);
1809277b024eSKalle Valo 
1810277b024eSKalle Valo 	timestamp = le64_to_cpu(bcn_param->timestamp);
1811277b024eSKalle Valo 	beacon_period = le16_to_cpu(bcn_param->beacon_period);
1812277b024eSKalle Valo 
1813277b024eSKalle Valo 	cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
1814277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
1815277b024eSKalle Valo 		    "info: InterpretIE: capabilities=0x%X\n",
1816277b024eSKalle Valo 		    cap_info_bitmap);
1817277b024eSKalle Valo 
1818277b024eSKalle Valo 	/* Rest of the current buffer are IE's */
1819277b024eSKalle Valo 	ie_buf = current_ptr;
1820277b024eSKalle Valo 	ie_len = curr_bcn_bytes;
1821277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
1822277b024eSKalle Valo 		    "info: InterpretIE: IELength for this AP = %d\n",
1823277b024eSKalle Valo 		    curr_bcn_bytes);
1824277b024eSKalle Valo 
1825277b024eSKalle Valo 	while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
1826277b024eSKalle Valo 		u8 element_id, element_len;
1827277b024eSKalle Valo 
1828277b024eSKalle Valo 		element_id = *current_ptr;
1829277b024eSKalle Valo 		element_len = *(current_ptr + 1);
1830277b024eSKalle Valo 		if (curr_bcn_bytes < element_len +
1831277b024eSKalle Valo 				sizeof(struct ieee_types_header)) {
1832277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
1833277b024eSKalle Valo 				    "%s: bytes left < IE length\n", __func__);
1834277b024eSKalle Valo 			return -EFAULT;
1835277b024eSKalle Valo 		}
1836277b024eSKalle Valo 		if (element_id == WLAN_EID_DS_PARAMS) {
1837277b024eSKalle Valo 			channel = *(current_ptr +
1838277b024eSKalle Valo 				    sizeof(struct ieee_types_header));
1839277b024eSKalle Valo 			break;
1840277b024eSKalle Valo 		}
1841277b024eSKalle Valo 
1842277b024eSKalle Valo 		current_ptr += element_len + sizeof(struct ieee_types_header);
1843277b024eSKalle Valo 		curr_bcn_bytes -= element_len +
1844277b024eSKalle Valo 					sizeof(struct ieee_types_header);
1845277b024eSKalle Valo 	}
1846277b024eSKalle Valo 
1847277b024eSKalle Valo 	if (channel) {
1848277b024eSKalle Valo 		struct ieee80211_channel *chan;
1849277b024eSKalle Valo 		u8 band;
1850277b024eSKalle Valo 
1851277b024eSKalle Valo 		/* Skip entry if on csa closed channel */
1852277b024eSKalle Valo 		if (channel == priv->csa_chan) {
1853277b024eSKalle Valo 			mwifiex_dbg(adapter, WARN,
1854277b024eSKalle Valo 				    "Dropping entry on csa closed channel\n");
1855277b024eSKalle Valo 			return 0;
1856277b024eSKalle Valo 		}
1857277b024eSKalle Valo 
1858277b024eSKalle Valo 		band = BAND_G;
1859277b024eSKalle Valo 		if (radio_type)
1860277b024eSKalle Valo 			band = mwifiex_radio_type_to_band(*radio_type &
1861277b024eSKalle Valo 							  (BIT(0) | BIT(1)));
1862277b024eSKalle Valo 
1863277b024eSKalle Valo 		cfp = mwifiex_get_cfp(priv, band, channel, 0);
1864277b024eSKalle Valo 
1865277b024eSKalle Valo 		freq = cfp ? cfp->freq : 0;
1866277b024eSKalle Valo 
1867277b024eSKalle Valo 		chan = ieee80211_get_channel(priv->wdev.wiphy, freq);
1868277b024eSKalle Valo 
1869277b024eSKalle Valo 		if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
1870277b024eSKalle Valo 			bss = cfg80211_inform_bss(priv->wdev.wiphy,
1871277b024eSKalle Valo 					    chan, CFG80211_BSS_FTYPE_UNKNOWN,
1872277b024eSKalle Valo 					    bssid, timestamp,
1873277b024eSKalle Valo 					    cap_info_bitmap, beacon_period,
1874d2ab7f00SChristophe JAILLET 					    ie_buf, ie_len, rssi, GFP_ATOMIC);
1875277b024eSKalle Valo 			if (bss) {
1876277b024eSKalle Valo 				bss_priv = (struct mwifiex_bss_priv *)bss->priv;
1877277b024eSKalle Valo 				bss_priv->band = band;
1878277b024eSKalle Valo 				bss_priv->fw_tsf = fw_tsf;
1879277b024eSKalle Valo 				if (priv->media_connected &&
1880277b024eSKalle Valo 				    !memcmp(bssid, priv->curr_bss_params.
1881277b024eSKalle Valo 					    bss_descriptor.mac_address,
1882277b024eSKalle Valo 					    ETH_ALEN))
1883277b024eSKalle Valo 					mwifiex_update_curr_bss_params(priv,
1884277b024eSKalle Valo 								       bss);
1885277b024eSKalle Valo 
1886277b024eSKalle Valo 				if ((chan->flags & IEEE80211_CHAN_RADAR) ||
1887277b024eSKalle Valo 				    (chan->flags & IEEE80211_CHAN_NO_IR)) {
1888277b024eSKalle Valo 					mwifiex_dbg(adapter, INFO,
1889277b024eSKalle Valo 						    "radar or passive channel %d\n",
1890277b024eSKalle Valo 						    channel);
18911dcd9429SPan Bian 					mwifiex_save_hidden_ssid_channels(priv,
18921dcd9429SPan Bian 									  bss);
18931dcd9429SPan Bian 				}
18941dcd9429SPan Bian 
18951dcd9429SPan Bian 				cfg80211_put_bss(priv->wdev.wiphy, bss);
1896277b024eSKalle Valo 			}
1897277b024eSKalle Valo 		}
1898277b024eSKalle Valo 	} else {
1899277b024eSKalle Valo 		mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n");
1900277b024eSKalle Valo 	}
1901277b024eSKalle Valo 
1902277b024eSKalle Valo 	return 0;
1903277b024eSKalle Valo }
1904277b024eSKalle Valo 
mwifiex_complete_scan(struct mwifiex_private * priv)1905277b024eSKalle Valo static void mwifiex_complete_scan(struct mwifiex_private *priv)
1906277b024eSKalle Valo {
1907277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
1908277b024eSKalle Valo 
1909277b024eSKalle Valo 	adapter->survey_idx = 0;
1910277b024eSKalle Valo 	if (adapter->curr_cmd->wait_q_enabled) {
1911277b024eSKalle Valo 		adapter->cmd_wait_q.status = 0;
1912277b024eSKalle Valo 		if (!priv->scan_request) {
1913277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
1914277b024eSKalle Valo 				    "complete internal scan\n");
1915277b024eSKalle Valo 			mwifiex_complete_cmd(adapter, adapter->curr_cmd);
1916277b024eSKalle Valo 		}
1917277b024eSKalle Valo 	}
1918277b024eSKalle Valo }
1919277b024eSKalle Valo 
1920277b024eSKalle Valo /* This function checks if any hidden SSID found in passive scan channels
1921277b024eSKalle Valo  * and do specific SSID active scan for those channels
1922277b024eSKalle Valo  */
1923277b024eSKalle Valo static int
mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private * priv)1924277b024eSKalle Valo mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv)
1925277b024eSKalle Valo {
1926277b024eSKalle Valo 	int ret;
1927277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
1928277b024eSKalle Valo 	u8 id = 0;
1929277b024eSKalle Valo 	struct mwifiex_user_scan_cfg  *user_scan_cfg;
1930277b024eSKalle Valo 
193116d25da9SAmitkumar Karwar 	if (adapter->active_scan_triggered || !priv->scan_request ||
193216d25da9SAmitkumar Karwar 	    priv->scan_aborting) {
1933277b024eSKalle Valo 		adapter->active_scan_triggered = false;
1934277b024eSKalle Valo 		return 0;
1935277b024eSKalle Valo 	}
1936277b024eSKalle Valo 
1937277b024eSKalle Valo 	if (!priv->hidden_chan[0].chan_number) {
1938277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n");
1939277b024eSKalle Valo 		return 0;
1940277b024eSKalle Valo 	}
1941277b024eSKalle Valo 	user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
1942277b024eSKalle Valo 
1943277b024eSKalle Valo 	if (!user_scan_cfg)
1944277b024eSKalle Valo 		return -ENOMEM;
1945277b024eSKalle Valo 
1946277b024eSKalle Valo 	for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) {
1947277b024eSKalle Valo 		if (!priv->hidden_chan[id].chan_number)
1948277b024eSKalle Valo 			break;
1949277b024eSKalle Valo 		memcpy(&user_scan_cfg->chan_list[id],
1950277b024eSKalle Valo 		       &priv->hidden_chan[id],
1951277b024eSKalle Valo 		       sizeof(struct mwifiex_user_scan_chan));
1952277b024eSKalle Valo 	}
1953277b024eSKalle Valo 
1954277b024eSKalle Valo 	adapter->active_scan_triggered = true;
195589001c1cSGanapathi Bhat 	if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
1956e251a882SGanapathi Bhat 		ether_addr_copy(user_scan_cfg->random_mac,
1957e251a882SGanapathi Bhat 				priv->scan_request->mac_addr);
1958277b024eSKalle Valo 	user_scan_cfg->num_ssids = priv->scan_request->n_ssids;
1959277b024eSKalle Valo 	user_scan_cfg->ssid_list = priv->scan_request->ssids;
1960277b024eSKalle Valo 
1961277b024eSKalle Valo 	ret = mwifiex_scan_networks(priv, user_scan_cfg);
1962277b024eSKalle Valo 	kfree(user_scan_cfg);
1963277b024eSKalle Valo 
1964277b024eSKalle Valo 	memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan));
1965277b024eSKalle Valo 
1966277b024eSKalle Valo 	if (ret) {
1967277b024eSKalle Valo 		dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
1968277b024eSKalle Valo 		return ret;
1969277b024eSKalle Valo 	}
1970277b024eSKalle Valo 
1971277b024eSKalle Valo 	return 0;
1972277b024eSKalle Valo }
mwifiex_check_next_scan_command(struct mwifiex_private * priv)1973277b024eSKalle Valo static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
1974277b024eSKalle Valo {
1975277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
1976c70ca8cbSAndreas Fenkart 	struct cmd_ctrl_node *cmd_node;
1977277b024eSKalle Valo 
19788a7f9fd8SBrian Norris 	spin_lock_bh(&adapter->scan_pending_q_lock);
1979277b024eSKalle Valo 	if (list_empty(&adapter->scan_pending_q)) {
19808a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->scan_pending_q_lock);
1981c70ca8cbSAndreas Fenkart 
19828a7f9fd8SBrian Norris 		spin_lock_bh(&adapter->mwifiex_cmd_lock);
1983277b024eSKalle Valo 		adapter->scan_processing = false;
19848a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
1985277b024eSKalle Valo 
1986277b024eSKalle Valo 		mwifiex_active_scan_req_for_passive_chan(priv);
1987277b024eSKalle Valo 
1988277b024eSKalle Valo 		if (!adapter->ext_scan)
1989277b024eSKalle Valo 			mwifiex_complete_scan(priv);
1990277b024eSKalle Valo 
1991277b024eSKalle Valo 		if (priv->scan_request) {
19921d76250bSAvraham Stern 			struct cfg80211_scan_info info = {
19931d76250bSAvraham Stern 				.aborted = false,
19941d76250bSAvraham Stern 			};
19951d76250bSAvraham Stern 
1996277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
1997277b024eSKalle Valo 				    "info: notifying scan done\n");
19981d76250bSAvraham Stern 			cfg80211_scan_done(priv->scan_request, &info);
1999277b024eSKalle Valo 			priv->scan_request = NULL;
200009e672a1SAmitkumar Karwar 			priv->scan_aborting = false;
2001277b024eSKalle Valo 		} else {
2002277b024eSKalle Valo 			priv->scan_aborting = false;
2003277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
2004277b024eSKalle Valo 				    "info: scan already aborted\n");
2005277b024eSKalle Valo 		}
2006277b024eSKalle Valo 	} else if ((priv->scan_aborting && !priv->scan_request) ||
2007277b024eSKalle Valo 		   priv->scan_block) {
20088a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->scan_pending_q_lock);
2009277b024eSKalle Valo 
2010c70ca8cbSAndreas Fenkart 		mwifiex_cancel_pending_scan_cmd(adapter);
2011c70ca8cbSAndreas Fenkart 
20128a7f9fd8SBrian Norris 		spin_lock_bh(&adapter->mwifiex_cmd_lock);
2013277b024eSKalle Valo 		adapter->scan_processing = false;
20148a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
2015277b024eSKalle Valo 
2016277b024eSKalle Valo 		if (!adapter->active_scan_triggered) {
2017277b024eSKalle Valo 			if (priv->scan_request) {
20181d76250bSAvraham Stern 				struct cfg80211_scan_info info = {
20191d76250bSAvraham Stern 					.aborted = true,
20201d76250bSAvraham Stern 				};
20211d76250bSAvraham Stern 
2022277b024eSKalle Valo 				mwifiex_dbg(adapter, INFO,
2023277b024eSKalle Valo 					    "info: aborting scan\n");
20241d76250bSAvraham Stern 				cfg80211_scan_done(priv->scan_request, &info);
2025277b024eSKalle Valo 				priv->scan_request = NULL;
202609e672a1SAmitkumar Karwar 				priv->scan_aborting = false;
2027277b024eSKalle Valo 			} else {
2028277b024eSKalle Valo 				priv->scan_aborting = false;
2029277b024eSKalle Valo 				mwifiex_dbg(adapter, INFO,
2030277b024eSKalle Valo 					    "info: scan already aborted\n");
2031277b024eSKalle Valo 			}
2032277b024eSKalle Valo 		}
2033277b024eSKalle Valo 	} else {
2034277b024eSKalle Valo 		/* Get scan command from scan_pending_q and put to
2035277b024eSKalle Valo 		 * cmd_pending_q
2036277b024eSKalle Valo 		 */
2037277b024eSKalle Valo 		cmd_node = list_first_entry(&adapter->scan_pending_q,
2038277b024eSKalle Valo 					    struct cmd_ctrl_node, list);
2039277b024eSKalle Valo 		list_del(&cmd_node->list);
20408a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->scan_pending_q_lock);
2041463df471SBrian Norris 		mwifiex_insert_cmd_to_pending_q(adapter, cmd_node);
2042277b024eSKalle Valo 	}
2043277b024eSKalle Valo 
2044277b024eSKalle Valo 	return;
2045277b024eSKalle Valo }
2046277b024eSKalle Valo 
mwifiex_cancel_scan(struct mwifiex_adapter * adapter)2047a9c790baSXinming Hu void mwifiex_cancel_scan(struct mwifiex_adapter *adapter)
2048a9c790baSXinming Hu {
2049a9c790baSXinming Hu 	struct mwifiex_private *priv;
2050a9c790baSXinming Hu 	int i;
2051a9c790baSXinming Hu 
2052a9c790baSXinming Hu 	mwifiex_cancel_pending_scan_cmd(adapter);
2053a9c790baSXinming Hu 
2054a9c790baSXinming Hu 	if (adapter->scan_processing) {
20558a7f9fd8SBrian Norris 		spin_lock_bh(&adapter->mwifiex_cmd_lock);
2056a9c790baSXinming Hu 		adapter->scan_processing = false;
20578a7f9fd8SBrian Norris 		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
2058a9c790baSXinming Hu 		for (i = 0; i < adapter->priv_num; i++) {
2059a9c790baSXinming Hu 			priv = adapter->priv[i];
2060a9c790baSXinming Hu 			if (!priv)
2061a9c790baSXinming Hu 				continue;
2062a9c790baSXinming Hu 			if (priv->scan_request) {
206388b3ec52SDavid S. Miller 				struct cfg80211_scan_info info = {
206488b3ec52SDavid S. Miller 					.aborted = true,
206588b3ec52SDavid S. Miller 				};
206688b3ec52SDavid S. Miller 
2067a9c790baSXinming Hu 				mwifiex_dbg(adapter, INFO,
2068a9c790baSXinming Hu 					    "info: aborting scan\n");
206988b3ec52SDavid S. Miller 				cfg80211_scan_done(priv->scan_request, &info);
2070a9c790baSXinming Hu 				priv->scan_request = NULL;
207109e672a1SAmitkumar Karwar 				priv->scan_aborting = false;
2072a9c790baSXinming Hu 			}
2073a9c790baSXinming Hu 		}
2074a9c790baSXinming Hu 	}
2075a9c790baSXinming Hu }
2076a9c790baSXinming Hu 
2077277b024eSKalle Valo /*
2078277b024eSKalle Valo  * This function handles the command response of scan.
2079277b024eSKalle Valo  *
2080277b024eSKalle Valo  * The response buffer for the scan command has the following
2081277b024eSKalle Valo  * memory layout:
2082277b024eSKalle Valo  *
2083277b024eSKalle Valo  *      .-------------------------------------------------------------.
2084277b024eSKalle Valo  *      |  Header (4 * sizeof(t_u16)):  Standard command response hdr |
2085277b024eSKalle Valo  *      .-------------------------------------------------------------.
2086277b024eSKalle Valo  *      |  BufSize (t_u16) : sizeof the BSS Description data          |
2087277b024eSKalle Valo  *      .-------------------------------------------------------------.
2088277b024eSKalle Valo  *      |  NumOfSet (t_u8) : Number of BSS Descs returned             |
2089277b024eSKalle Valo  *      .-------------------------------------------------------------.
2090277b024eSKalle Valo  *      |  BSSDescription data (variable, size given in BufSize)      |
2091277b024eSKalle Valo  *      .-------------------------------------------------------------.
2092277b024eSKalle Valo  *      |  TLV data (variable, size calculated using Header->Size,    |
2093277b024eSKalle Valo  *      |            BufSize and sizeof the fixed fields above)       |
2094277b024eSKalle Valo  *      .-------------------------------------------------------------.
2095277b024eSKalle Valo  */
mwifiex_ret_802_11_scan(struct mwifiex_private * priv,struct host_cmd_ds_command * resp)2096277b024eSKalle Valo int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
2097277b024eSKalle Valo 			    struct host_cmd_ds_command *resp)
2098277b024eSKalle Valo {
2099277b024eSKalle Valo 	int ret = 0;
2100277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
2101277b024eSKalle Valo 	struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
2102277b024eSKalle Valo 	struct mwifiex_ie_types_data *tlv_data;
2103277b024eSKalle Valo 	struct mwifiex_ie_types_tsf_timestamp *tsf_tlv;
2104277b024eSKalle Valo 	u8 *bss_info;
2105277b024eSKalle Valo 	u32 scan_resp_size;
2106277b024eSKalle Valo 	u32 bytes_left;
2107277b024eSKalle Valo 	u32 idx;
2108277b024eSKalle Valo 	u32 tlv_buf_size;
2109277b024eSKalle Valo 	struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv;
2110277b024eSKalle Valo 	struct chan_band_param_set *chan_band;
2111277b024eSKalle Valo 	u8 is_bgscan_resp;
2112277b024eSKalle Valo 	__le64 fw_tsf = 0;
2113277b024eSKalle Valo 	u8 *radio_type;
21147d7f07d8Schunfan chen 	struct cfg80211_wowlan_nd_match *pmatch;
21157d7f07d8Schunfan chen 	struct cfg80211_sched_scan_request *nd_config = NULL;
2116277b024eSKalle Valo 
2117277b024eSKalle Valo 	is_bgscan_resp = (le16_to_cpu(resp->command)
2118277b024eSKalle Valo 			  == HostCmd_CMD_802_11_BG_SCAN_QUERY);
2119277b024eSKalle Valo 	if (is_bgscan_resp)
2120277b024eSKalle Valo 		scan_rsp = &resp->params.bg_scan_query_resp.scan_resp;
2121277b024eSKalle Valo 	else
2122277b024eSKalle Valo 		scan_rsp = &resp->params.scan_resp;
2123277b024eSKalle Valo 
2124277b024eSKalle Valo 
2125277b024eSKalle Valo 	if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) {
2126277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
2127277b024eSKalle Valo 			    "SCAN_RESP: too many AP returned (%d)\n",
2128277b024eSKalle Valo 			    scan_rsp->number_of_sets);
2129277b024eSKalle Valo 		ret = -1;
2130277b024eSKalle Valo 		goto check_next_scan;
2131277b024eSKalle Valo 	}
2132277b024eSKalle Valo 
2133277b024eSKalle Valo 	/* Check csa channel expiry before parsing scan response */
2134277b024eSKalle Valo 	mwifiex_11h_get_csa_closed_channel(priv);
2135277b024eSKalle Valo 
2136277b024eSKalle Valo 	bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
2137277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
2138277b024eSKalle Valo 		    "info: SCAN_RESP: bss_descript_size %d\n",
2139277b024eSKalle Valo 		    bytes_left);
2140277b024eSKalle Valo 
2141277b024eSKalle Valo 	scan_resp_size = le16_to_cpu(resp->size);
2142277b024eSKalle Valo 
2143277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
2144277b024eSKalle Valo 		    "info: SCAN_RESP: returned %d APs before parsing\n",
2145277b024eSKalle Valo 		    scan_rsp->number_of_sets);
2146277b024eSKalle Valo 
2147277b024eSKalle Valo 	bss_info = scan_rsp->bss_desc_and_tlv_buffer;
2148277b024eSKalle Valo 
2149277b024eSKalle Valo 	/*
2150277b024eSKalle Valo 	 * The size of the TLV buffer is equal to the entire command response
2151277b024eSKalle Valo 	 *   size (scan_resp_size) minus the fixed fields (sizeof()'s), the
2152277b024eSKalle Valo 	 *   BSS Descriptions (bss_descript_size as bytesLef) and the command
2153277b024eSKalle Valo 	 *   response header (S_DS_GEN)
2154277b024eSKalle Valo 	 */
2155277b024eSKalle Valo 	tlv_buf_size = scan_resp_size - (bytes_left
2156277b024eSKalle Valo 					 + sizeof(scan_rsp->bss_descript_size)
2157277b024eSKalle Valo 					 + sizeof(scan_rsp->number_of_sets)
2158277b024eSKalle Valo 					 + S_DS_GEN);
2159277b024eSKalle Valo 
2160277b024eSKalle Valo 	tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp->
2161277b024eSKalle Valo 						 bss_desc_and_tlv_buffer +
2162277b024eSKalle Valo 						 bytes_left);
2163277b024eSKalle Valo 
2164277b024eSKalle Valo 	/* Search the TLV buffer space in the scan response for any valid
2165277b024eSKalle Valo 	   TLVs */
2166277b024eSKalle Valo 	mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size,
2167277b024eSKalle Valo 					     TLV_TYPE_TSFTIMESTAMP,
2168277b024eSKalle Valo 					     (struct mwifiex_ie_types_data **)
2169277b024eSKalle Valo 					     &tsf_tlv);
2170277b024eSKalle Valo 
2171277b024eSKalle Valo 	/* Search the TLV buffer space in the scan response for any valid
2172277b024eSKalle Valo 	   TLVs */
2173277b024eSKalle Valo 	mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size,
2174277b024eSKalle Valo 					     TLV_TYPE_CHANNELBANDLIST,
2175277b024eSKalle Valo 					     (struct mwifiex_ie_types_data **)
2176277b024eSKalle Valo 					     &chan_band_tlv);
2177277b024eSKalle Valo 
21787d7f07d8Schunfan chen #ifdef CONFIG_PM
21797d7f07d8Schunfan chen 	if (priv->wdev.wiphy->wowlan_config)
21807d7f07d8Schunfan chen 		nd_config = priv->wdev.wiphy->wowlan_config->nd_config;
21817d7f07d8Schunfan chen #endif
21827d7f07d8Schunfan chen 
21837d7f07d8Schunfan chen 	if (nd_config) {
21847d7f07d8Schunfan chen 		adapter->nd_info =
2185d9aef04fSChristophe JAILLET 			kzalloc(struct_size(adapter->nd_info, matches,
2186d9aef04fSChristophe JAILLET 					    scan_rsp->number_of_sets),
2187d9aef04fSChristophe JAILLET 				GFP_ATOMIC);
21887d7f07d8Schunfan chen 
21897d7f07d8Schunfan chen 		if (adapter->nd_info)
21907d7f07d8Schunfan chen 			adapter->nd_info->n_matches = scan_rsp->number_of_sets;
21917d7f07d8Schunfan chen 	}
21927d7f07d8Schunfan chen 
2193277b024eSKalle Valo 	for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
2194277b024eSKalle Valo 		/*
2195277b024eSKalle Valo 		 * If the TSF TLV was appended to the scan results, save this
2196277b024eSKalle Valo 		 * entry's TSF value in the fw_tsf field. It is the firmware's
2197277b024eSKalle Valo 		 * TSF value at the time the beacon or probe response was
2198277b024eSKalle Valo 		 * received.
2199277b024eSKalle Valo 		 */
2200277b024eSKalle Valo 		if (tsf_tlv)
2201277b024eSKalle Valo 			memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
2202277b024eSKalle Valo 			       sizeof(fw_tsf));
2203277b024eSKalle Valo 
2204277b024eSKalle Valo 		if (chan_band_tlv) {
2205277b024eSKalle Valo 			chan_band = &chan_band_tlv->chan_band_param[idx];
2206277b024eSKalle Valo 			radio_type = &chan_band->radio_type;
2207277b024eSKalle Valo 		} else {
2208277b024eSKalle Valo 			radio_type = NULL;
2209277b024eSKalle Valo 		}
2210277b024eSKalle Valo 
22117d7f07d8Schunfan chen 		if (chan_band_tlv && adapter->nd_info) {
22127d7f07d8Schunfan chen 			adapter->nd_info->matches[idx] =
2213b7116576SChristophe Jaillet 				kzalloc(sizeof(*pmatch) + sizeof(u32),
2214b7116576SChristophe Jaillet 					GFP_ATOMIC);
22157d7f07d8Schunfan chen 
22167d7f07d8Schunfan chen 			pmatch = adapter->nd_info->matches[idx];
22177d7f07d8Schunfan chen 
2218efdf0e39SDan Carpenter 			if (pmatch) {
22197d7f07d8Schunfan chen 				pmatch->n_channels = 1;
2220b7116576SChristophe Jaillet 				pmatch->channels[0] = chan_band->chan_number;
22217d7f07d8Schunfan chen 			}
22227d7f07d8Schunfan chen 		}
22237d7f07d8Schunfan chen 
2224277b024eSKalle Valo 		ret = mwifiex_parse_single_response_buf(priv, &bss_info,
2225277b024eSKalle Valo 							&bytes_left,
2226277b024eSKalle Valo 							le64_to_cpu(fw_tsf),
2227277b024eSKalle Valo 							radio_type, false, 0);
2228277b024eSKalle Valo 		if (ret)
2229277b024eSKalle Valo 			goto check_next_scan;
2230277b024eSKalle Valo 	}
2231277b024eSKalle Valo 
2232277b024eSKalle Valo check_next_scan:
2233277b024eSKalle Valo 	mwifiex_check_next_scan_command(priv);
2234277b024eSKalle Valo 	return ret;
2235277b024eSKalle Valo }
2236277b024eSKalle Valo 
2237277b024eSKalle Valo /*
2238277b024eSKalle Valo  * This function prepares an extended scan command to be sent to the firmware
2239277b024eSKalle Valo  *
2240277b024eSKalle Valo  * This uses the scan command configuration sent to the command processing
2241277b024eSKalle Valo  * module in command preparation stage to configure a extended scan command
2242277b024eSKalle Valo  * structure to send to firmware.
2243277b024eSKalle Valo  */
mwifiex_cmd_802_11_scan_ext(struct mwifiex_private * priv,struct host_cmd_ds_command * cmd,void * data_buf)2244277b024eSKalle Valo int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
2245277b024eSKalle Valo 				struct host_cmd_ds_command *cmd,
2246277b024eSKalle Valo 				void *data_buf)
2247277b024eSKalle Valo {
2248277b024eSKalle Valo 	struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan;
2249277b024eSKalle Valo 	struct mwifiex_scan_cmd_config *scan_cfg = data_buf;
2250277b024eSKalle Valo 
2251277b024eSKalle Valo 	memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
2252277b024eSKalle Valo 
2253277b024eSKalle Valo 	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT);
2254277b024eSKalle Valo 
2255277b024eSKalle Valo 	/* Size is equal to the sizeof(fixed portions) + the TLV len + header */
2256277b024eSKalle Valo 	cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved)
2257277b024eSKalle Valo 				      + scan_cfg->tlv_buf_len + S_DS_GEN));
2258277b024eSKalle Valo 
2259277b024eSKalle Valo 	return 0;
2260277b024eSKalle Valo }
2261277b024eSKalle Valo 
22620c9b7f22SXinming Hu /* This function prepares an background scan config command to be sent
22630c9b7f22SXinming Hu  * to the firmware
22640c9b7f22SXinming Hu  */
mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private * priv,struct host_cmd_ds_command * cmd,void * data_buf)22650c9b7f22SXinming Hu int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv,
22660c9b7f22SXinming Hu 				      struct host_cmd_ds_command *cmd,
22670c9b7f22SXinming Hu 				      void *data_buf)
22680c9b7f22SXinming Hu {
22690c9b7f22SXinming Hu 	struct host_cmd_ds_802_11_bg_scan_config *bgscan_config =
22700c9b7f22SXinming Hu 					&cmd->params.bg_scan_config;
22710c9b7f22SXinming Hu 	struct mwifiex_bg_scan_cfg *bgscan_cfg_in = data_buf;
22720c9b7f22SXinming Hu 	u8 *tlv_pos = bgscan_config->tlv;
22730c9b7f22SXinming Hu 	u8 num_probes;
22740c9b7f22SXinming Hu 	u32 ssid_len, chan_idx, scan_type, scan_dur, chan_num;
22750c9b7f22SXinming Hu 	int i;
22760c9b7f22SXinming Hu 	struct mwifiex_ie_types_num_probes *num_probes_tlv;
22770c9b7f22SXinming Hu 	struct mwifiex_ie_types_repeat_count *repeat_count_tlv;
2278fdcab083SGanapathi Bhat 	struct mwifiex_ie_types_min_rssi_threshold *rssi_threshold_tlv;
22790c9b7f22SXinming Hu 	struct mwifiex_ie_types_bgscan_start_later *start_later_tlv;
22800c9b7f22SXinming Hu 	struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
22810c9b7f22SXinming Hu 	struct mwifiex_ie_types_chan_list_param_set *chan_list_tlv;
22820c9b7f22SXinming Hu 	struct mwifiex_chan_scan_param_set *temp_chan;
22830c9b7f22SXinming Hu 
22840c9b7f22SXinming Hu 	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG);
22850c9b7f22SXinming Hu 	cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN);
22860c9b7f22SXinming Hu 
22870c9b7f22SXinming Hu 	bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action);
22880c9b7f22SXinming Hu 	bgscan_config->enable = bgscan_cfg_in->enable;
22890c9b7f22SXinming Hu 	bgscan_config->bss_type = bgscan_cfg_in->bss_type;
22900c9b7f22SXinming Hu 	bgscan_config->scan_interval =
22910c9b7f22SXinming Hu 		cpu_to_le32(bgscan_cfg_in->scan_interval);
22920c9b7f22SXinming Hu 	bgscan_config->report_condition =
22930c9b7f22SXinming Hu 		cpu_to_le32(bgscan_cfg_in->report_condition);
22940c9b7f22SXinming Hu 
22950c9b7f22SXinming Hu 	/*  stop sched scan  */
22960c9b7f22SXinming Hu 	if (!bgscan_config->enable)
22970c9b7f22SXinming Hu 		return 0;
22980c9b7f22SXinming Hu 
22990c9b7f22SXinming Hu 	bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan;
23000c9b7f22SXinming Hu 
23010c9b7f22SXinming Hu 	num_probes = (bgscan_cfg_in->num_probes ? bgscan_cfg_in->
23020c9b7f22SXinming Hu 		      num_probes : priv->adapter->scan_probes);
23030c9b7f22SXinming Hu 
23040c9b7f22SXinming Hu 	if (num_probes) {
23050c9b7f22SXinming Hu 		num_probes_tlv = (struct mwifiex_ie_types_num_probes *)tlv_pos;
23060c9b7f22SXinming Hu 		num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
23070c9b7f22SXinming Hu 		num_probes_tlv->header.len =
23080c9b7f22SXinming Hu 			cpu_to_le16(sizeof(num_probes_tlv->num_probes));
23090c9b7f22SXinming Hu 		num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes);
23100c9b7f22SXinming Hu 
23110c9b7f22SXinming Hu 		tlv_pos += sizeof(num_probes_tlv->header) +
23120c9b7f22SXinming Hu 			le16_to_cpu(num_probes_tlv->header.len);
23130c9b7f22SXinming Hu 	}
23140c9b7f22SXinming Hu 
23150c9b7f22SXinming Hu 	if (bgscan_cfg_in->repeat_count) {
23160c9b7f22SXinming Hu 		repeat_count_tlv =
23170c9b7f22SXinming Hu 			(struct mwifiex_ie_types_repeat_count *)tlv_pos;
23180c9b7f22SXinming Hu 		repeat_count_tlv->header.type =
23190c9b7f22SXinming Hu 			cpu_to_le16(TLV_TYPE_REPEAT_COUNT);
23200c9b7f22SXinming Hu 		repeat_count_tlv->header.len =
23210c9b7f22SXinming Hu 			cpu_to_le16(sizeof(repeat_count_tlv->repeat_count));
23220c9b7f22SXinming Hu 		repeat_count_tlv->repeat_count =
23230c9b7f22SXinming Hu 			cpu_to_le16(bgscan_cfg_in->repeat_count);
23240c9b7f22SXinming Hu 
23250c9b7f22SXinming Hu 		tlv_pos += sizeof(repeat_count_tlv->header) +
23260c9b7f22SXinming Hu 			le16_to_cpu(repeat_count_tlv->header.len);
23270c9b7f22SXinming Hu 	}
23280c9b7f22SXinming Hu 
2329fdcab083SGanapathi Bhat 	if (bgscan_cfg_in->rssi_threshold) {
2330fdcab083SGanapathi Bhat 		rssi_threshold_tlv =
2331fdcab083SGanapathi Bhat 			(struct mwifiex_ie_types_min_rssi_threshold *)tlv_pos;
2332fdcab083SGanapathi Bhat 		rssi_threshold_tlv->header.type =
2333fdcab083SGanapathi Bhat 			cpu_to_le16(TLV_TYPE_RSSI_LOW);
2334fdcab083SGanapathi Bhat 		rssi_threshold_tlv->header.len =
2335fdcab083SGanapathi Bhat 			cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold));
2336fdcab083SGanapathi Bhat 		rssi_threshold_tlv->rssi_threshold =
2337fdcab083SGanapathi Bhat 			cpu_to_le16(bgscan_cfg_in->rssi_threshold);
2338fdcab083SGanapathi Bhat 
2339fdcab083SGanapathi Bhat 		tlv_pos += sizeof(rssi_threshold_tlv->header) +
2340fdcab083SGanapathi Bhat 			le16_to_cpu(rssi_threshold_tlv->header.len);
2341fdcab083SGanapathi Bhat 	}
2342fdcab083SGanapathi Bhat 
23430c9b7f22SXinming Hu 	for (i = 0; i < bgscan_cfg_in->num_ssids; i++) {
23440c9b7f22SXinming Hu 		ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len;
23450c9b7f22SXinming Hu 
23460c9b7f22SXinming Hu 		wildcard_ssid_tlv =
23470c9b7f22SXinming Hu 			(struct mwifiex_ie_types_wildcard_ssid_params *)tlv_pos;
23480c9b7f22SXinming Hu 		wildcard_ssid_tlv->header.type =
23490c9b7f22SXinming Hu 				cpu_to_le16(TLV_TYPE_WILDCARDSSID);
23500c9b7f22SXinming Hu 		wildcard_ssid_tlv->header.len = cpu_to_le16(
23510c9b7f22SXinming Hu 				(u16)(ssid_len + sizeof(wildcard_ssid_tlv->
23520c9b7f22SXinming Hu 							 max_ssid_length)));
23530c9b7f22SXinming Hu 
23540c9b7f22SXinming Hu 		/* max_ssid_length = 0 tells firmware to perform
23550c9b7f22SXinming Hu 		 * specific scan for the SSID filled, whereas
23560c9b7f22SXinming Hu 		 * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
23570c9b7f22SXinming Hu 		 * wildcard scan.
23580c9b7f22SXinming Hu 		 */
23590c9b7f22SXinming Hu 		if (ssid_len)
23600c9b7f22SXinming Hu 			wildcard_ssid_tlv->max_ssid_length = 0;
23610c9b7f22SXinming Hu 		else
23620c9b7f22SXinming Hu 			wildcard_ssid_tlv->max_ssid_length =
23630c9b7f22SXinming Hu 						IEEE80211_MAX_SSID_LEN;
23640c9b7f22SXinming Hu 
23650c9b7f22SXinming Hu 		memcpy(wildcard_ssid_tlv->ssid,
23660c9b7f22SXinming Hu 		       bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len);
23670c9b7f22SXinming Hu 
23680c9b7f22SXinming Hu 		tlv_pos += (sizeof(wildcard_ssid_tlv->header)
23690c9b7f22SXinming Hu 				+ le16_to_cpu(wildcard_ssid_tlv->header.len));
23700c9b7f22SXinming Hu 	}
23710c9b7f22SXinming Hu 
23720c9b7f22SXinming Hu 	chan_list_tlv = (struct mwifiex_ie_types_chan_list_param_set *)tlv_pos;
23730c9b7f22SXinming Hu 
23740c9b7f22SXinming Hu 	if (bgscan_cfg_in->chan_list[0].chan_number) {
23750c9b7f22SXinming Hu 		dev_dbg(priv->adapter->dev, "info: bgscan: Using supplied channel list\n");
23760c9b7f22SXinming Hu 
23770c9b7f22SXinming Hu 		chan_list_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
23780c9b7f22SXinming Hu 
23790c9b7f22SXinming Hu 		for (chan_idx = 0;
23800c9b7f22SXinming Hu 		     chan_idx < MWIFIEX_BG_SCAN_CHAN_MAX &&
23810c9b7f22SXinming Hu 		     bgscan_cfg_in->chan_list[chan_idx].chan_number;
23820c9b7f22SXinming Hu 		     chan_idx++) {
23830c9b7f22SXinming Hu 			temp_chan = chan_list_tlv->chan_scan_param + chan_idx;
23840c9b7f22SXinming Hu 
23850c9b7f22SXinming Hu 			/* Increment the TLV header length by size appended */
23865653c646SDaniel Mentz 			le16_unaligned_add_cpu(&chan_list_tlv->header.len,
23875653c646SDaniel Mentz 					       sizeof(
23885653c646SDaniel Mentz 					       chan_list_tlv->chan_scan_param));
23890c9b7f22SXinming Hu 
23900c9b7f22SXinming Hu 			temp_chan->chan_number =
23910c9b7f22SXinming Hu 				bgscan_cfg_in->chan_list[chan_idx].chan_number;
23920c9b7f22SXinming Hu 			temp_chan->radio_type =
23930c9b7f22SXinming Hu 				bgscan_cfg_in->chan_list[chan_idx].radio_type;
23940c9b7f22SXinming Hu 
23950c9b7f22SXinming Hu 			scan_type =
23960c9b7f22SXinming Hu 				bgscan_cfg_in->chan_list[chan_idx].scan_type;
23970c9b7f22SXinming Hu 
23980c9b7f22SXinming Hu 			if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
23990c9b7f22SXinming Hu 				temp_chan->chan_scan_mode_bitmap
24000c9b7f22SXinming Hu 					|= MWIFIEX_PASSIVE_SCAN;
24010c9b7f22SXinming Hu 			else
24020c9b7f22SXinming Hu 				temp_chan->chan_scan_mode_bitmap
24030c9b7f22SXinming Hu 					&= ~MWIFIEX_PASSIVE_SCAN;
24040c9b7f22SXinming Hu 
24050c9b7f22SXinming Hu 			if (bgscan_cfg_in->chan_list[chan_idx].scan_time) {
24060c9b7f22SXinming Hu 				scan_dur = (u16)bgscan_cfg_in->
24070c9b7f22SXinming Hu 					chan_list[chan_idx].scan_time;
24080c9b7f22SXinming Hu 			} else {
24090c9b7f22SXinming Hu 				scan_dur = (scan_type ==
24100c9b7f22SXinming Hu 					    MWIFIEX_SCAN_TYPE_PASSIVE) ?
24110c9b7f22SXinming Hu 					    priv->adapter->passive_scan_time :
24120c9b7f22SXinming Hu 					    priv->adapter->specific_scan_time;
24130c9b7f22SXinming Hu 			}
24140c9b7f22SXinming Hu 
24150c9b7f22SXinming Hu 			temp_chan->min_scan_time = cpu_to_le16(scan_dur);
24160c9b7f22SXinming Hu 			temp_chan->max_scan_time = cpu_to_le16(scan_dur);
24170c9b7f22SXinming Hu 		}
24180c9b7f22SXinming Hu 	} else {
24190c9b7f22SXinming Hu 		dev_dbg(priv->adapter->dev,
24200c9b7f22SXinming Hu 			"info: bgscan: Creating full region channel list\n");
24210c9b7f22SXinming Hu 		chan_num =
24220c9b7f22SXinming Hu 			mwifiex_bgscan_create_channel_list(priv, bgscan_cfg_in,
24230c9b7f22SXinming Hu 							   chan_list_tlv->
24240c9b7f22SXinming Hu 							   chan_scan_param);
24255653c646SDaniel Mentz 		le16_unaligned_add_cpu(&chan_list_tlv->header.len,
24260c9b7f22SXinming Hu 				       chan_num *
24270c9b7f22SXinming Hu 			     sizeof(chan_list_tlv->chan_scan_param[0]));
24280c9b7f22SXinming Hu 	}
24290c9b7f22SXinming Hu 
24300c9b7f22SXinming Hu 	tlv_pos += (sizeof(chan_list_tlv->header)
24310c9b7f22SXinming Hu 			+ le16_to_cpu(chan_list_tlv->header.len));
24320c9b7f22SXinming Hu 
24330c9b7f22SXinming Hu 	if (bgscan_cfg_in->start_later) {
24340c9b7f22SXinming Hu 		start_later_tlv =
24350c9b7f22SXinming Hu 			(struct mwifiex_ie_types_bgscan_start_later *)tlv_pos;
24360c9b7f22SXinming Hu 		start_later_tlv->header.type =
24370c9b7f22SXinming Hu 			cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER);
24380c9b7f22SXinming Hu 		start_later_tlv->header.len =
24390c9b7f22SXinming Hu 			cpu_to_le16(sizeof(start_later_tlv->start_later));
24400c9b7f22SXinming Hu 		start_later_tlv->start_later =
24410c9b7f22SXinming Hu 			cpu_to_le16(bgscan_cfg_in->start_later);
24420c9b7f22SXinming Hu 
24430c9b7f22SXinming Hu 		tlv_pos += sizeof(start_later_tlv->header) +
24440c9b7f22SXinming Hu 			le16_to_cpu(start_later_tlv->header.len);
24450c9b7f22SXinming Hu 	}
24460c9b7f22SXinming Hu 
24470c9b7f22SXinming Hu 	/* Append vendor specific IE TLV */
24480c9b7f22SXinming Hu 	mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_BGSCAN, &tlv_pos);
24490c9b7f22SXinming Hu 
24505653c646SDaniel Mentz 	le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv);
24510c9b7f22SXinming Hu 
24520c9b7f22SXinming Hu 	return 0;
24530c9b7f22SXinming Hu }
24540c9b7f22SXinming Hu 
mwifiex_stop_bg_scan(struct mwifiex_private * priv)24550c9b7f22SXinming Hu int mwifiex_stop_bg_scan(struct mwifiex_private *priv)
24560c9b7f22SXinming Hu {
24570c9b7f22SXinming Hu 	struct mwifiex_bg_scan_cfg *bgscan_cfg;
24580c9b7f22SXinming Hu 
24590c9b7f22SXinming Hu 	if (!priv->sched_scanning) {
24600c9b7f22SXinming Hu 		dev_dbg(priv->adapter->dev, "bgscan already stopped!\n");
24610c9b7f22SXinming Hu 		return 0;
24620c9b7f22SXinming Hu 	}
24630c9b7f22SXinming Hu 
24640c9b7f22SXinming Hu 	bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL);
24650c9b7f22SXinming Hu 	if (!bgscan_cfg)
24660c9b7f22SXinming Hu 		return -ENOMEM;
24670c9b7f22SXinming Hu 
24680c9b7f22SXinming Hu 	bgscan_cfg->bss_type = MWIFIEX_BSS_MODE_INFRA;
24690c9b7f22SXinming Hu 	bgscan_cfg->action = MWIFIEX_BGSCAN_ACT_SET;
24700c9b7f22SXinming Hu 	bgscan_cfg->enable = false;
24710c9b7f22SXinming Hu 
24720c9b7f22SXinming Hu 	if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_CONFIG,
24730c9b7f22SXinming Hu 			     HostCmd_ACT_GEN_SET, 0, bgscan_cfg, true)) {
24740c9b7f22SXinming Hu 		kfree(bgscan_cfg);
24750c9b7f22SXinming Hu 		return -EFAULT;
24760c9b7f22SXinming Hu 	}
24770c9b7f22SXinming Hu 
24780c9b7f22SXinming Hu 	kfree(bgscan_cfg);
24790c9b7f22SXinming Hu 	priv->sched_scanning = false;
24800c9b7f22SXinming Hu 
24810c9b7f22SXinming Hu 	return 0;
24820c9b7f22SXinming Hu }
24830c9b7f22SXinming Hu 
2484277b024eSKalle Valo static void
mwifiex_update_chan_statistics(struct mwifiex_private * priv,struct mwifiex_ietypes_chanstats * tlv_stat)2485277b024eSKalle Valo mwifiex_update_chan_statistics(struct mwifiex_private *priv,
2486277b024eSKalle Valo 			       struct mwifiex_ietypes_chanstats *tlv_stat)
2487277b024eSKalle Valo {
2488277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
2489277b024eSKalle Valo 	u8 i, num_chan;
2490277b024eSKalle Valo 	struct mwifiex_fw_chan_stats *fw_chan_stats;
2491277b024eSKalle Valo 	struct mwifiex_chan_stats chan_stats;
2492277b024eSKalle Valo 
2493277b024eSKalle Valo 	fw_chan_stats = (void *)((u8 *)tlv_stat +
2494277b024eSKalle Valo 			      sizeof(struct mwifiex_ie_types_header));
2495277b024eSKalle Valo 	num_chan = le16_to_cpu(tlv_stat->header.len) /
2496277b024eSKalle Valo 					      sizeof(struct mwifiex_chan_stats);
2497277b024eSKalle Valo 
2498277b024eSKalle Valo 	for (i = 0 ; i < num_chan; i++) {
24994b5dde2dSBrian Norris 		if (adapter->survey_idx >= adapter->num_in_chan_stats) {
25004b5dde2dSBrian Norris 			mwifiex_dbg(adapter, WARN,
25014b5dde2dSBrian Norris 				    "FW reported too many channel results (max %d)\n",
25024b5dde2dSBrian Norris 				    adapter->num_in_chan_stats);
25034b5dde2dSBrian Norris 			return;
25044b5dde2dSBrian Norris 		}
2505277b024eSKalle Valo 		chan_stats.chan_num = fw_chan_stats->chan_num;
2506277b024eSKalle Valo 		chan_stats.bandcfg = fw_chan_stats->bandcfg;
2507277b024eSKalle Valo 		chan_stats.flags = fw_chan_stats->flags;
2508277b024eSKalle Valo 		chan_stats.noise = fw_chan_stats->noise;
2509277b024eSKalle Valo 		chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss);
2510277b024eSKalle Valo 		chan_stats.cca_scan_dur =
2511277b024eSKalle Valo 				       le16_to_cpu(fw_chan_stats->cca_scan_dur);
2512277b024eSKalle Valo 		chan_stats.cca_busy_dur =
2513277b024eSKalle Valo 				       le16_to_cpu(fw_chan_stats->cca_busy_dur);
2514277b024eSKalle Valo 		mwifiex_dbg(adapter, INFO,
2515277b024eSKalle Valo 			    "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n",
2516277b024eSKalle Valo 			    chan_stats.chan_num,
2517277b024eSKalle Valo 			    chan_stats.noise,
2518277b024eSKalle Valo 			    chan_stats.total_bss,
2519277b024eSKalle Valo 			    chan_stats.cca_scan_dur,
2520277b024eSKalle Valo 			    chan_stats.cca_busy_dur);
2521277b024eSKalle Valo 		memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats,
2522277b024eSKalle Valo 		       sizeof(struct mwifiex_chan_stats));
2523277b024eSKalle Valo 		fw_chan_stats++;
2524277b024eSKalle Valo 	}
2525277b024eSKalle Valo }
2526277b024eSKalle Valo 
2527277b024eSKalle Valo /* This function handles the command response of extended scan */
mwifiex_ret_802_11_scan_ext(struct mwifiex_private * priv,struct host_cmd_ds_command * resp)2528277b024eSKalle Valo int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
2529277b024eSKalle Valo 				struct host_cmd_ds_command *resp)
2530277b024eSKalle Valo {
2531277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
2532277b024eSKalle Valo 	struct host_cmd_ds_802_11_scan_ext *ext_scan_resp;
2533277b024eSKalle Valo 	struct mwifiex_ie_types_header *tlv;
2534277b024eSKalle Valo 	struct mwifiex_ietypes_chanstats *tlv_stat;
2535277b024eSKalle Valo 	u16 buf_left, type, len;
2536277b024eSKalle Valo 
2537277b024eSKalle Valo 	struct host_cmd_ds_command *cmd_ptr;
2538277b024eSKalle Valo 	struct cmd_ctrl_node *cmd_node;
2539277b024eSKalle Valo 	bool complete_scan = false;
2540277b024eSKalle Valo 
2541277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n");
2542277b024eSKalle Valo 
2543277b024eSKalle Valo 	ext_scan_resp = &resp->params.ext_scan;
2544277b024eSKalle Valo 
2545277b024eSKalle Valo 	tlv = (void *)ext_scan_resp->tlv_buffer;
2546*fef7b51fSGustavo A. R. Silva 	buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN);
2547277b024eSKalle Valo 
2548277b024eSKalle Valo 	while (buf_left >= sizeof(struct mwifiex_ie_types_header)) {
2549277b024eSKalle Valo 		type = le16_to_cpu(tlv->type);
2550277b024eSKalle Valo 		len = le16_to_cpu(tlv->len);
2551277b024eSKalle Valo 
2552277b024eSKalle Valo 		if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) {
2553277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
2554277b024eSKalle Valo 				    "error processing scan response TLVs");
2555277b024eSKalle Valo 			break;
2556277b024eSKalle Valo 		}
2557277b024eSKalle Valo 
2558277b024eSKalle Valo 		switch (type) {
2559277b024eSKalle Valo 		case TLV_TYPE_CHANNEL_STATS:
2560277b024eSKalle Valo 			tlv_stat = (void *)tlv;
2561277b024eSKalle Valo 			mwifiex_update_chan_statistics(priv, tlv_stat);
2562277b024eSKalle Valo 			break;
2563277b024eSKalle Valo 		default:
2564277b024eSKalle Valo 			break;
2565277b024eSKalle Valo 		}
2566277b024eSKalle Valo 
2567277b024eSKalle Valo 		buf_left -= len + sizeof(struct mwifiex_ie_types_header);
2568277b024eSKalle Valo 		tlv = (void *)((u8 *)tlv + len +
2569277b024eSKalle Valo 			       sizeof(struct mwifiex_ie_types_header));
2570277b024eSKalle Valo 	}
2571277b024eSKalle Valo 
25728a7f9fd8SBrian Norris 	spin_lock_bh(&adapter->cmd_pending_q_lock);
25738a7f9fd8SBrian Norris 	spin_lock_bh(&adapter->scan_pending_q_lock);
2574277b024eSKalle Valo 	if (list_empty(&adapter->scan_pending_q)) {
2575277b024eSKalle Valo 		complete_scan = true;
2576277b024eSKalle Valo 		list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) {
2577277b024eSKalle Valo 			cmd_ptr = (void *)cmd_node->cmd_skb->data;
2578277b024eSKalle Valo 			if (le16_to_cpu(cmd_ptr->command) ==
2579277b024eSKalle Valo 			    HostCmd_CMD_802_11_SCAN_EXT) {
2580277b024eSKalle Valo 				mwifiex_dbg(adapter, INFO,
2581277b024eSKalle Valo 					    "Scan pending in command pending list");
2582277b024eSKalle Valo 				complete_scan = false;
2583277b024eSKalle Valo 				break;
2584277b024eSKalle Valo 			}
2585277b024eSKalle Valo 		}
2586277b024eSKalle Valo 	}
25878a7f9fd8SBrian Norris 	spin_unlock_bh(&adapter->scan_pending_q_lock);
25888a7f9fd8SBrian Norris 	spin_unlock_bh(&adapter->cmd_pending_q_lock);
2589277b024eSKalle Valo 
2590277b024eSKalle Valo 	if (complete_scan)
2591277b024eSKalle Valo 		mwifiex_complete_scan(priv);
2592277b024eSKalle Valo 
2593277b024eSKalle Valo 	return 0;
2594277b024eSKalle Valo }
2595277b024eSKalle Valo 
2596277b024eSKalle Valo /* This function This function handles the event extended scan report. It
2597277b024eSKalle Valo  * parses extended scan results and informs to cfg80211 stack.
2598277b024eSKalle Valo  */
mwifiex_handle_event_ext_scan_report(struct mwifiex_private * priv,void * buf)2599277b024eSKalle Valo int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
2600277b024eSKalle Valo 					 void *buf)
2601277b024eSKalle Valo {
2602277b024eSKalle Valo 	int ret = 0;
2603277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
2604277b024eSKalle Valo 	u8 *bss_info;
2605277b024eSKalle Valo 	u32 bytes_left, bytes_left_for_tlv, idx;
2606277b024eSKalle Valo 	u16 type, len;
2607277b024eSKalle Valo 	struct mwifiex_ie_types_data *tlv;
2608277b024eSKalle Valo 	struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv;
2609277b024eSKalle Valo 	struct mwifiex_ie_types_bss_scan_info *scan_info_tlv;
2610277b024eSKalle Valo 	u8 *radio_type;
2611277b024eSKalle Valo 	u64 fw_tsf = 0;
2612277b024eSKalle Valo 	s32 rssi = 0;
2613277b024eSKalle Valo 	struct mwifiex_event_scan_result *event_scan = buf;
2614277b024eSKalle Valo 	u8 num_of_set = event_scan->num_of_set;
2615277b024eSKalle Valo 	u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result);
2616277b024eSKalle Valo 	u16 scan_resp_size = le16_to_cpu(event_scan->buf_size);
2617277b024eSKalle Valo 
2618277b024eSKalle Valo 	if (num_of_set > MWIFIEX_MAX_AP) {
2619277b024eSKalle Valo 		mwifiex_dbg(adapter, ERROR,
2620277b024eSKalle Valo 			    "EXT_SCAN: Invalid number of AP returned (%d)!!\n",
2621277b024eSKalle Valo 			    num_of_set);
2622277b024eSKalle Valo 		ret = -1;
2623277b024eSKalle Valo 		goto check_next_scan;
2624277b024eSKalle Valo 	}
2625277b024eSKalle Valo 
2626277b024eSKalle Valo 	bytes_left = scan_resp_size;
2627277b024eSKalle Valo 	mwifiex_dbg(adapter, INFO,
2628277b024eSKalle Valo 		    "EXT_SCAN: size %d, returned %d APs...",
2629277b024eSKalle Valo 		    scan_resp_size, num_of_set);
2630277b024eSKalle Valo 	mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf,
2631277b024eSKalle Valo 			 scan_resp_size +
2632277b024eSKalle Valo 			 sizeof(struct mwifiex_event_scan_result));
2633277b024eSKalle Valo 
2634277b024eSKalle Valo 	tlv = (struct mwifiex_ie_types_data *)scan_resp;
2635277b024eSKalle Valo 
2636277b024eSKalle Valo 	for (idx = 0; idx < num_of_set && bytes_left; idx++) {
2637277b024eSKalle Valo 		type = le16_to_cpu(tlv->header.type);
2638277b024eSKalle Valo 		len = le16_to_cpu(tlv->header.len);
2639277b024eSKalle Valo 		if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) {
2640277b024eSKalle Valo 			mwifiex_dbg(adapter, ERROR,
2641277b024eSKalle Valo 				    "EXT_SCAN: Error bytes left < TLV length\n");
2642277b024eSKalle Valo 			break;
2643277b024eSKalle Valo 		}
2644277b024eSKalle Valo 		scan_rsp_tlv = NULL;
2645277b024eSKalle Valo 		scan_info_tlv = NULL;
2646277b024eSKalle Valo 		bytes_left_for_tlv = bytes_left;
2647277b024eSKalle Valo 
2648277b024eSKalle Valo 		/* BSS response TLV with beacon or probe response buffer
2649277b024eSKalle Valo 		 * at the initial position of each descriptor
2650277b024eSKalle Valo 		 */
2651277b024eSKalle Valo 		if (type != TLV_TYPE_BSS_SCAN_RSP)
2652277b024eSKalle Valo 			break;
2653277b024eSKalle Valo 
2654277b024eSKalle Valo 		bss_info = (u8 *)tlv;
2655277b024eSKalle Valo 		scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv;
2656277b024eSKalle Valo 		tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
2657277b024eSKalle Valo 		bytes_left_for_tlv -=
2658277b024eSKalle Valo 				(len + sizeof(struct mwifiex_ie_types_header));
2659277b024eSKalle Valo 
2660277b024eSKalle Valo 		while (bytes_left_for_tlv >=
2661277b024eSKalle Valo 		       sizeof(struct mwifiex_ie_types_header) &&
2662277b024eSKalle Valo 		       le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) {
2663277b024eSKalle Valo 			type = le16_to_cpu(tlv->header.type);
2664277b024eSKalle Valo 			len = le16_to_cpu(tlv->header.len);
2665277b024eSKalle Valo 			if (bytes_left_for_tlv <
2666277b024eSKalle Valo 			    sizeof(struct mwifiex_ie_types_header) + len) {
2667277b024eSKalle Valo 				mwifiex_dbg(adapter, ERROR,
2668277b024eSKalle Valo 					    "EXT_SCAN: Error in processing TLV,\t"
2669277b024eSKalle Valo 					    "bytes left < TLV length\n");
2670277b024eSKalle Valo 				scan_rsp_tlv = NULL;
2671277b024eSKalle Valo 				bytes_left_for_tlv = 0;
2672277b024eSKalle Valo 				continue;
2673277b024eSKalle Valo 			}
2674277b024eSKalle Valo 			switch (type) {
2675277b024eSKalle Valo 			case TLV_TYPE_BSS_SCAN_INFO:
2676277b024eSKalle Valo 				scan_info_tlv =
2677277b024eSKalle Valo 				  (struct mwifiex_ie_types_bss_scan_info *)tlv;
2678277b024eSKalle Valo 				if (len !=
2679277b024eSKalle Valo 				 sizeof(struct mwifiex_ie_types_bss_scan_info) -
2680277b024eSKalle Valo 				 sizeof(struct mwifiex_ie_types_header)) {
2681277b024eSKalle Valo 					bytes_left_for_tlv = 0;
2682277b024eSKalle Valo 					continue;
2683277b024eSKalle Valo 				}
2684277b024eSKalle Valo 				break;
2685277b024eSKalle Valo 			default:
2686277b024eSKalle Valo 				break;
2687277b024eSKalle Valo 			}
2688277b024eSKalle Valo 			tlv = (struct mwifiex_ie_types_data *)(tlv->data + len);
2689277b024eSKalle Valo 			bytes_left -=
2690277b024eSKalle Valo 				(len + sizeof(struct mwifiex_ie_types_header));
2691277b024eSKalle Valo 			bytes_left_for_tlv -=
2692277b024eSKalle Valo 				(len + sizeof(struct mwifiex_ie_types_header));
2693277b024eSKalle Valo 		}
2694277b024eSKalle Valo 
2695277b024eSKalle Valo 		if (!scan_rsp_tlv)
2696277b024eSKalle Valo 			break;
2697277b024eSKalle Valo 
2698277b024eSKalle Valo 		/* Advance pointer to the beacon buffer length and
2699277b024eSKalle Valo 		 * update the bytes count so that the function
2700277b024eSKalle Valo 		 * wlan_interpret_bss_desc_with_ie() can handle the
2701277b024eSKalle Valo 		 * scan buffer withut any change
2702277b024eSKalle Valo 		 */
2703277b024eSKalle Valo 		bss_info += sizeof(u16);
2704277b024eSKalle Valo 		bytes_left -= sizeof(u16);
2705277b024eSKalle Valo 
2706277b024eSKalle Valo 		if (scan_info_tlv) {
2707277b024eSKalle Valo 			rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi));
2708277b024eSKalle Valo 			rssi *= 100;           /* Convert dBm to mBm */
2709277b024eSKalle Valo 			mwifiex_dbg(adapter, INFO,
2710277b024eSKalle Valo 				    "info: InterpretIE: RSSI=%d\n", rssi);
2711277b024eSKalle Valo 			fw_tsf = le64_to_cpu(scan_info_tlv->tsf);
2712277b024eSKalle Valo 			radio_type = &scan_info_tlv->radio_type;
2713277b024eSKalle Valo 		} else {
2714277b024eSKalle Valo 			radio_type = NULL;
2715277b024eSKalle Valo 		}
2716277b024eSKalle Valo 		ret = mwifiex_parse_single_response_buf(priv, &bss_info,
2717277b024eSKalle Valo 							&bytes_left, fw_tsf,
2718277b024eSKalle Valo 							radio_type, true, rssi);
2719277b024eSKalle Valo 		if (ret)
2720277b024eSKalle Valo 			goto check_next_scan;
2721277b024eSKalle Valo 	}
2722277b024eSKalle Valo 
2723277b024eSKalle Valo check_next_scan:
2724277b024eSKalle Valo 	if (!event_scan->more_event)
2725277b024eSKalle Valo 		mwifiex_check_next_scan_command(priv);
2726277b024eSKalle Valo 
2727277b024eSKalle Valo 	return ret;
2728277b024eSKalle Valo }
2729277b024eSKalle Valo 
2730277b024eSKalle Valo /*
2731277b024eSKalle Valo  * This function prepares command for background scan query.
2732277b024eSKalle Valo  *
2733277b024eSKalle Valo  * Preparation includes -
2734277b024eSKalle Valo  *      - Setting command ID and proper size
2735277b024eSKalle Valo  *      - Setting background scan flush parameter
2736277b024eSKalle Valo  *      - Ensuring correct endian-ness
2737277b024eSKalle Valo  */
mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command * cmd)2738277b024eSKalle Valo int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd)
2739277b024eSKalle Valo {
2740277b024eSKalle Valo 	struct host_cmd_ds_802_11_bg_scan_query *bg_query =
2741277b024eSKalle Valo 		&cmd->params.bg_scan_query;
2742277b024eSKalle Valo 
2743277b024eSKalle Valo 	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY);
2744277b024eSKalle Valo 	cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query)
2745277b024eSKalle Valo 				+ S_DS_GEN);
2746277b024eSKalle Valo 
2747277b024eSKalle Valo 	bg_query->flush = 1;
2748277b024eSKalle Valo 
2749277b024eSKalle Valo 	return 0;
2750277b024eSKalle Valo }
2751277b024eSKalle Valo 
2752277b024eSKalle Valo /*
2753277b024eSKalle Valo  * This function inserts scan command node to the scan pending queue.
2754277b024eSKalle Valo  */
2755277b024eSKalle Valo void
mwifiex_queue_scan_cmd(struct mwifiex_private * priv,struct cmd_ctrl_node * cmd_node)2756277b024eSKalle Valo mwifiex_queue_scan_cmd(struct mwifiex_private *priv,
2757277b024eSKalle Valo 		       struct cmd_ctrl_node *cmd_node)
2758277b024eSKalle Valo {
2759277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
2760277b024eSKalle Valo 
2761277b024eSKalle Valo 	cmd_node->wait_q_enabled = true;
2762277b024eSKalle Valo 	cmd_node->condition = &adapter->scan_wait_q_woken;
27638a7f9fd8SBrian Norris 	spin_lock_bh(&adapter->scan_pending_q_lock);
2764277b024eSKalle Valo 	list_add_tail(&cmd_node->list, &adapter->scan_pending_q);
27658a7f9fd8SBrian Norris 	spin_unlock_bh(&adapter->scan_pending_q_lock);
2766277b024eSKalle Valo }
2767277b024eSKalle Valo 
2768277b024eSKalle Valo /*
2769277b024eSKalle Valo  * This function sends a scan command for all available channels to the
2770277b024eSKalle Valo  * firmware, filtered on a specific SSID.
2771277b024eSKalle Valo  */
mwifiex_scan_specific_ssid(struct mwifiex_private * priv,struct cfg80211_ssid * req_ssid)2772277b024eSKalle Valo static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv,
2773277b024eSKalle Valo 				      struct cfg80211_ssid *req_ssid)
2774277b024eSKalle Valo {
2775277b024eSKalle Valo 	struct mwifiex_adapter *adapter = priv->adapter;
2776277b024eSKalle Valo 	int ret;
2777277b024eSKalle Valo 	struct mwifiex_user_scan_cfg *scan_cfg;
2778277b024eSKalle Valo 
2779277b024eSKalle Valo 	if (adapter->scan_processing) {
2780277b024eSKalle Valo 		mwifiex_dbg(adapter, WARN,
2781277b024eSKalle Valo 			    "cmd: Scan already in process...\n");
2782277b024eSKalle Valo 		return -EBUSY;
2783277b024eSKalle Valo 	}
2784277b024eSKalle Valo 
2785277b024eSKalle Valo 	if (priv->scan_block) {
2786277b024eSKalle Valo 		mwifiex_dbg(adapter, WARN,
2787277b024eSKalle Valo 			    "cmd: Scan is blocked during association...\n");
2788277b024eSKalle Valo 		return -EBUSY;
2789277b024eSKalle Valo 	}
2790277b024eSKalle Valo 
2791277b024eSKalle Valo 	scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL);
2792277b024eSKalle Valo 	if (!scan_cfg)
2793277b024eSKalle Valo 		return -ENOMEM;
2794277b024eSKalle Valo 
2795277b024eSKalle Valo 	scan_cfg->ssid_list = req_ssid;
2796277b024eSKalle Valo 	scan_cfg->num_ssids = 1;
2797277b024eSKalle Valo 
2798277b024eSKalle Valo 	ret = mwifiex_scan_networks(priv, scan_cfg);
2799277b024eSKalle Valo 
2800277b024eSKalle Valo 	kfree(scan_cfg);
2801277b024eSKalle Valo 	return ret;
2802277b024eSKalle Valo }
2803277b024eSKalle Valo 
2804277b024eSKalle Valo /*
2805277b024eSKalle Valo  * Sends IOCTL request to start a scan.
2806277b024eSKalle Valo  *
2807277b024eSKalle Valo  * This function allocates the IOCTL request buffer, fills it
2808277b024eSKalle Valo  * with requisite parameters and calls the IOCTL handler.
2809277b024eSKalle Valo  *
2810277b024eSKalle Valo  * Scan command can be issued for both normal scan and specific SSID
2811277b024eSKalle Valo  * scan, depending upon whether an SSID is provided or not.
2812277b024eSKalle Valo  */
mwifiex_request_scan(struct mwifiex_private * priv,struct cfg80211_ssid * req_ssid)2813277b024eSKalle Valo int mwifiex_request_scan(struct mwifiex_private *priv,
2814277b024eSKalle Valo 			 struct cfg80211_ssid *req_ssid)
2815277b024eSKalle Valo {
2816277b024eSKalle Valo 	int ret;
2817277b024eSKalle Valo 
28181abf9ae7SBinoy Jayan 	if (mutex_lock_interruptible(&priv->async_mutex)) {
2819277b024eSKalle Valo 		mwifiex_dbg(priv->adapter, ERROR,
2820277b024eSKalle Valo 			    "%s: acquire semaphore fail\n",
2821277b024eSKalle Valo 			    __func__);
2822277b024eSKalle Valo 		return -1;
2823277b024eSKalle Valo 	}
2824277b024eSKalle Valo 
2825277b024eSKalle Valo 	priv->adapter->scan_wait_q_woken = false;
2826277b024eSKalle Valo 
2827277b024eSKalle Valo 	if (req_ssid && req_ssid->ssid_len != 0)
2828277b024eSKalle Valo 		/* Specific SSID scan */
2829277b024eSKalle Valo 		ret = mwifiex_scan_specific_ssid(priv, req_ssid);
2830277b024eSKalle Valo 	else
2831277b024eSKalle Valo 		/* Normal scan */
2832277b024eSKalle Valo 		ret = mwifiex_scan_networks(priv, NULL);
2833277b024eSKalle Valo 
28341abf9ae7SBinoy Jayan 	mutex_unlock(&priv->async_mutex);
2835277b024eSKalle Valo 
2836277b024eSKalle Valo 	return ret;
2837277b024eSKalle Valo }
2838277b024eSKalle Valo 
2839277b024eSKalle Valo /*
2840277b024eSKalle Valo  * This function appends the vendor specific IE TLV to a buffer.
2841277b024eSKalle Valo  */
2842277b024eSKalle Valo int
mwifiex_cmd_append_vsie_tlv(struct mwifiex_private * priv,u16 vsie_mask,u8 ** buffer)2843277b024eSKalle Valo mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv,
2844277b024eSKalle Valo 			    u16 vsie_mask, u8 **buffer)
2845277b024eSKalle Valo {
2846277b024eSKalle Valo 	int id, ret_len = 0;
2847277b024eSKalle Valo 	struct mwifiex_ie_types_vendor_param_set *vs_param_set;
2848277b024eSKalle Valo 
2849277b024eSKalle Valo 	if (!buffer)
2850277b024eSKalle Valo 		return 0;
2851277b024eSKalle Valo 	if (!(*buffer))
2852277b024eSKalle Valo 		return 0;
2853277b024eSKalle Valo 
2854277b024eSKalle Valo 	/*
2855277b024eSKalle Valo 	 * Traverse through the saved vendor specific IE array and append
2856277b024eSKalle Valo 	 * the selected(scan/assoc/adhoc) IE as TLV to the command
2857277b024eSKalle Valo 	 */
2858277b024eSKalle Valo 	for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) {
2859277b024eSKalle Valo 		if (priv->vs_ie[id].mask & vsie_mask) {
2860277b024eSKalle Valo 			vs_param_set =
2861277b024eSKalle Valo 				(struct mwifiex_ie_types_vendor_param_set *)
2862277b024eSKalle Valo 				*buffer;
2863277b024eSKalle Valo 			vs_param_set->header.type =
2864277b024eSKalle Valo 				cpu_to_le16(TLV_TYPE_PASSTHROUGH);
2865277b024eSKalle Valo 			vs_param_set->header.len =
2866277b024eSKalle Valo 				cpu_to_le16((((u16) priv->vs_ie[id].ie[1])
2867277b024eSKalle Valo 				& 0x00FF) + 2);
2868b70261a2SQing Xu 			if (le16_to_cpu(vs_param_set->header.len) >
2869b70261a2SQing Xu 				MWIFIEX_MAX_VSIE_LEN) {
2870b70261a2SQing Xu 				mwifiex_dbg(priv->adapter, ERROR,
2871b70261a2SQing Xu 					    "Invalid param length!\n");
2872b70261a2SQing Xu 				break;
2873b70261a2SQing Xu 			}
2874b70261a2SQing Xu 
2875277b024eSKalle Valo 			memcpy(vs_param_set->ie, priv->vs_ie[id].ie,
2876277b024eSKalle Valo 			       le16_to_cpu(vs_param_set->header.len));
2877277b024eSKalle Valo 			*buffer += le16_to_cpu(vs_param_set->header.len) +
2878277b024eSKalle Valo 				   sizeof(struct mwifiex_ie_types_header);
2879277b024eSKalle Valo 			ret_len += le16_to_cpu(vs_param_set->header.len) +
2880277b024eSKalle Valo 				   sizeof(struct mwifiex_ie_types_header);
2881277b024eSKalle Valo 		}
2882277b024eSKalle Valo 	}
2883277b024eSKalle Valo 	return ret_len;
2884277b024eSKalle Valo }
2885277b024eSKalle Valo 
2886277b024eSKalle Valo /*
2887277b024eSKalle Valo  * This function saves a beacon buffer of the current BSS descriptor.
2888277b024eSKalle Valo  *
2889277b024eSKalle Valo  * The current beacon buffer is saved so that it can be restored in the
2890277b024eSKalle Valo  * following cases that makes the beacon buffer not to contain the current
2891277b024eSKalle Valo  * ssid's beacon buffer.
2892277b024eSKalle Valo  *      - The current ssid was not found somehow in the last scan.
2893277b024eSKalle Valo  *      - The current ssid was the last entry of the scan table and overloaded.
2894277b024eSKalle Valo  */
2895277b024eSKalle Valo void
mwifiex_save_curr_bcn(struct mwifiex_private * priv)2896277b024eSKalle Valo mwifiex_save_curr_bcn(struct mwifiex_private *priv)
2897277b024eSKalle Valo {
2898277b024eSKalle Valo 	struct mwifiex_bssdescriptor *curr_bss =
2899277b024eSKalle Valo 		&priv->curr_bss_params.bss_descriptor;
2900277b024eSKalle Valo 
2901277b024eSKalle Valo 	if (!curr_bss->beacon_buf_size)
2902277b024eSKalle Valo 		return;
2903277b024eSKalle Valo 
2904277b024eSKalle Valo 	/* allocate beacon buffer at 1st time; or if it's size has changed */
2905277b024eSKalle Valo 	if (!priv->curr_bcn_buf ||
2906277b024eSKalle Valo 	    priv->curr_bcn_size != curr_bss->beacon_buf_size) {
2907277b024eSKalle Valo 		priv->curr_bcn_size = curr_bss->beacon_buf_size;
2908277b024eSKalle Valo 
2909277b024eSKalle Valo 		kfree(priv->curr_bcn_buf);
2910277b024eSKalle Valo 		priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size,
2911277b024eSKalle Valo 					     GFP_ATOMIC);
2912277b024eSKalle Valo 		if (!priv->curr_bcn_buf)
2913277b024eSKalle Valo 			return;
2914277b024eSKalle Valo 	}
2915277b024eSKalle Valo 
2916277b024eSKalle Valo 	memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf,
2917277b024eSKalle Valo 	       curr_bss->beacon_buf_size);
2918277b024eSKalle Valo 	mwifiex_dbg(priv->adapter, INFO,
2919277b024eSKalle Valo 		    "info: current beacon saved %d\n",
2920277b024eSKalle Valo 		    priv->curr_bcn_size);
2921277b024eSKalle Valo 
2922277b024eSKalle Valo 	curr_bss->beacon_buf = priv->curr_bcn_buf;
2923277b024eSKalle Valo 
2924277b024eSKalle Valo 	/* adjust the pointers in the current BSS descriptor */
2925277b024eSKalle Valo 	if (curr_bss->bcn_wpa_ie)
2926277b024eSKalle Valo 		curr_bss->bcn_wpa_ie =
2927277b024eSKalle Valo 			(struct ieee_types_vendor_specific *)
2928277b024eSKalle Valo 			(curr_bss->beacon_buf +
2929277b024eSKalle Valo 			 curr_bss->wpa_offset);
2930277b024eSKalle Valo 
2931277b024eSKalle Valo 	if (curr_bss->bcn_rsn_ie)
2932277b024eSKalle Valo 		curr_bss->bcn_rsn_ie = (struct ieee_types_generic *)
2933277b024eSKalle Valo 			(curr_bss->beacon_buf +
2934277b024eSKalle Valo 			 curr_bss->rsn_offset);
2935277b024eSKalle Valo 
2936277b024eSKalle Valo 	if (curr_bss->bcn_ht_cap)
2937277b024eSKalle Valo 		curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *)
2938277b024eSKalle Valo 			(curr_bss->beacon_buf +
2939277b024eSKalle Valo 			 curr_bss->ht_cap_offset);
2940277b024eSKalle Valo 
2941277b024eSKalle Valo 	if (curr_bss->bcn_ht_oper)
2942277b024eSKalle Valo 		curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *)
2943277b024eSKalle Valo 			(curr_bss->beacon_buf +
2944277b024eSKalle Valo 			 curr_bss->ht_info_offset);
2945277b024eSKalle Valo 
2946277b024eSKalle Valo 	if (curr_bss->bcn_vht_cap)
2947277b024eSKalle Valo 		curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf +
2948277b024eSKalle Valo 						 curr_bss->vht_cap_offset);
2949277b024eSKalle Valo 
2950277b024eSKalle Valo 	if (curr_bss->bcn_vht_oper)
2951277b024eSKalle Valo 		curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf +
2952277b024eSKalle Valo 						  curr_bss->vht_info_offset);
2953277b024eSKalle Valo 
2954277b024eSKalle Valo 	if (curr_bss->bcn_bss_co_2040)
2955277b024eSKalle Valo 		curr_bss->bcn_bss_co_2040 =
2956277b024eSKalle Valo 			(curr_bss->beacon_buf + curr_bss->bss_co_2040_offset);
2957277b024eSKalle Valo 
2958277b024eSKalle Valo 	if (curr_bss->bcn_ext_cap)
2959277b024eSKalle Valo 		curr_bss->bcn_ext_cap = curr_bss->beacon_buf +
2960277b024eSKalle Valo 			curr_bss->ext_cap_offset;
2961277b024eSKalle Valo 
2962277b024eSKalle Valo 	if (curr_bss->oper_mode)
2963277b024eSKalle Valo 		curr_bss->oper_mode = (void *)(curr_bss->beacon_buf +
2964277b024eSKalle Valo 					       curr_bss->oper_mode_offset);
2965277b024eSKalle Valo }
2966277b024eSKalle Valo 
2967277b024eSKalle Valo /*
2968277b024eSKalle Valo  * This function frees the current BSS descriptor beacon buffer.
2969277b024eSKalle Valo  */
2970277b024eSKalle Valo void
mwifiex_free_curr_bcn(struct mwifiex_private * priv)2971277b024eSKalle Valo mwifiex_free_curr_bcn(struct mwifiex_private *priv)
2972277b024eSKalle Valo {
2973277b024eSKalle Valo 	kfree(priv->curr_bcn_buf);
2974277b024eSKalle Valo 	priv->curr_bcn_buf = NULL;
2975277b024eSKalle Valo }
2976