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