summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c')
-rw-r--r--drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c7893
1 files changed, 7893 insertions, 0 deletions
diff --git a/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c
new file mode 100644
index 000000000000..72f70404d85c
--- /dev/null
+++ b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c
@@ -0,0 +1,7893 @@
+/** @file moal_sta_cfg80211.c
+ *
+ * @brief This file contains the functions for STA CFG80211.
+ *
+ *
+ * Copyright 2014-2020 NXP
+ *
+ * This software file (the File) is distributed by NXP
+ * under the terms of the GNU General Public License Version 2, June 1991
+ * (the License). You may use, redistribute and/or modify the File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+
+#include "moal_cfg80211.h"
+#include "moal_cfg80211_util.h"
+#include "moal_sta_cfg80211.h"
+#include "moal_eth_ioctl.h"
+#ifdef UAP_SUPPORT
+#include "moal_uap.h"
+#endif
+#include <linux/sort.h>
+
+#if defined(STA_CFG80211) || defined(UAP_CFG80211)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+extern int fw_region;
+#endif
+#endif
+/* Supported crypto cipher suits to be advertised to cfg80211 */
+const u32 cfg80211_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_SMS4, WLAN_CIPHER_SUITE_AES_CMAC,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ WLAN_CIPHER_SUITE_BIP_GMAC_256,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ WLAN_CIPHER_SUITE_GCMP,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ WLAN_CIPHER_SUITE_GCMP_256,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ WLAN_CIPHER_SUITE_CCMP_256,
+#endif
+};
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+static void
+#else
+static int
+#endif
+woal_cfg80211_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request);
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+static int woal_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request);
+#else
+static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_scan_request *request);
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+static void woal_cfg80211_abort_scan(struct wiphy *wiphy,
+ struct wireless_dev *wdev);
+#endif
+static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme);
+
+static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ t_u16 reason_code);
+
+static int woal_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *mac,
+#else
+ u8 *mac,
+#endif
+ struct station_info *sinfo);
+
+static int woal_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev, int idx,
+ t_u8 *mac, struct station_info *sinfo);
+
+static int woal_cfg80211_dump_survey(struct wiphy *wiphy,
+ struct net_device *dev, int idx,
+ struct survey_info *survey);
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static int woal_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef);
+#endif
+static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev, bool enabled,
+ int timeout);
+#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
+static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst);
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+static int woal_cfg80211_get_tx_power(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev,
+#endif
+ int *dbm);
+
+static int woal_cfg80211_set_tx_power(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev,
+#endif
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ enum tx_power_setting type,
+#else
+ enum nl80211_tx_power_setting type,
+#endif
+ int dbm);
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct wireless_dev *wdev,
+#else
+ struct net_device *dev,
+#endif
+ u64 cookie);
+
+static int
+woal_cfg80211_remain_on_channel(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct wireless_dev *wdev,
+#else
+ struct net_device *dev,
+#endif
+ struct ieee80211_channel *chan,
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ enum nl80211_channel_type channel_type,
+#endif
+ unsigned int duration, u64 *cookie);
+
+static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct wireless_dev *wdev,
+#else
+ struct net_device *dev,
+#endif
+ u64 cookie);
+#endif /* KERNEL_VERSION */
+
+#ifdef CONFIG_NL80211_TESTMODE
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+static int woal_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len);
+#else
+static int woal_testmode_cmd(struct wiphy *wiphy, void *data, int len);
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+int woal_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_sched_scan_request *request);
+int woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ ,
+ u64 reqid
+#endif
+);
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+int woal_cfg80211_resume(struct wiphy *wiphy);
+int woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+static void woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled);
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+static int woal_cfg80211_change_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *mac,
+#else
+ u8 *mac,
+#endif
+ struct station_parameters *params);
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie);
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static int woal_cfg80211_authenticate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_auth_request *req);
+
+static int woal_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_assoc_request *req);
+#ifdef UAP_SUPPORT
+int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_parameters *params);
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+#ifdef UAP_SUPPORT
+static int woal_cfg80211_add_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *mac,
+#else
+ u8 *mac,
+#endif
+ struct station_parameters *params);
+#endif
+#endif
+
+static int woal_cfg80211_deauthenticate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_deauth_request *req);
+
+static int woal_cfg80211_disassociate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_disassoc_request *req);
+
+/** cfg80211 operations */
+static struct cfg80211_ops woal_cfg80211_ops = {
+ .change_virtual_intf = woal_cfg80211_change_virtual_intf,
+ .scan = woal_cfg80211_scan,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+ .abort_scan = woal_cfg80211_abort_scan,
+#endif
+ .connect = woal_cfg80211_connect,
+ .disconnect = woal_cfg80211_disconnect,
+ .deauth = woal_cfg80211_deauthenticate,
+ .disassoc = woal_cfg80211_disassociate,
+ .get_station = woal_cfg80211_get_station,
+ .dump_station = woal_cfg80211_dump_station,
+ .dump_survey = woal_cfg80211_dump_survey,
+ .set_wiphy_params = woal_cfg80211_set_wiphy_params,
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+ .set_channel = woal_cfg80211_set_channel,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ .get_channel = woal_cfg80211_get_channel,
+#endif
+ .add_key = woal_cfg80211_add_key,
+ .del_key = woal_cfg80211_del_key,
+ .set_default_key = woal_cfg80211_set_default_key,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
+ .set_default_mgmt_key = woal_cfg80211_set_default_mgmt_key,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ .set_rekey_data = woal_cfg80211_set_rekey_data,
+#endif
+ .set_pmksa = woal_cfg80211_set_pmksa,
+ .del_pmksa = woal_cfg80211_del_pmksa,
+ .flush_pmksa = woal_cfg80211_flush_pmksa,
+ .set_power_mgmt = woal_cfg80211_set_power_mgmt,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+ .set_tx_power = woal_cfg80211_set_tx_power,
+ .get_tx_power = woal_cfg80211_get_tx_power,
+#endif
+ .set_bitrate_mask = woal_cfg80211_set_bitrate_mask,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ .sched_scan_start = woal_cfg80211_sched_scan_start,
+ .sched_scan_stop = woal_cfg80211_sched_scan_stop,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ .suspend = woal_cfg80211_suspend,
+ .resume = woal_cfg80211_resume,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ .set_wakeup = woal_cfg80211_set_wakeup,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ .set_antenna = woal_cfg80211_set_antenna,
+ .get_antenna = woal_cfg80211_get_antenna,
+#endif
+#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
+ .set_cqm_rssi_config = woal_cfg80211_set_cqm_rssi_config,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ .change_station = woal_cfg80211_change_station,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ .update_ft_ies = woal_cfg80211_update_ft_ies,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ .set_qos_map = woal_cfg80211_set_qos_map,
+#endif
+#ifdef UAP_CFG80211
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ .set_coalesce = woal_cfg80211_set_coalesce,
+#endif
+ .add_virtual_intf = woal_cfg80211_add_virtual_intf,
+ .del_virtual_intf = woal_cfg80211_del_virtual_intf,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ .start_ap = woal_cfg80211_add_beacon,
+ .change_beacon = woal_cfg80211_set_beacon,
+ .stop_ap = woal_cfg80211_del_beacon,
+#else
+ .add_beacon = woal_cfg80211_add_beacon,
+ .set_beacon = woal_cfg80211_set_beacon,
+ .del_beacon = woal_cfg80211_del_beacon,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ .change_bss = woal_cfg80211_change_bss,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ .add_station = woal_cfg80211_add_station,
+#endif
+ .del_station = woal_cfg80211_del_station,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ .set_txq_params = woal_cfg80211_set_txq_params,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ .set_mac_acl = woal_cfg80211_set_mac_acl,
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ .start_radar_detection = woal_cfg80211_start_radar_detection,
+
+ .channel_switch = woal_cfg80211_channel_switch,
+#endif
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+ .update_mgmt_frame_registrations = woal_cfg80211_mgmt_frame_register,
+#else
+ .mgmt_frame_register = woal_cfg80211_mgmt_frame_register,
+#endif
+ .mgmt_tx = woal_cfg80211_mgmt_tx,
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ .mgmt_tx_cancel_wait = woal_cfg80211_mgmt_tx_cancel_wait,
+ .remain_on_channel = woal_cfg80211_remain_on_channel,
+ .cancel_remain_on_channel = woal_cfg80211_cancel_remain_on_channel,
+#endif
+
+#ifdef CONFIG_NL80211_TESTMODE
+ .testmode_cmd = woal_testmode_cmd,
+#endif
+};
+
+/** Region code mapping */
+typedef struct _region_code_t {
+ /** Region */
+ t_u8 region[COUNTRY_CODE_LEN];
+} region_code_t;
+
+static const struct ieee80211_regdomain mrvl_regdom = {
+ .n_reg_rules = 4,
+ .alpha2 = "99",
+ .reg_rules = {
+ /* IEEE 802.11b/g, channels 1..11 */
+ REG_RULE(2412 - 10, 2472 + 10, 40, 6, 20, 0),
+ /* If any */
+ /* IEEE 802.11 channel 14 - Only JP enables
+ * this and for 802.11b only
+ */
+ REG_RULE(2484 - 10, 2484 + 10, 20, 6, 20, 0),
+ /* IEEE 802.11a, channel 36..64 */
+ REG_RULE(5150 - 10, 5350 + 10, 80, 6, 20, 0),
+ /* IEEE 802.11a, channel 100..165 */
+ REG_RULE(5470 - 10, 5850 + 10, 80, 6, 20, 0),
+ }};
+/********************************************************
+ Local Variables
+********************************************************/
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+// clang-format off
+static const struct ieee80211_txrx_stypes
+ ieee80211_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = MBIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ MBIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ MBIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ MBIT(IEEE80211_STYPE_AUTH >> 4) |
+ MBIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ MBIT(IEEE80211_STYPE_ACTION >> 4),
+ },
+ [NL80211_IFTYPE_AP_VLAN] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
+ MBIT(IEEE80211_STYPE_AUTH >> 4) |
+ MBIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
+ MBIT(IEEE80211_STYPE_REASSOC_RESP >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = MBIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ MBIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ MBIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ MBIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ MBIT(IEEE80211_STYPE_AUTH >> 4) |
+ MBIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ MBIT(IEEE80211_STYPE_ACTION >> 4),
+ },
+#endif
+#endif
+ [NL80211_IFTYPE_MESH_POINT] = {
+ .tx = 0x0000,
+ .rx = 0x0000,
+ },
+
+};
+// clang-format on
+#endif
+
+#if CFG80211_VERSION_CODE > KERNEL_VERSION(3, 0, 0)
+/**
+ * NOTE: types in all the sets must be equals to the
+ * initial value of wiphy->interface_modes
+ */
+static const struct ieee80211_iface_limit cfg80211_ap_sta_limits[] = {
+ {.max = 4,
+ .types = MBIT(NL80211_IFTYPE_STATION)
+#ifdef UAP_CFG80211
+ | MBIT(NL80211_IFTYPE_AP)
+#endif
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ | MBIT(NL80211_IFTYPE_P2P_GO) |
+ MBIT(NL80211_IFTYPE_P2P_CLIENT)
+#endif
+#endif
+ }};
+
+static struct ieee80211_iface_combination cfg80211_iface_comb_ap_sta = {
+ .limits = cfg80211_ap_sta_limits,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(cfg80211_ap_sta_limits),
+ .max_interfaces = 4,
+ .beacon_int_infra_match = MTRUE,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ .radar_detect_widths =
+ MBIT(NL80211_CHAN_WIDTH_20_NOHT) | MBIT(NL80211_CHAN_WIDTH_20),
+#endif
+};
+#endif
+
+extern pmoal_handle m_handle[];
+
+#ifdef CONFIG_PM
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+static const struct wiphy_wowlan_support wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT,
+ .n_patterns = MAX_NUM_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = WOWLAN_MAX_PATTERN_LEN,
+ .max_pkt_offset = WOWLAN_MAX_OFFSET_LEN,
+};
+static const struct wiphy_wowlan_support wowlan_support_with_gtk = {
+ .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT |
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE,
+ .n_patterns = MAX_NUM_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = WOWLAN_MAX_PATTERN_LEN,
+ .max_pkt_offset = WOWLAN_MAX_OFFSET_LEN,
+};
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+static const struct wiphy_coalesce_support coalesce_support = {
+ .n_rules = COALESCE_MAX_RULES,
+ .max_delay = MAX_COALESCING_DELAY,
+ .n_patterns = COALESCE_MAX_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = MAX_PATTERN_LEN,
+ .max_pkt_offset = MAX_OFFSET_LEN,
+};
+#endif
+
+/********************************************************
+ Global Variables
+********************************************************/
+
+/********************************************************
+ Local Functions
+********************************************************/
+
+/**
+ * @brief This function check cfg80211 special region code.
+ *
+ * @param region_string Region string
+ *
+ * @return MTRUE/MFALSE
+ */
+t_u8 is_cfg80211_special_region_code(t_u8 *region_string)
+{
+ t_u8 i;
+ region_code_t cfg80211_special_region_code[] = {
+ {"00 "}, {"99 "}, {"98 "}, {"97 "}};
+
+ for (i = 0; i < COUNTRY_CODE_LEN && region_string[i]; i++)
+ region_string[i] = toupper(region_string[i]);
+
+ for (i = 0; i < ARRAY_SIZE(cfg80211_special_region_code); i++) {
+ if (!memcmp(region_string,
+ cfg80211_special_region_code[i].region,
+ COUNTRY_CODE_LEN)) {
+ PRINTM(MIOCTL, "special region code=%s\n",
+ region_string);
+ return MTRUE;
+ }
+ }
+ return MFALSE;
+}
+
+/**
+ * @brief Get the encryption mode from cipher
+ *
+ * @param cipher Cipher cuite
+ * @param wpa_enabled WPA enable or disable
+ *
+ * @return MLAN_ENCRYPTION_MODE_*
+ */
+static int woal_cfg80211_get_encryption_mode(t_u32 cipher, int *wpa_enabled)
+{
+ int encrypt_mode;
+
+ ENTER();
+
+ *wpa_enabled = 0;
+ switch (cipher) {
+ case MW_AUTH_CIPHER_NONE:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_NONE;
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP;
+ *wpa_enabled = 1;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP;
+ *wpa_enabled = 1;
+ break;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP_256;
+ *wpa_enabled = 1;
+ break;
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ case WLAN_CIPHER_SUITE_GCMP:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_GCMP;
+ *wpa_enabled = 1;
+ break;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ encrypt_mode = MLAN_ENCRYPTION_MODE_GCMP_256;
+ *wpa_enabled = 1;
+ break;
+#endif
+ default:
+ encrypt_mode = -1;
+ }
+
+ LEAVE();
+ return encrypt_mode;
+}
+
+/**
+ * @brief get associate failure status code
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ *
+ * @return IEEE status code
+ */
+static int woal_get_assoc_status(moal_private *priv)
+{
+ int ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ t_u16 status = (t_u16)(priv->assoc_status & 0xffff);
+ t_u16 cap = (t_u16)(priv->assoc_status >> 16);
+
+ switch (cap) {
+ case 0xfffd:
+ case 0xfffe:
+ ret = status;
+ break;
+ case 0xfffc:
+ ret = WLAN_STATUS_AUTH_TIMEOUT;
+ break;
+ default:
+ break;
+ }
+ PRINTM(MCMND, "Assoc fail: status=%d, cap=0x%x, IEEE status=%d\n",
+ status, cap, ret);
+ return ret;
+}
+
+/**
+ * @brief Check the pairwise or group cipher for
+ * WEP enabled or not
+ *
+ * @param cipher MLAN Cipher cuite
+ *
+ * @return 1 -- enable or 0 -- disable
+ */
+static int woal_cfg80211_is_alg_wep(t_u32 cipher)
+{
+ int alg = 0;
+ ENTER();
+
+ if (cipher == MLAN_ENCRYPTION_MODE_WEP40 ||
+ cipher == MLAN_ENCRYPTION_MODE_WEP104)
+ alg = 1;
+
+ LEAVE();
+ return alg;
+}
+
+/**
+ * @brief Convert NL80211 interface type to MLAN_BSS_MODE_*
+ *
+ * @param iftype Interface type of NL80211
+ *
+ * @return Driver bss mode
+ */
+static t_u32 woal_nl80211_iftype_to_mode(enum nl80211_iftype iftype)
+{
+ switch (iftype) {
+ case NL80211_IFTYPE_STATION:
+ return MLAN_BSS_MODE_INFRA;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ default:
+ return MLAN_BSS_MODE_AUTO;
+ }
+}
+
+/**
+ * @brief Control WPS Session Enable/Disable
+ *
+ * @param priv Pointer to the moal_private driver data struct
+ * @param enable enable/disable flag
+ *
+ * @return 0 --success, otherwise fail
+ */
+static int woal_wps_cfg(moal_private *priv, int enable)
+{
+ int ret = 0;
+ mlan_ds_wps_cfg *pwps = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ PRINTM(MINFO, "WOAL_WPS_SESSION\n");
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pwps = (mlan_ds_wps_cfg *)req->pbuf;
+ req->req_id = MLAN_IOCTL_WPS_CFG;
+ req->action = MLAN_ACT_SET;
+ pwps->sub_command = MLAN_OID_WPS_CFG_SESSION;
+ if (enable)
+ pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START;
+ else
+ pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END;
+
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+done:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief configure ASSOC IE
+ *
+ * @param priv A pointer to moal private structure
+ * @param ie A pointer to ie data
+ * @param ie_len The length of ie data
+ * @param wait_option wait option
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_assoc_ies_cfg(moal_private *priv, t_u8 *ie, int ie_len,
+ t_u8 wait_option)
+{
+ int bytes_left = ie_len;
+ t_u8 *pcurrent_ptr = ie;
+ int total_ie_len;
+ t_u8 element_len;
+ int ret = MLAN_STATUS_SUCCESS;
+ IEEEtypes_ElementId_e element_id;
+ IEEEtypes_VendorSpecific_t *pvendor_ie;
+ t_u8 wps_oui[] = {0x00, 0x50, 0xf2, 0x04};
+ t_u8 hs20_oui[] = {0x50, 0x6f, 0x9a, 0x10};
+
+ while (bytes_left >= 2) {
+ element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr));
+ element_len = *((t_u8 *)pcurrent_ptr + 1);
+ total_ie_len = element_len + sizeof(IEEEtypes_Header_t);
+ if (bytes_left < total_ie_len) {
+ PRINTM(MERROR,
+ "InterpretIE: Error in processing IE, bytes left < IE length\n");
+ bytes_left = 0;
+ continue;
+ }
+ switch (element_id) {
+ case RSN_IE:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR, "Fail to set RSN IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Set RSN IE\n");
+ break;
+ case VENDOR_SPECIFIC_221:
+ pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr;
+ if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui,
+ sizeof(pvendor_ie->vend_hdr.oui)) &&
+ (pvendor_ie->vend_hdr.oui_type == wps_oui[3])) {
+ PRINTM(MIOCTL, "Enable WPS session\n");
+ woal_wps_cfg(priv, MTRUE);
+ }
+ if (!memcmp(pvendor_ie->vend_hdr.oui, hs20_oui,
+ sizeof(pvendor_ie->vend_hdr.oui)) &&
+ (pvendor_ie->vend_hdr.oui_type == hs20_oui[3])) {
+ PRINTM(MIOCTL,
+ "Hotspot2.0 is enabled for this bss\n");
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_hotspotcfg(priv, wait_option,
+ (HOTSPOT_BY_SUPPLICANT |
+ HOTSPOT_ENABLED))) {
+ PRINTM(MERROR,
+ "Fail to enable hotspot 2.0\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR,
+ "Fail to Set VENDOR SPECIFIC IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL,
+ "Set VENDOR SPECIFIC IE, OUI: %02x:%02x:%02x:%02x\n",
+ pvendor_ie->vend_hdr.oui[0],
+ pvendor_ie->vend_hdr.oui[1],
+ pvendor_ie->vend_hdr.oui[2],
+ pvendor_ie->vend_hdr.oui_type);
+ break;
+ case MOBILITY_DOMAIN:
+ break;
+ case FAST_BSS_TRANSITION:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR, "Fail to set"
+ "FAST_BSS_TRANSITION IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Set FAST_BSS_TRANSITION IE\n");
+ break;
+ case RIC:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR,
+ "Fail to set"
+ "RESOURCE INFORMATION CONTAINER IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL,
+ "Set RESOURCE INFORMATION CONTAINER IE\n");
+ break;
+ case EXT_CAPABILITY:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR,
+ "Fail to set Extended Capabilites IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Set Extended Capabilities IE\n");
+ break;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ case EXTENSION:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR, "Fail to set Extension IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Set Extension IE\n");
+ break;
+ case FRAGMENT:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR, "Fail to set Fragmented IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Set Fragmented IE\n");
+ break;
+#endif
+ default:
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET,
+ pcurrent_ptr, &total_ie_len,
+ wait_option)) {
+ PRINTM(MERROR, "Fail to set GEN IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Set GEN IE\n");
+ break;
+ }
+ pcurrent_ptr += element_len + 2;
+ /* Need to account for IE ID and IE Len */
+ bytes_left -= (element_len + 2);
+ }
+done:
+ return ret;
+}
+
+#ifdef CONFIG_NL80211_TESTMODE
+enum moal_tm_attr {
+ __MOAL_TM_ATTR_INVALID = 0,
+ MOAL_TM_ATTR_CMD = 1,
+ MOAL_TM_ATTR_DATA = 2,
+
+ /* keep last */
+ __MOAL_TM_ATTR_AFTER_LAST,
+ MOAL_TM_ATTR_MAX = __MOAL_TM_ATTR_AFTER_LAST - 1,
+};
+
+static const struct nla_policy moal_tm_policy[MOAL_TM_ATTR_MAX + 1] = {
+ [MOAL_TM_ATTR_CMD] = {.type = NLA_U32},
+ [MOAL_TM_ATTR_DATA] = {.type = NLA_BINARY,
+ .len = MRVDRV_SIZE_OF_CMD_BUFFER},
+};
+
+enum moal_tm_command {
+ MOAL_TM_CMD_HOSTCMD = 0,
+};
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+static int woal_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len)
+#else
+static int woal_testmode_cmd(struct wiphy *wiphy, void *data, int len)
+#endif
+{
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+ moal_private *priv =
+ (moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_misc_cfg *misc_cfg = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ struct nlattr *tb[MOAL_TM_ATTR_MAX + 1];
+ struct sk_buff *skb;
+ int err;
+
+ if (!priv)
+ return -EINVAL;
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ err = nla_parse(tb, MOAL_TM_ATTR_MAX, data, len, moal_tm_policy, NULL);
+#else
+ err = nla_parse(tb, MOAL_TM_ATTR_MAX, data, len, moal_tm_policy);
+#endif
+ if (err)
+ return err;
+
+ if (!tb[MOAL_TM_ATTR_CMD])
+ return -EINVAL;
+
+ switch (nla_get_u32(tb[MOAL_TM_ATTR_CMD])) {
+ case MOAL_TM_CMD_HOSTCMD:
+ if (!tb[MOAL_TM_ATTR_DATA])
+ return -EINVAL;
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL)
+ return -ENOMEM;
+ misc_cfg = (mlan_ds_misc_cfg *)req->pbuf;
+ misc_cfg->sub_command = MLAN_OID_MISC_HOST_CMD;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ req->action = MLAN_ACT_SET;
+ misc_cfg->param.hostcmd.len = nla_len(tb[MOAL_TM_ATTR_DATA]);
+ moal_memcpy_ext(priv->phandle, misc_cfg->param.hostcmd.cmd,
+ nla_data(tb[MOAL_TM_ATTR_DATA]),
+ misc_cfg->param.hostcmd.len,
+ MRVDRV_SIZE_OF_CMD_BUFFER);
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status != MLAN_STATUS_SUCCESS) {
+ err = -EFAULT;
+ goto error;
+ }
+ /* process hostcmd response*/
+ skb = cfg80211_testmode_alloc_reply_skb(
+ wiphy, misc_cfg->param.hostcmd.len);
+ if (!skb) {
+ kfree(req);
+ return -ENOMEM;
+ }
+ err = nla_put(skb, MOAL_TM_ATTR_DATA,
+ misc_cfg->param.hostcmd.len,
+ misc_cfg->param.hostcmd.cmd);
+ if (err) {
+ kfree(req);
+ kfree_skb(skb);
+ return -EMSGSIZE;
+ }
+ err = cfg80211_testmode_reply(skb);
+ kfree(req);
+ return err;
+ default:
+ return -EOPNOTSUPP;
+ }
+error:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ return err;
+}
+#endif
+
+/**
+ * @brief Send domain info command to FW
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option wait option
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static mlan_status woal_send_domain_info_cmd_fw(moal_private *priv,
+ t_u8 wait_option)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ enum ieee80211_band band;
+ struct ieee80211_supported_band *sband = NULL;
+ struct ieee80211_channel *channel = NULL;
+ t_u8 no_of_sub_band = 0;
+ t_u8 no_of_parsed_chan = 0;
+ t_u8 first_chan = 0, next_chan = 0, max_pwr = 0;
+ t_u8 i, flag = 0;
+ mlan_ds_11d_cfg *cfg_11d = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ if (!priv->wdev || !priv->wdev->wiphy) {
+ PRINTM(MERROR, "No wdev or wiphy in priv\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ band = priv->phandle->band;
+ if (!priv->wdev->wiphy->bands[band]) {
+ PRINTM(MERROR, "11D: setting domain info in FW failed band=%d",
+ band);
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ if (MTRUE ==
+ is_cfg80211_special_region_code(priv->phandle->country_code)) {
+ PRINTM(MIOCTL,
+ "skip region code config, cfg80211 special region code: %s\n",
+ priv->phandle->country_code);
+ goto done;
+ }
+ PRINTM(MIOCTL, "Send domain info: country=%c%c band=%d\n",
+ priv->phandle->country_code[0], priv->phandle->country_code[1],
+ band);
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg));
+ if (req == NULL) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ cfg_11d = (mlan_ds_11d_cfg *)req->pbuf;
+ cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO_EXT;
+ req->req_id = MLAN_IOCTL_11D_CFG;
+ req->action = MLAN_ACT_SET;
+
+ /* Set country code */
+ cfg_11d->param.domain_info.country_code[0] =
+ priv->phandle->country_code[0];
+ cfg_11d->param.domain_info.country_code[1] =
+ priv->phandle->country_code[1];
+ cfg_11d->param.domain_info.country_code[2] = ' ';
+ cfg_11d->param.domain_info.band = band;
+
+ sband = priv->wdev->wiphy->bands[band];
+ for (i = 0; (i < sband->n_channels) &&
+ (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D);
+ i++) {
+ channel = &sband->channels[i];
+ if (channel->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (!flag) {
+ flag = 1;
+ next_chan = first_chan = (t_u32)channel->hw_value;
+ max_pwr = channel->max_power;
+ no_of_parsed_chan = 1;
+ continue;
+ }
+
+ if (channel->hw_value == next_chan + 1 &&
+ channel->max_power == max_pwr) {
+ next_chan++;
+ no_of_parsed_chan++;
+ } else {
+ cfg_11d->param.domain_info.sub_band[no_of_sub_band]
+ .first_chan = first_chan;
+ cfg_11d->param.domain_info.sub_band[no_of_sub_band]
+ .no_of_chan = no_of_parsed_chan;
+ cfg_11d->param.domain_info.sub_band[no_of_sub_band]
+ .max_tx_pwr = max_pwr;
+ no_of_sub_band++;
+ next_chan = first_chan = (t_u32)channel->hw_value;
+ max_pwr = channel->max_power;
+ no_of_parsed_chan = 1;
+ }
+ }
+
+ if (flag && (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D)) {
+ cfg_11d->param.domain_info.sub_band[no_of_sub_band].first_chan =
+ first_chan;
+ cfg_11d->param.domain_info.sub_band[no_of_sub_band].no_of_chan =
+ no_of_parsed_chan;
+ cfg_11d->param.domain_info.sub_band[no_of_sub_band].max_tx_pwr =
+ max_pwr;
+ no_of_sub_band++;
+ }
+ cfg_11d->param.domain_info.no_of_sub_band = no_of_sub_band;
+
+ PRINTM(MCMND, "CFG80211: Country=%c%c, band=%d, no_of_sub_band=%d\n",
+ priv->phandle->country_code[0], priv->phandle->country_code[1],
+ priv->phandle->band, cfg_11d->param.domain_info.no_of_sub_band);
+ /* Send domain info command to FW */
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = MLAN_STATUS_FAILURE;
+ PRINTM(MERROR, "11D: Error setting domain info in FW\n");
+ goto done;
+ }
+
+done:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Request the driver to change the channel and
+ * change domain info according to that channel
+ *
+ * @param priv A pointer to moal_private structure
+ * @param chan A pointer to ieee80211_channel structure
+ * @param channel_type Channel type of nl80211_channel_type
+ * @param wait_option wait option
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_set_rf_channel(moal_private *priv, struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ t_u8 wait_option)
+{
+ int ret = 0;
+ t_u32 mode, config_bands = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_radio_cfg *radio_cfg = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ if (!chan) {
+ LEAVE();
+ return -EINVAL;
+ }
+ mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype);
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
+ radio_cfg->sub_command = MLAN_OID_BAND_CFG;
+ req->req_id = MLAN_IOCTL_RADIO_CFG;
+ /* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN
+ */
+ req->action = MLAN_ACT_GET;
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto done;
+ }
+ req->action = MLAN_ACT_SET;
+ priv->phandle->band = chan->band;
+ /* Set appropriate bands */
+ if (chan->band == IEEE80211_BAND_2GHZ)
+ config_bands = BAND_B | BAND_G | BAND_GN;
+ else {
+ config_bands = BAND_AN | BAND_A;
+ }
+ if (mode == MLAN_BSS_MODE_IBSS) {
+ radio_cfg->param.band_cfg.adhoc_start_band = config_bands;
+ radio_cfg->param.band_cfg.adhoc_channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ }
+
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto done;
+ }
+ woal_send_domain_info_cmd_fw(priv, wait_option);
+
+ PRINTM(MINFO, "Setting band %d, and mode = %d channel=%d\n",
+ config_bands, mode,
+ ieee80211_frequency_to_channel(chan->center_freq));
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_change_adhoc_chan(
+ priv, ieee80211_frequency_to_channel(chan->center_freq),
+ wait_option)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+done:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Set ewpa mode
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ * @param ssid_bssid A pointer to mlan_ssid_bssid structure
+ *
+ * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --
+ * success, otherwise fail
+ */
+mlan_status woal_set_ewpa_mode(moal_private *priv, t_u8 wait_option,
+ mlan_ssid_bssid *ssid_bssid)
+{
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_sec_cfg *sec = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ if (!priv->phandle->card_info->embedded_supp) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ /* Fill request buffer */
+ sec = (mlan_ds_sec_cfg *)req->pbuf;
+ sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE;
+ req->req_id = MLAN_IOCTL_SEC_CFG;
+ req->action = MLAN_ACT_GET;
+
+ /* Try Get All */
+ memset(&sec->param.passphrase, 0, sizeof(mlan_ds_passphrase));
+ moal_memcpy_ext(priv->phandle, &sec->param.passphrase.ssid,
+ &ssid_bssid->ssid, sizeof(sec->param.passphrase.ssid),
+ sizeof(sec->param.passphrase.ssid));
+ moal_memcpy_ext(priv->phandle, &sec->param.passphrase.bssid,
+ &ssid_bssid->bssid, MLAN_MAC_ADDR_LENGTH,
+ sizeof(sec->param.passphrase.bssid));
+ sec->param.passphrase.psk_type = MLAN_PSK_QUERY;
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status != MLAN_STATUS_SUCCESS)
+ goto error;
+ sec->param.ewpa_enabled = MFALSE;
+ if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) {
+ if (sec->param.passphrase.psk.passphrase.passphrase_len > 0)
+ sec->param.ewpa_enabled = MTRUE;
+ } else if (sec->param.passphrase.psk_type == MLAN_PSK_PMK)
+ sec->param.ewpa_enabled = MTRUE;
+
+ sec->sub_command = MLAN_OID_SEC_CFG_EWPA_ENABLED;
+ req->action = MLAN_ACT_SET;
+
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, wait_option);
+
+error:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return status;
+}
+
+/**
+ * @brief Set encryption mode and enable WPA
+ *
+ * @param priv A pointer to moal_private structure
+ * @param encrypt_mode Encryption mode
+ * @param wpa_enabled WPA enable or not
+ * @param wait_option wait option
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_set_auth(moal_private *priv, int encrypt_mode,
+ int wpa_enabled, t_u8 wait_option)
+{
+ int ret = 0;
+
+ ENTER();
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_encrypt_mode(priv, wait_option, encrypt_mode))
+ ret = -EFAULT;
+
+ if (wpa_enabled) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_wpa_enable(priv, wait_option, 1))
+ ret = -EFAULT;
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Informs the CFG802.11 subsystem of a new BSS connection.
+ *
+ * The following information are sent to the CFG802.11 subsystem
+ * to register the new BSS connection. If we do not register the new BSS,
+ * a kernel panic will result.
+ * - MAC address
+ * - Capabilities
+ * - Beacon period
+ * - RSSI value
+ * - Channel
+ * - Supported rates IE
+ * - Extended capabilities IE
+ * - DS parameter set IE
+ * - HT Capability IE
+ * - Vendor Specific IE (221)
+ * - WPA IE
+ * - RSN IE
+ *
+ * @param priv A pointer to moal_private structure
+ * @param ssid_bssid A pointer to A pointer to mlan_ssid_bssid structure
+ * @param wait_option wait_option
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status woal_inform_bss_from_scan_result(moal_private *priv,
+ mlan_ssid_bssid *ssid_bssid,
+ t_u8 wait_option)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ struct ieee80211_channel *chan;
+ mlan_scan_resp scan_resp;
+ BSSDescriptor_t *scan_table;
+ t_u64 ts = 0;
+ u16 cap_info = 0;
+ int i = 0;
+ struct cfg80211_bss *pub = NULL;
+
+ ENTER();
+ if (!priv->wdev || !priv->wdev->wiphy) {
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ memset(&scan_resp, 0, sizeof(scan_resp));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_scan_table(priv, wait_option, &scan_resp)) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ if (scan_resp.num_in_scan_table) {
+ scan_table = (BSSDescriptor_t *)scan_resp.pscan_table;
+ for (i = 0; i < scan_resp.num_in_scan_table; i++) {
+ if (ssid_bssid) {
+ /* Inform specific BSS only */
+ if (memcmp(ssid_bssid->ssid.ssid,
+ scan_table[i].ssid.ssid,
+ ssid_bssid->ssid.ssid_len) ||
+ memcmp(ssid_bssid->bssid,
+ scan_table[i].mac_address, ETH_ALEN))
+ continue;
+ }
+ if (!scan_table[i].freq) {
+ scan_table[i].freq =
+ ieee80211_channel_to_frequency(
+ (int)scan_table[i].channel
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ ,
+ woal_band_cfg_to_ieee_band(
+ scan_table[i].bss_band)
+#endif
+ );
+ }
+ chan = ieee80211_get_channel(priv->wdev->wiphy,
+ scan_table[i].freq);
+ if (!chan) {
+ PRINTM(MCMND,
+ "Fail to get chan with freq: channel=%d freq=%d\n",
+ (int)scan_table[i].channel,
+ (int)scan_table[i].freq);
+ continue;
+ }
+#if defined(WIFI_DIRECT_SUPPORT)
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
+ !ssid_bssid) {
+ if (!strncmp(scan_table[i].ssid.ssid, "DIRECT-",
+ strlen("DIRECT-"))) {
+ PRINTM(MCMND,
+ "wlan: P2P device " MACSTR
+ " found, channel=%d\n",
+ MAC2STR(scan_table[i]
+ .mac_address),
+ (int)chan->hw_value);
+ }
+ }
+#endif
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ /** Andorid's Location service is expecting timestamp to
+ * be local time (in microsecond) since boot; and not
+ * the TSF found in the beacon. */
+ ts = ktime_to_us(ktime_get_boottime());
+#else
+ moal_memcpy_ext(priv->phandle, &ts,
+ scan_table[i].time_stamp, sizeof(ts),
+ sizeof(ts));
+#endif
+ moal_memcpy_ext(priv->phandle, &cap_info,
+ &scan_table[i].cap_info,
+ sizeof(cap_info), sizeof(cap_info));
+ pub = cfg80211_inform_bss(
+ priv->wdev->wiphy, chan,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ CFG80211_BSS_FTYPE_UNKNOWN,
+#endif
+ scan_table[i].mac_address, ts, cap_info,
+ scan_table[i].beacon_period,
+ scan_table[i].pbeacon_buf +
+ WLAN_802_11_FIXED_IE_SIZE,
+ scan_table[i].beacon_buf_size -
+ WLAN_802_11_FIXED_IE_SIZE,
+ -RSSI_DBM_TO_MDM(scan_table[i].rssi),
+ GFP_KERNEL);
+ if (pub) {
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ pub->len_information_elements =
+ pub->len_beacon_ies;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(priv->wdev->wiphy, pub);
+#else
+ cfg80211_put_bss(pub);
+#endif
+ }
+ }
+ }
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Informs the CFG802.11 subsystem of a new IBSS connection.
+ *
+ * The following information are sent to the CFG802.11 subsystem
+ * to register the new IBSS connection. If we do not register the
+ * new IBSS, a kernel panic will result.
+ * - MAC address
+ * - Capabilities
+ * - Beacon period
+ * - RSSI value
+ * - Channel
+ * - Supported rates IE
+ * - Extended capabilities IE
+ * - DS parameter set IE
+ * - HT Capability IE
+ * - Vendor Specific IE (221)
+ * - WPA IE
+ * - RSN IE
+ *
+ * @param priv A pointer to moal_private structure
+ * @param cahn A pointer to ieee80211_channel structure
+ * @param beacon_interval Beacon interval
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static mlan_status woal_cfg80211_inform_ibss_bss(moal_private *priv,
+ struct ieee80211_channel *chan,
+ t_u16 beacon_interval)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_bss_info bss_info;
+ mlan_ds_get_signal signal;
+ t_u8 ie_buf[MLAN_MAX_SSID_LENGTH + sizeof(IEEEtypes_Header_t)];
+ int ie_len = 0;
+ struct cfg80211_bss *bss = NULL;
+
+ ENTER();
+
+ ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ if (ret)
+ goto done;
+
+ memset(ie_buf, 0, sizeof(ie_buf));
+ ie_buf[0] = WLAN_EID_SSID;
+ ie_buf[1] = bss_info.ssid.ssid_len;
+
+ moal_memcpy_ext(priv->phandle, &ie_buf[sizeof(IEEEtypes_Header_t)],
+ &bss_info.ssid.ssid, bss_info.ssid.ssid_len,
+ sizeof(ie_buf) - sizeof(IEEEtypes_Header_t));
+ ie_len = ie_buf[1] + sizeof(IEEEtypes_Header_t);
+
+ /* Get signal information from the firmware */
+ memset(&signal, 0, sizeof(mlan_ds_get_signal));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
+ PRINTM(MERROR, "Error getting signal information\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ bss = cfg80211_inform_bss(priv->wdev->wiphy, chan,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ CFG80211_BSS_FTYPE_UNKNOWN,
+#endif
+ bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
+ beacon_interval, ie_buf, ie_len,
+ signal.bcn_rssi_avg, GFP_KERNEL);
+ if (bss)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(priv->wdev->wiphy, bss);
+#else
+ cfg80211_put_bss(bss);
+#endif
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Process country IE before assoicate
+ *
+ * @param priv A pointer to moal_private structure
+ * @param bss A pointer to cfg80211_bss structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_process_country_ie(moal_private *priv, struct cfg80211_bss *bss)
+{
+ u8 *country_ie, country_ie_len;
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_11d_cfg *cfg_11d = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+ country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+ if (!country_ie) {
+ PRINTM(MIOCTL, "No country IE found!\n");
+ woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
+ LEAVE();
+ return 0;
+ }
+
+ country_ie_len = country_ie[1];
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
+ PRINTM(MIOCTL, "Wrong Country IE length!\n");
+ woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
+ LEAVE();
+ return 0;
+ }
+ priv->phandle->country_code[0] = country_ie[2];
+ priv->phandle->country_code[1] = country_ie[3];
+ priv->phandle->country_code[2] = ' ';
+ if (is_cfg80211_special_region_code(priv->phandle->country_code)) {
+ PRINTM(MIOCTL, "Skip special region code in CountryIE");
+ LEAVE();
+ return 0;
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_region_code(priv, priv->phandle->country_code))
+ PRINTM(MERROR, "Set country code failed!\n");
+
+ /* Allocate an IOCTL request buffer */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg));
+ if (req == NULL) {
+ PRINTM(MERROR, "Fail to allocate mlan_ds_11d_cfg buffer\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ cfg_11d = (mlan_ds_11d_cfg *)req->pbuf;
+ cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO_EXT;
+ req->req_id = MLAN_IOCTL_11D_CFG;
+ req->action = MLAN_ACT_SET;
+
+ /* Set country code */
+ cfg_11d->param.domain_info.country_code[0] =
+ priv->phandle->country_code[0];
+ cfg_11d->param.domain_info.country_code[1] =
+ priv->phandle->country_code[1];
+ cfg_11d->param.domain_info.country_code[2] = ' ';
+
+ /** IEEE80211_BAND_2GHZ or IEEE80211_BAND_5GHZ */
+ cfg_11d->param.domain_info.band = priv->phandle->band;
+
+ country_ie_len -= COUNTRY_CODE_LEN;
+ cfg_11d->param.domain_info.no_of_sub_band = MIN(
+ MRVDRV_MAX_SUBBAND_802_11D,
+ (country_ie_len / sizeof(struct ieee80211_country_ie_triplet)));
+ moal_memcpy_ext(priv->phandle,
+ (u8 *)cfg_11d->param.domain_info.sub_band,
+ &country_ie[2] + COUNTRY_CODE_LEN,
+ cfg_11d->param.domain_info.no_of_sub_band *
+ sizeof(mlan_ds_subband_set_t),
+ sizeof(cfg_11d->param.domain_info.sub_band));
+
+ PRINTM(MCMND, "11D: Country IE: %c%c band=%d no_of_sub_band=%d\n",
+ country_ie[2], country_ie[3], priv->phandle->band,
+ cfg_11d->param.domain_info.no_of_sub_band);
+ /* Send domain info command to FW */
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = MLAN_STATUS_FAILURE;
+ PRINTM(MERROR, "11D: Error setting domain info in FW\n");
+ goto done;
+ }
+done:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Request scan based on connect parameter
+ *
+ * @param priv A pointer to moal_private structure
+ * @param conn_param A pointer to connect parameters
+ * @param wait_option wait option
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_connect_scan(moal_private *priv,
+ struct cfg80211_connect_params *conn_param,
+ t_u8 wait_option)
+{
+ moal_handle *handle = priv->phandle;
+ int ret = 0;
+ wlan_user_scan_cfg scan_req;
+ enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ int chan_idx = 0, i;
+
+ ENTER();
+ if (handle->scan_pending_on_block == MTRUE) {
+ PRINTM(MINFO, "scan already in processing...\n");
+ LEAVE();
+ return ret;
+ }
+#ifdef REASSOCIATION
+ if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) {
+ PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n");
+ LEAVE();
+ return -EBUSY;
+ }
+#endif /* REASSOCIATION */
+ priv->report_scan_result = MTRUE;
+ memset(&scan_req, 0x00, sizeof(scan_req));
+ moal_memcpy_ext(priv->phandle, scan_req.ssid_list[0].ssid,
+ conn_param->ssid, conn_param->ssid_len,
+ sizeof(scan_req.ssid_list[0].ssid));
+ scan_req.ssid_list[0].max_len = 0;
+ if (conn_param->channel) {
+ scan_req.chan_list[0].chan_number =
+ conn_param->channel->hw_value;
+ scan_req.chan_list[0].radio_type = conn_param->channel->band;
+ if (conn_param->channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ scan_req.chan_list[0].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE;
+ else if (conn_param->channel->flags & IEEE80211_CHAN_RADAR)
+ scan_req.chan_list[0].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
+ else
+ scan_req.chan_list[0].scan_type = MLAN_SCAN_TYPE_ACTIVE;
+ scan_req.chan_list[0].scan_time = 0;
+ } else {
+ for (band = 0; (band < IEEE80211_NUM_BANDS); band++) {
+ if (!priv->wdev->wiphy->bands[band])
+ continue;
+ sband = priv->wdev->wiphy->bands[band];
+ for (i = 0; (i < sband->n_channels); i++) {
+ ch = &sband->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ scan_req.chan_list[chan_idx].radio_type = band;
+ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ scan_req.chan_list[chan_idx].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE;
+ else if (ch->flags & IEEE80211_CHAN_RADAR)
+ scan_req.chan_list[chan_idx].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
+ else
+ scan_req.chan_list[chan_idx].scan_type =
+ MLAN_SCAN_TYPE_ACTIVE;
+ scan_req.chan_list[chan_idx].chan_number =
+ (u32)ch->hw_value;
+ chan_idx++;
+ }
+ }
+ }
+ moal_memcpy_ext(priv->phandle, scan_req.random_mac, priv->random_mac,
+ ETH_ALEN, sizeof(scan_req.random_mac));
+ ret = woal_request_userscan(priv, wait_option, &scan_req);
+#ifdef REASSOCIATION
+ MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
+#endif
+ LEAVE();
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+/**
+ * @brief Save assoc parameters for roaming
+ *
+ * @param priv A pointer to moal_private
+ * @param req A pointer to cfg80211_assoc_request structure
+ */
+void woal_save_assoc_params(moal_private *priv,
+ struct cfg80211_assoc_request *req,
+ mlan_ssid_bssid *ssid_bssid)
+{
+ ENTER();
+
+ if (req->bss->channel) {
+ priv->sme_current.channel = &priv->conn_chan;
+ moal_memcpy_ext(priv->phandle, priv->sme_current.channel,
+ req->bss->channel,
+ sizeof(struct ieee80211_channel),
+ sizeof(struct ieee80211_channel));
+ }
+ priv->sme_current.bssid = priv->conn_bssid;
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
+ req->bss->bssid, MLAN_MAC_ADDR_LENGTH,
+ MLAN_MAC_ADDR_LENGTH);
+ if (req->ie && req->ie_len) {
+ priv->sme_current.ie = kzalloc(req->ie_len, GFP_KERNEL);
+ priv->sme_current.ie_len = req->ie_len;
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ie,
+ req->ie, req->ie_len, priv->sme_current.ie_len);
+ }
+ moal_memcpy_ext(priv->phandle, &priv->sme_current.crypto, &req->crypto,
+ sizeof(struct cfg80211_crypto_settings),
+ sizeof(struct cfg80211_crypto_settings));
+ priv->sme_current.flags = req->flags;
+ moal_memcpy_ext(priv->phandle, &priv->sme_current.ht_capa,
+ &req->ht_capa, sizeof(struct ieee80211_ht_cap),
+ sizeof(struct ieee80211_ht_cap));
+ moal_memcpy_ext(priv->phandle, &priv->sme_current.ht_capa_mask,
+ &req->ht_capa_mask, sizeof(struct ieee80211_ht_cap),
+ sizeof(struct ieee80211_ht_cap));
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ moal_memcpy_ext(priv->phandle, &priv->sme_current.vht_capa,
+ &req->vht_capa, sizeof(struct ieee80211_vht_cap),
+ sizeof(struct ieee80211_vht_cap));
+ moal_memcpy_ext(priv->phandle, &priv->sme_current.vht_capa_mask,
+ &req->vht_capa_mask, sizeof(struct ieee80211_vht_cap),
+ sizeof(struct ieee80211_vht_cap));
+#endif
+ if (ssid_bssid && ssid_bssid->ssid.ssid_len) {
+ priv->sme_current.ssid = priv->conn_ssid;
+ memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH);
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ssid,
+ ssid_bssid->ssid.ssid,
+ ssid_bssid->ssid.ssid_len,
+ sizeof(priv->conn_ssid));
+ priv->conn_ssid_len = ssid_bssid->ssid.ssid_len;
+ }
+ LEAVE();
+}
+
+/**
+ * @brief Save auth parameters for roaming
+ *
+ * @param priv A pointer to moal_private
+ * @param req A pointer to struct cfg80211_auth_request
+ */
+void woal_save_auth_params(moal_private *priv,
+ struct cfg80211_auth_request *req)
+{
+ ENTER();
+ woal_clear_conn_params(priv);
+ priv->sme_current.auth_type = req->auth_type;
+ priv->sme_current.key_idx = req->key_idx;
+ priv->sme_current.key_len = req->key_len;
+ if (req->key && req->key_len && (req->key_len <= MAX_WEP_KEY_SIZE)) {
+ priv->sme_current.key = priv->conn_wep_key;
+ moal_memcpy_ext(priv->phandle, (t_u8 *)priv->sme_current.key,
+ req->key, req->key_len,
+ sizeof(priv->conn_wep_key));
+ }
+ LEAVE();
+}
+
+/**
+ * @brief This function is authentication handler when host MLME
+ * enable.
+ * In this case driver will prepare and send Auth Req.
+ *
+ * @param wiphy A pointer to wiphy.
+ *
+ * @param dev A pointer to net_device
+ *
+ * @param req A pointer to cfg80211_auth_request
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_authenticate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_auth_request *req)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ IEEE80211_MGMT *mgmt = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ pmlan_buffer pmbuf = NULL;
+ t_u32 pkt_type, tx_control;
+ t_u16 packet_len = 0, auth_alg;
+ t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ int ret = 0;
+
+ t_u8 trans = 1, status_code = 0;
+ t_u8 *varptr = NULL;
+ mlan_ssid_bssid *ssid_bssid;
+ moal_handle *handle = priv->phandle;
+ int i;
+
+ ENTER();
+
+ priv->cfg_disconnect = MFALSE;
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return -EFAULT;
+ }
+#endif
+ if (priv->wdev->iftype != NL80211_IFTYPE_STATION
+#ifdef WIFI_DIRECT_SUPPORT
+ && priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT
+
+#endif /* WIFI_DIRECT_SUPPORT */
+ ) {
+ PRINTM(MERROR,
+ "Received infra auth request when interface not in infra mode\n");
+ LEAVE();
+ return -EINVAL;
+ }
+
+ ssid_bssid = kzalloc(sizeof(mlan_ssid_bssid), GFP_ATOMIC);
+ if (!ssid_bssid) {
+ PRINTM(MERROR, "Fail to allocate ssid_bssid buffer\n");
+ LEAVE();
+ return -ENOMEM;
+ }
+ moal_memcpy_ext(priv->phandle, ssid_bssid->bssid, req->bss->bssid,
+ ETH_ALEN, sizeof(ssid_bssid->bssid));
+ /* Not allowed to connect to the same AP which is already connected
+ with other interface */
+ for (i = 0; i < handle->priv_num; i++) {
+ if (handle->priv[i] != priv &&
+ MTRUE == woal_is_connected(handle->priv[i], ssid_bssid)) {
+ PRINTM(MMSG,
+ "wlan: already connected with other interface, bssid " MACSTR
+ "\n",
+ MAC2STR(handle->priv[i]->cfg_bssid));
+ kfree(ssid_bssid);
+ LEAVE();
+ return -EINVAL;
+ }
+ }
+ if (MLAN_STATUS_SUCCESS != woal_find_bssid(priv, req->bss->bssid)) {
+ PRINTM(MMSG, "bssid not find in scan list\n");
+ kfree(ssid_bssid);
+ LEAVE();
+ return -EFAULT;
+ }
+ kfree(ssid_bssid);
+
+ if ((priv->auth_alg != WLAN_AUTH_SAE) &&
+ (priv->auth_flag & HOST_MLME_AUTH_PENDING)) {
+ PRINTM(MERROR, "pending auth on going\n");
+ LEAVE();
+ return -EBUSY;
+ }
+
+ /** cancel pending scan */
+ woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
+
+#ifdef WIFI_DIRECT_SUPPORT
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
+ (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ /* if bsstype == wifi direct, and iftype == station or p2p
+ * client, that means wpa_supplicant wants to enable wifi direct
+ * functionality, so we should init p2p client.
+ *
+ * Note that due to kernel iftype check, ICS wpa_supplicant
+ * could not updaet iftype to init p2p client, so we have to
+ * done it here.
+ * */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_init_p2p_client(priv)) {
+ PRINTM(MERROR,
+ "Init p2p client for wpa_supplicant failed.\n");
+ ret = -EFAULT;
+ LEAVE();
+ return ret;
+ }
+ }
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
+ /* WAR for P2P connection with vendor TV */
+ woal_sched_timeout(200);
+ }
+#endif
+
+ /*enable auth register frame*/
+ if (priv->auth_flag == 0) {
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MTRUE);
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_DEAUTH, MTRUE);
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_DISASSOC, MTRUE);
+ }
+
+#define HEADER_SIZE 8
+ // frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4
+#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6)
+ // 6 = auth_alg + auth_transaction +auth_status
+#define AUTH_BODY_LEN 6
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
+ packet_len = (t_u16)req->ie_len + req->auth_data_len + MGMT_HEADER_LEN +
+ AUTH_BODY_LEN;
+#else
+ packet_len = (t_u16)req->ie_len + req->sae_data_len + MGMT_HEADER_LEN +
+ AUTH_BODY_LEN;
+#endif
+ pmbuf = woal_alloc_mlan_buffer(priv->phandle,
+ MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE +
+ packet_len + sizeof(packet_len));
+
+ if (!pmbuf) {
+ PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0,
+ KEY_INDEX_CLEAR_ALL, NULL, 1,
+ MOAL_IOCTL_WAIT)) {
+ /* Disable keys and clear all previous security settings */
+ ret = -EFAULT;
+ goto done;
+ }
+
+ switch (req->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ auth_alg = WLAN_AUTH_OPEN;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ auth_alg = WLAN_AUTH_SHARED_KEY;
+ break;
+ case NL80211_AUTHTYPE_FT:
+ auth_alg = WLAN_AUTH_FT;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ auth_alg = WLAN_AUTH_LEAP;
+ break;
+ case NL80211_AUTHTYPE_SAE:
+ auth_alg = WLAN_AUTH_SAE;
+ break;
+ default:
+ PRINTM(MERROR, "Unsupported auth type=%d\n", req->auth_type);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ if (ret)
+ goto done;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_alg)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (req->key && ((auth_alg == WLAN_AUTH_OPEN) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY))) {
+ PRINTM(MMSG, "Setting wep encryption with key len %d\n",
+ req->key_len);
+ /* Set the WEP key */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_wep_keys(priv, req->key, req->key_len,
+ req->key_idx, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ /* Enable the WEP key by key index */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_wep_keys(priv, NULL, 0, req->key_idx,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+#define AUTH_TX_DEFAULT_WAIT_TIME 1200
+ if (priv->auth_flag == 0) {
+ if (woal_cfg80211_remain_on_channel_cfg(
+ priv, MOAL_IOCTL_WAIT, MFALSE, (t_u8 *)&status,
+ req->bss->channel, 0, AUTH_TX_DEFAULT_WAIT_TIME)) {
+ PRINTM(MERROR, "Fail to configure remain on channel\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (status == MLAN_STATUS_SUCCESS) {
+ priv->phandle->remain_on_channel = MTRUE;
+ moal_memcpy_ext(priv->phandle, &(priv->phandle->chan),
+ req->bss->channel,
+ sizeof(struct ieee80211_channel),
+ sizeof(priv->phandle->chan));
+ } else {
+ PRINTM(MERROR,
+ "HostMlme %s: Set remain on Channel: with status=%d\n",
+ dev->name, status);
+ }
+ }
+
+ pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
+ pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
+ tx_control = 0;
+ /* Add pkt_type and tx_control */
+ moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset,
+ &pkt_type, sizeof(pkt_type), sizeof(pkt_type));
+ moal_memcpy_ext(priv->phandle,
+ pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
+ &tx_control, sizeof(tx_control), sizeof(tx_control));
+
+ mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
+ HEADER_SIZE + sizeof(packet_len));
+ memset(mgmt, 0, MGMT_HEADER_LEN);
+ /**Authentication Frame: Frame Control*/
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ /**Authentication Frame: Destination Address*/
+ moal_memcpy_ext(priv->phandle, mgmt->da, req->bss->bssid, ETH_ALEN,
+ sizeof(mgmt->da));
+ /**Authentication Frame: Source Address*/
+ moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
+ sizeof(mgmt->sa));
+ /**Authentication Frame: BSSID*/
+ moal_memcpy_ext(priv->phandle, mgmt->bssid, req->bss->bssid, ETH_ALEN,
+ sizeof(mgmt->bssid));
+ moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN,
+ sizeof(mgmt->addr4));
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
+ if (req->auth_data_len >= 4) {
+ if (req->auth_type == NL80211_AUTHTYPE_SAE) {
+ __le16 *pos = (__le16 *)req->auth_data;
+
+ trans = le16_to_cpu(pos[0]);
+ status_code = le16_to_cpu(pos[1]);
+ }
+ moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable),
+ req->auth_data + 4, req->auth_data_len - 4,
+ req->auth_data_len - 4);
+ varptr = (t_u8 *)&mgmt->u.auth.variable +
+ (req->auth_data_len - 4);
+ packet_len -= 4;
+ }
+#else
+ if (req->sae_data_len >= 4) {
+ if (req->auth_type == NL80211_AUTHTYPE_SAE) {
+ __le16 *pos = (__le16 *)req->sae_data;
+
+ trans = le16_to_cpu(pos[0]);
+ status_code = le16_to_cpu(pos[1]);
+ }
+ moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable),
+ req->sae_data + 4, req->sae_data_len - 4,
+ req->sae_data_len - 4);
+ varptr = (t_u8 *)&mgmt->u.auth.variable +
+ (req->sae_data_len - 4);
+ packet_len -= 4;
+ }
+#endif
+ /*Add packet len*/
+ moal_memcpy_ext(priv->phandle,
+ pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
+ &packet_len, sizeof(packet_len), sizeof(packet_len));
+
+ /**Authentication Frame: Authentication Alg*/
+ mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
+ mgmt->u.auth.auth_transaction = trans;
+ /**Authentication Frame: Status code*/
+ mgmt->u.auth.status_code = status_code;
+
+ if (req->ie && req->ie_len) {
+ if (!varptr) {
+ varptr = (t_u8 *)&mgmt->u.auth.variable;
+ }
+ moal_memcpy_ext(priv->phandle, (t_u8 *)varptr, req->ie,
+ req->ie_len, req->ie_len);
+ }
+
+ pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len);
+ pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
+ pmbuf->bss_index = priv->bss_index;
+ pmbuf->priority = 7;
+
+ priv->host_mlme = MTRUE;
+ priv->auth_flag = HOST_MLME_AUTH_PENDING;
+ priv->auth_alg = cpu_to_le16(auth_alg);
+
+ PRINTM(MCMND, "wlan: HostMlme %s send auth to bssid " MACSTR "\n",
+ dev->name, MAC2STR(req->bss->bssid));
+ DBG_HEXDUMP(MDAT_D, "Auth:", pmbuf->pbuf + pmbuf->data_offset,
+ pmbuf->data_len);
+ if (priv->bss_type == MLAN_BSS_TYPE_STA)
+ woal_save_auth_params(priv, req);
+ status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
+
+ switch (status) {
+ case MLAN_STATUS_PENDING:
+ atomic_inc(&priv->phandle->tx_pending);
+ queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
+ break;
+ case MLAN_STATUS_SUCCESS:
+ woal_free_mlan_buffer(priv->phandle, pmbuf);
+ break;
+ case MLAN_STATUS_FAILURE:
+ default:
+ woal_free_mlan_buffer(priv->phandle, pmbuf);
+ priv->host_mlme = MFALSE;
+ priv->auth_flag = 0;
+ priv->auth_alg = 0xFFFF;
+ ret = -EFAULT;
+ break;
+ }
+done:
+ if (ret) {
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
+ if (priv->phandle->remain_on_channel) {
+ woal_cfg80211_remain_on_channel_cfg(
+ priv, MOAL_IOCTL_WAIT, MTRUE, (t_u8 *)&status,
+ NULL, 0, 0);
+ priv->phandle->remain_on_channel = MFALSE;
+ }
+ }
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This workqueue function handles association response in host mlme
+ * case
+ *
+ * @param work A pointer to work_struct
+ *
+ * @return N/A
+ */
+void woal_host_mlme_work_queue(struct work_struct *work)
+{
+ moal_handle *handle = container_of(work, moal_handle, host_mlme_work);
+ moal_private *priv = (moal_private *)handle->host_mlme_priv;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ if (priv) {
+ if (priv->auth_flag & HOST_MLME_AUTH_DONE) {
+ priv->auth_flag = 0;
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH,
+ MFALSE);
+
+ if (priv->phandle->remain_on_channel) {
+ woal_cfg80211_remain_on_channel_cfg(
+ priv, MOAL_IOCTL_WAIT, MTRUE,
+ (t_u8 *)&status, NULL, 0, 0);
+ priv->phandle->remain_on_channel = MFALSE;
+ }
+ PRINTM(MCMND, "wlan: HostMlme %s auth success\n",
+ priv->netdev->name);
+ }
+ }
+}
+
+/**
+ * @brief This workqueue function handles association response in event queue
+ * case
+ *
+ * @param priv pointer to moal_private
+ * @param assoc_rsp pointer to mlan_ds_misc_assoc_rsp
+ *
+ * @return N/A
+ */
+void woal_host_mlme_process_assoc_resp(moal_private *priv,
+ mlan_ds_misc_assoc_rsp *assoc_rsp)
+{
+ struct cfg80211_bss *bss = NULL;
+ unsigned long flags;
+
+ if (priv) {
+ if (priv->auth_flag & HOST_MLME_ASSOC_DONE) {
+ priv->auth_flag = 0;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
+ bss = cfg80211_get_bss(
+ priv->wdev->wiphy, NULL, priv->conn_bssid,
+ priv->conn_ssid, priv->conn_ssid_len,
+ IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
+#else
+ bss = cfg80211_get_bss(
+ priv->wdev->wiphy, NULL, priv->conn_bssid,
+ priv->conn_ssid, priv->conn_ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+#endif
+ if (!bss) {
+ PRINTM(MERROR, "HostMlme %s:Fail to get bss\n",
+ priv->netdev->name);
+ return;
+ }
+
+ if (assoc_rsp->assoc_resp_len) {
+ PRINTM(MCMND,
+ "HostMlme: %s assoc_resp_len=%d, frame_control=0x%x\n",
+ priv->netdev->name,
+ assoc_rsp->assoc_resp_len,
+ ((struct ieee80211_mgmt *)
+ assoc_rsp->assoc_resp_buf)
+ ->frame_control);
+ if (ieee80211_is_assoc_resp(
+ ((struct ieee80211_mgmt *)
+ assoc_rsp->assoc_resp_buf)
+ ->frame_control) ||
+ ieee80211_is_reassoc_resp(
+ ((struct ieee80211_mgmt *)
+ assoc_rsp->assoc_resp_buf)
+ ->frame_control)) {
+ spin_lock_irqsave(&priv->connect_lock,
+ flags);
+ if (le16_to_cpu(
+ ((struct ieee80211_mgmt
+ *)assoc_rsp
+ ->assoc_resp_buf)
+ ->u.assoc_resp
+ .status_code) !=
+ WLAN_STATUS_SUCCESS) {
+ memset(priv->cfg_bssid, 0,
+ ETH_ALEN);
+ if (priv->bss_type ==
+ MLAN_BSS_TYPE_STA)
+ woal_clear_conn_params(
+ priv);
+ }
+ spin_unlock_irqrestore(
+ &priv->connect_lock, flags);
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
+ cfg80211_rx_assoc_resp(
+ priv->netdev, bss,
+ assoc_rsp->assoc_resp_buf,
+ assoc_rsp->assoc_resp_len, -1,
+ NULL, 0);
+#else
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ cfg80211_rx_assoc_resp(
+ priv->netdev, bss,
+ assoc_rsp->assoc_resp_buf,
+ assoc_rsp->assoc_resp_len, -1);
+#else
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ cfg80211_rx_assoc_resp(
+ priv->netdev, bss,
+ assoc_rsp->assoc_resp_buf,
+ assoc_rsp->assoc_resp_len);
+#else
+ cfg80211_send_rx_assoc(
+ priv->netdev, bss,
+ assoc_rsp->assoc_resp_buf,
+ assoc_rsp->assoc_resp_len);
+#endif
+#endif
+#endif
+ }
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(priv->wdev->wiphy, bss);
+#else
+ cfg80211_put_bss(bss);
+#endif
+ }
+ }
+}
+
+/**
+ * @brief Handle assoc response event
+ *
+ * @param priv A pointer moal_private structure
+ * @param pchan_info A pointer to mlan_ds_misc_assoc_rsp structure
+ *
+ * @return N/A
+ */
+
+void woal_assoc_resp_event(moal_private *priv,
+ mlan_ds_misc_assoc_rsp *passoc_rsp)
+{
+ struct woal_event *evt;
+ unsigned long flags;
+ moal_handle *handle = priv->phandle;
+
+ evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC);
+ if (evt) {
+ evt->priv = priv;
+ evt->type = WOAL_EVENT_ASSOC_RESP;
+ moal_memcpy_ext(priv->phandle, &evt->assoc_resp, passoc_rsp,
+ sizeof(mlan_ds_misc_assoc_rsp),
+ sizeof(mlan_ds_misc_assoc_rsp));
+ INIT_LIST_HEAD(&evt->link);
+ spin_lock_irqsave(&handle->evt_lock, flags);
+ list_add_tail(&evt->link, &handle->evt_queue);
+ spin_unlock_irqrestore(&handle->evt_lock, flags);
+ queue_work(handle->evt_workqueue, &handle->evt_work);
+ }
+}
+
+/**
+ * @brief This function is association handler when host MLME
+ * enable.
+ * In this case driver will prepare and send Assoc Req.
+ *
+ * @param wiphy A pointer to wiphy.
+ *
+ * @param dev A pointer to net_device
+ *
+ * @param req A pointer to cfg80211_assoc_request
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_assoc_request *req)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ int ret = 0;
+ mlan_ssid_bssid ssid_bssid;
+ unsigned long flags;
+ const u8 *ssid_ie;
+ int wpa_enabled = 0, group_enc_mode = 0, pairwise_enc_mode = 0;
+ mlan_bss_info bss_info;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ if (priv->auth_alg == WLAN_AUTH_SAE) {
+ priv->auth_flag = HOST_MLME_AUTH_DONE;
+
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
+ PRINTM(MINFO, "wlan: HostMlme %s auth exchange successful\n",
+ priv->netdev->name);
+
+ if (priv->phandle->remain_on_channel) {
+ if (woal_cfg80211_remain_on_channel_cfg(
+ priv, MOAL_IOCTL_WAIT, MTRUE,
+ (t_u8 *)&status, NULL, 0, 0)) {
+ PRINTM(MERROR,
+ "Failed to cancel remain on channel\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ priv->phandle->remain_on_channel = MFALSE;
+ }
+ }
+
+ if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE)) {
+ LEAVE();
+ return -EBUSY;
+ }
+
+ priv->cfg_connect = MTRUE;
+ priv->assoc_status = 0;
+ priv->auth_alg = 0xFFFF;
+
+ memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid));
+ ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+ moal_memcpy_ext(priv->phandle, ssid_bssid.bssid, req->bss->bssid,
+ ETH_ALEN, sizeof(ssid_bssid.bssid));
+
+ if (!ssid_ie) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, ssid_ie + 2,
+ ssid_ie[1], sizeof(ssid_bssid.ssid.ssid));
+ ssid_bssid.ssid.ssid_len = ssid_ie[1];
+
+ if (ssid_bssid.ssid.ssid_len > MW_ESSID_MAX_SIZE) {
+ PRINTM(MERROR, "Invalid SSID - aborting\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!ssid_bssid.ssid.ssid_len || ssid_bssid.ssid.ssid[0] < 0x20) {
+ PRINTM(MERROR, "Invalid SSID - aborting\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+#ifdef STA_WEXT
+ if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) {
+ switch (req->crypto.wpa_versions) {
+ case NL80211_WPA_VERSION_2:
+ priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2;
+ break;
+ case NL80211_WPA_VERSION_1:
+ priv->wpa_version = IW_AUTH_WPA_VERSION_WPA;
+ break;
+ default:
+ priv->wpa_version = 0;
+ break;
+ }
+ if (req->crypto.n_akm_suites) {
+ switch (req->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_PSK:
+ priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK;
+ break;
+ case WLAN_AKM_SUITE_8021X:
+ priv->key_mgmt = IW_AUTH_KEY_MGMT_802_1X;
+ break;
+ default:
+ priv->key_mgmt = 0;
+ break;
+ }
+ }
+ }
+#endif
+
+ if (req->ie && req->ie_len) { /* Set the IE */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_assoc_ies_cfg(priv, (t_u8 *)req->ie,
+ req->ie_len, MOAL_IOCTL_WAIT)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (req->crypto.n_ciphers_pairwise) {
+ pairwise_enc_mode = woal_cfg80211_get_encryption_mode(
+ req->crypto.ciphers_pairwise[0], &wpa_enabled);
+ ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode,
+ wpa_enabled, MOAL_IOCTL_WAIT);
+ if (ret)
+ goto done;
+ }
+
+ if (req->crypto.cipher_group) {
+ group_enc_mode = woal_cfg80211_get_encryption_mode(
+ req->crypto.cipher_group, &wpa_enabled);
+ ret = woal_cfg80211_set_auth(priv, group_enc_mode, wpa_enabled,
+ MOAL_IOCTL_WAIT);
+ if (ret)
+ goto done;
+ }
+ ssid_bssid.host_mlme = priv->host_mlme;
+
+ if (req->bss->channel) {
+ ssid_bssid.channel_flags = req->bss->channel->flags;
+ ssid_bssid.channel_flags |= CHAN_FLAGS_MAX;
+ PRINTM(MCMND, "channel flags=0x%x\n", req->bss->channel->flags);
+ }
+
+ PRINTM(MCMND, "wlan: HostMlme %s send assoicate to bssid " MACSTR "\n",
+ priv->netdev->name, MAC2STR(req->bss->bssid));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) {
+ PRINTM(MERROR, "HostMlme %s: bss_start Fails\n",
+ priv->netdev->name);
+ priv->host_mlme = MFALSE;
+ priv->auth_flag = 0;
+ ret = -EFAULT;
+ }
+
+done:
+
+ if (!ret) {
+ priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
+ if (priv->bss_type == MLAN_BSS_TYPE_STA
+#ifdef WIFI_DIRECT_SUPPORT
+ || priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT
+#endif
+ )
+ woal_save_assoc_params(priv, req, &ssid_bssid);
+ memset(&bss_info, 0, sizeof(bss_info));
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ priv->channel = bss_info.bss_chan;
+ }
+
+ spin_lock_irqsave(&priv->connect_lock, flags);
+ priv->cfg_connect = MFALSE;
+ if (!ret && priv->media_connected) {
+ PRINTM(MMSG,
+ "wlan: HostMlme %s Connected to bssid " MACSTR
+ " successfully\n",
+ priv->netdev->name, MAC2STR(priv->cfg_bssid));
+ spin_unlock_irqrestore(&priv->connect_lock, flags);
+ } else {
+ PRINTM(MERROR,
+ "wlan: HostMlme %s Failed to connect to bssid " MACSTR
+ "\n",
+ priv->netdev->name, MAC2STR(req->bss->bssid));
+ if (ssid_bssid.assoc_rsp.assoc_resp_len &&
+ ssid_bssid.assoc_rsp.assoc_resp_len >
+ sizeof(IEEEtypes_MgmtHdr_t)) {
+ // save the connection param when send assoc_resp to
+ // kernel
+ woal_save_assoc_params(priv, req, &ssid_bssid);
+ ret = 0;
+ } else {
+ ssid_bssid.assoc_rsp.assoc_resp_len = 0;
+ ret = -EFAULT;
+ memset(priv->cfg_bssid, 0, ETH_ALEN);
+ if (priv->bss_type == MLAN_BSS_TYPE_STA)
+ woal_clear_conn_params(priv);
+ }
+ priv->host_mlme = MFALSE;
+ priv->auth_flag = 0;
+ spin_unlock_irqrestore(&priv->connect_lock, flags);
+ }
+ /*Association Response should also be send when ret is non-zero.
+ We also need to return success when we have association response
+ available*/
+ if (ssid_bssid.assoc_rsp.assoc_resp_len) {
+ priv->auth_flag |= HOST_MLME_ASSOC_DONE;
+ woal_assoc_resp_event(priv, &ssid_bssid.assoc_rsp);
+ }
+
+ LEAVE();
+ return ret;
+}
+#endif
+
+/**
+ * @brief Request the driver for (re)association
+ *
+ * @param priv A pointer to moal_private structure
+ * @param sme A pointer to connect parameters
+ * @param wait_option wait option
+ * @param assoc_resp A pointer to assoc_rsp structure;
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_assoc(moal_private *priv, void *sme, t_u8 wait_option,
+ mlan_ds_misc_assoc_rsp *assoc_rsp)
+{
+ struct cfg80211_ibss_params *ibss_param = NULL;
+ struct cfg80211_connect_params *conn_param = NULL;
+ mlan_802_11_ssid req_ssid;
+ mlan_ssid_bssid ssid_bssid;
+ mlan_ioctl_req *req = NULL;
+ int ret = 0;
+ t_u32 auth_type = 0, mode;
+ int wpa_enabled = 0;
+ int group_enc_mode = 0, pairwise_enc_mode = 0;
+ int alg_is_wep = 0;
+
+ t_u8 *ssid, ssid_len = 0, *bssid;
+ t_u8 *ie = NULL;
+ int ie_len = 0;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct cfg80211_chan_def *chan_def = NULL;
+#endif
+ struct ieee80211_channel *channel = NULL;
+ t_u16 beacon_interval = 0;
+ bool privacy;
+ struct cfg80211_bss *bss = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ if (!sme) {
+ LEAVE();
+ return -EFAULT;
+ }
+
+ mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype);
+
+ if (mode == MLAN_BSS_MODE_IBSS) {
+ ibss_param = (struct cfg80211_ibss_params *)sme;
+ ssid = (t_u8 *)ibss_param->ssid;
+ ssid_len = ibss_param->ssid_len;
+ bssid = (t_u8 *)ibss_param->bssid;
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ channel = ibss_param->channel;
+#else
+ chan_def = &ibss_param->chandef;
+ channel = ibss_param->chandef.chan;
+#endif
+ if (channel)
+ priv->phandle->band = channel->band;
+ if (ibss_param->ie_len)
+ ie = (t_u8 *)ibss_param->ie;
+ ie_len = ibss_param->ie_len;
+ beacon_interval = ibss_param->beacon_interval;
+ privacy = ibss_param->privacy;
+
+ } else {
+ conn_param = (struct cfg80211_connect_params *)sme;
+ ssid = (t_u8 *)conn_param->ssid;
+ ssid_len = conn_param->ssid_len;
+ bssid = (t_u8 *)conn_param->bssid;
+ channel = conn_param->channel;
+ if (channel)
+ priv->phandle->band = channel->band;
+ if (conn_param->ie_len)
+ ie = (t_u8 *)conn_param->ie;
+ ie_len = conn_param->ie_len;
+ privacy = conn_param->privacy;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
+ bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid,
+ ssid_len, IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY);
+#else
+ bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid,
+ ssid_len, WLAN_CAPABILITY_ESS,
+ WLAN_CAPABILITY_ESS);
+#endif
+ if (bss) {
+ if ((!priv->phandle->params.reg_alpha2 ||
+ strncmp(priv->phandle->params.reg_alpha2, "99",
+ strlen("99")))
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ && (!moal_extflg_isset(priv->phandle,
+ EXT_COUNTRY_IE_IGNORE))
+#endif
+ )
+ woal_process_country_ie(priv, bss);
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(priv->wdev->wiphy, bss);
+#else
+ cfg80211_put_bss(bss);
+#endif
+ } else
+ woal_send_domain_info_cmd_fw(priv, wait_option);
+#ifdef STA_WEXT
+ if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) {
+ switch (conn_param->crypto.wpa_versions) {
+ case NL80211_WPA_VERSION_2:
+ priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2;
+ break;
+ case NL80211_WPA_VERSION_1:
+ priv->wpa_version = IW_AUTH_WPA_VERSION_WPA;
+ break;
+ default:
+ priv->wpa_version = 0;
+ break;
+ }
+ if (conn_param->crypto.n_akm_suites) {
+ switch (conn_param->crypto.akm_suites[0]) {
+ case WLAN_AKM_SUITE_PSK:
+ priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK;
+ break;
+ case WLAN_AKM_SUITE_8021X:
+ priv->key_mgmt =
+ IW_AUTH_KEY_MGMT_802_1X;
+ break;
+ default:
+ priv->key_mgmt = 0;
+ break;
+ }
+ }
+ }
+#endif
+ }
+
+ memset(&req_ssid, 0, sizeof(mlan_802_11_ssid));
+ memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid));
+
+ req_ssid.ssid_len = ssid_len;
+ if (ssid_len > MW_ESSID_MAX_SIZE) {
+ PRINTM(MERROR, "Invalid SSID - aborting\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ moal_memcpy_ext(priv->phandle, req_ssid.ssid, ssid, ssid_len,
+ sizeof(req_ssid.ssid));
+ if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
+ PRINTM(MERROR, "Invalid SSID - aborting\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (priv->phandle->card_info->embedded_supp)
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_ewpa_mode(priv, wait_option, &ssid_bssid)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0,
+ KEY_INDEX_CLEAR_ALL, NULL, 1, wait_option)) {
+ /* Disable keys and clear all previous security settings */
+ ret = -EFAULT;
+ goto done;
+ }
+#ifdef STA_CFG80211
+ if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) {
+ /** Check if current roaming support OKC offload roaming */
+ if (conn_param && conn_param->crypto.n_akm_suites &&
+ conn_param->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X) {
+ if (priv->okc_roaming_ie && priv->okc_ie_len) {
+ ie = priv->okc_roaming_ie;
+ ie_len = priv->okc_ie_len;
+ }
+ }
+ }
+#endif
+
+ if ((priv->ft_pre_connect ||
+ (conn_param && conn_param->auth_type == NL80211_AUTHTYPE_FT)) &&
+ priv->ft_ie_len) {
+ ie = priv->ft_ie;
+ ie_len = priv->ft_ie_len;
+ priv->ft_ie_len = 0;
+ }
+ if (ie && ie_len) { /* Set the IE */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_assoc_ies_cfg(priv, ie, ie_len,
+ wait_option)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (conn_param && mode != MLAN_BSS_MODE_IBSS) {
+ /* These parameters are only for managed mode */
+ if (conn_param->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+ auth_type = MLAN_AUTH_MODE_OPEN;
+ else if (conn_param->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+ auth_type = MLAN_AUTH_MODE_SHARED;
+ else if (conn_param->auth_type == NL80211_AUTHTYPE_NETWORK_EAP)
+ auth_type = MLAN_AUTH_MODE_NETWORKEAP;
+ else if (conn_param->auth_type == NL80211_AUTHTYPE_FT)
+ auth_type = MLAN_AUTH_MODE_FT;
+ else
+ auth_type = MLAN_AUTH_MODE_AUTO;
+ if (priv->ft_pre_connect)
+ auth_type = MLAN_AUTH_MODE_FT;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_auth_mode(priv, wait_option, auth_type)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (conn_param->crypto.n_ciphers_pairwise) {
+ pairwise_enc_mode = woal_cfg80211_get_encryption_mode(
+ conn_param->crypto.ciphers_pairwise[0],
+ &wpa_enabled);
+ ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode,
+ wpa_enabled, wait_option);
+ if (ret)
+ goto done;
+ }
+
+ if (conn_param->crypto.cipher_group) {
+ group_enc_mode = woal_cfg80211_get_encryption_mode(
+ conn_param->crypto.cipher_group, &wpa_enabled);
+ ret = woal_cfg80211_set_auth(priv, group_enc_mode,
+ wpa_enabled, wait_option);
+ if (ret)
+ goto done;
+ }
+
+ if (conn_param->key) {
+ alg_is_wep =
+ woal_cfg80211_is_alg_wep(pairwise_enc_mode) |
+ woal_cfg80211_is_alg_wep(group_enc_mode);
+ if (alg_is_wep) {
+ PRINTM(MINFO,
+ "Setting wep encryption with key len %d\n",
+ conn_param->key_len);
+ /* Set the WEP key */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_wep_keys(
+ priv, conn_param->key,
+ conn_param->key_len,
+ conn_param->key_idx, wait_option)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ /* Enable the WEP key by key index */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_wep_keys(
+ priv, NULL, 0, conn_param->key_idx,
+ wait_option)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ }
+
+ if (mode == MLAN_BSS_MODE_IBSS) {
+ mlan_ds_bss *bss = NULL;
+ /* Change beacon interval */
+ if ((beacon_interval < MLAN_MIN_BEACON_INTERVAL) ||
+ (beacon_interval > MLAN_MAX_BEACON_INTERVAL)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ kfree(req);
+ req = NULL;
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ bss = (mlan_ds_bss *)req->pbuf;
+ req->req_id = MLAN_IOCTL_BSS;
+ req->action = MLAN_ACT_SET;
+ bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL;
+ bss->param.bcn_interval = beacon_interval;
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* "privacy" is set only for ad-hoc mode */
+ if (privacy) {
+ /*
+ * Keep MLAN_ENCRYPTION_MODE_WEP40 for now so that
+ * the firmware can find a matching network from the
+ * scan. cfg80211 does not give us the encryption
+ * mode at this stage so just setting it to wep here
+ */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_auth_mode(priv, wait_option,
+ MLAN_AUTH_MODE_OPEN)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ wpa_enabled = 0;
+ ret = woal_cfg80211_set_auth(
+ priv, MLAN_ENCRYPTION_MODE_WEP104, wpa_enabled,
+ wait_option);
+ if (ret)
+ goto done;
+ }
+ }
+ moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, &req_ssid,
+ sizeof(mlan_802_11_ssid), sizeof(ssid_bssid.ssid));
+ if (bssid)
+ moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, bssid,
+ ETH_ALEN, sizeof(ssid_bssid.bssid));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_find_essid(priv, &ssid_bssid, wait_option)) {
+ /* Do specific SSID scanning */
+ if (mode != MLAN_BSS_MODE_IBSS)
+ ret = woal_cfg80211_connect_scan(priv, conn_param,
+ wait_option);
+ else
+ ret = woal_request_scan(priv, wait_option, &req_ssid);
+ if (ret) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ /* Disconnect before try to associate */
+ if (mode == MLAN_BSS_MODE_IBSS)
+ woal_disconnect(priv, wait_option, NULL,
+ DEF_DEAUTH_REASON_CODE);
+
+ if (mode != MLAN_BSS_MODE_IBSS) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_find_best_network(priv, wait_option, &ssid_bssid)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ /* Inform the BSS information to kernel, otherwise
+ * kernel will give a panic after successful assoc */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_inform_bss_from_scan_result(priv, &ssid_bssid,
+ wait_option)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ } else if (MLAN_STATUS_SUCCESS !=
+ woal_find_best_network(priv, wait_option, &ssid_bssid))
+ /* Adhoc start, Check the channel command */
+ woal_11h_channel_check_ioctl(priv, wait_option);
+
+ PRINTM(MINFO, "Trying to associate to %s and bssid " MACSTR "\n",
+ (char *)req_ssid.ssid, MAC2STR(ssid_bssid.bssid));
+
+ /* Zero SSID implies use BSSID to connect */
+ if (bssid)
+ memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid));
+ else /* Connect to BSS by ESSID */
+ memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH);
+ if (channel) {
+ ssid_bssid.channel_flags = channel->flags;
+ ssid_bssid.channel_flags |= CHAN_FLAGS_MAX;
+ PRINTM(MCMND, "channel flags=0x%x\n", channel->flags);
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ /* Inform the IBSS information to kernel, otherwise
+ * kernel will give a panic after successful assoc */
+ if (mode == MLAN_BSS_MODE_IBSS) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_inform_ibss_bss(priv, channel,
+ beacon_interval)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ } else if (assoc_rsp) {
+ moal_memcpy_ext(priv->phandle, assoc_rsp, &ssid_bssid.assoc_rsp,
+ sizeof(mlan_ds_misc_assoc_rsp),
+ sizeof(mlan_ds_misc_assoc_rsp));
+ PRINTM(MCMND, "assoc_rsp ie len=%d\n",
+ assoc_rsp->assoc_resp_len);
+ }
+done:
+ if (ret) {
+ /* clear the encryption mode */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE,
+ MFALSE, wait_option)) {
+ PRINTM(MERROR, "Could not clear encryption \n");
+ ret = -EFAULT;
+ }
+ /* clear IE */
+ ie_len = 0;
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_gen_ie(priv, MLAN_ACT_SET, NULL, &ie_len,
+ wait_option)) {
+ PRINTM(MERROR, "Could not clear RSN IE\n");
+ ret = -EFAULT;
+ }
+ }
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Request the driver to fill the tx/rx rate info
+ *
+ * @param priv A pointer to moal_private structure
+ * @param sinfo A pointer to station_info structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+void woal_cfg80211_fill_rate_info(moal_private *priv,
+ struct station_info *sinfo)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_rate *rate = NULL;
+ t_u16 Rates[12] = {0x02, 0x04, 0x0B, 0x16, 0x0C, 0x12,
+ 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c};
+ ENTER();
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
+ if (req == NULL) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ rate = (mlan_ds_rate *)req->pbuf;
+ rate->sub_command = MLAN_OID_GET_DATA_RATE;
+ req->req_id = MLAN_IOCTL_RATE;
+ req->action = MLAN_ACT_GET;
+ ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (ret != MLAN_STATUS_SUCCESS)
+ goto done;
+ if (rate->param.data_rate.tx_rate_format != MLAN_RATE_FORMAT_LG) {
+ if (rate->param.data_rate.tx_rate_format ==
+ MLAN_RATE_FORMAT_HT) {
+ sinfo->txrate.flags = RATE_INFO_FLAGS_MCS;
+ if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->txrate.bw = RATE_INFO_BW_40;
+#else
+ sinfo->txrate.flags |=
+ RATE_INFO_FLAGS_40_MHZ_WIDTH;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ else
+ sinfo->txrate.bw = RATE_INFO_BW_20;
+#endif
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ else if (rate->param.data_rate.tx_rate_format ==
+ MLAN_RATE_FORMAT_VHT) {
+ sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ sinfo->txrate.nss = rate->param.data_rate.tx_nss + 1;
+ if (rate->param.data_rate.tx_ht_bw == MLAN_VHT_BW80)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->txrate.bw = RATE_INFO_BW_80;
+#else
+ sinfo->txrate.flags |=
+ RATE_INFO_FLAGS_80_MHZ_WIDTH;
+#endif
+ else if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->txrate.bw = RATE_INFO_BW_40;
+#else
+ sinfo->txrate.flags |=
+ RATE_INFO_FLAGS_40_MHZ_WIDTH;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ else
+ sinfo->txrate.bw = RATE_INFO_BW_20;
+#endif
+ }
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 18)
+ else if (rate->param.data_rate.tx_rate_format ==
+ MLAN_RATE_FORMAT_HE) {
+ sinfo->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ sinfo->txrate.nss = rate->param.data_rate.tx_nss + 1;
+ sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index;
+ sinfo->txrate.he_gi = rate->param.data_rate.tx_ht_gi;
+ if (rate->param.data_rate.tx_ht_bw == MLAN_VHT_BW80)
+ sinfo->txrate.bw = RATE_INFO_BW_80;
+ else if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40)
+ sinfo->txrate.bw = RATE_INFO_BW_40;
+ else
+ sinfo->txrate.bw = RATE_INFO_BW_20;
+ }
+#endif
+ if (rate->param.data_rate.tx_ht_gi == MLAN_HT_SGI)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index;
+ } else {
+ /* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */
+ sinfo->txrate.legacy =
+ Rates[rate->param.data_rate.tx_data_rate] * 5;
+ }
+ // Fill Rx rate
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ if (rate->param.data_rate.rx_rate_format != MLAN_RATE_FORMAT_LG) {
+ if (rate->param.data_rate.rx_rate_format ==
+ MLAN_RATE_FORMAT_HT) {
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS;
+ if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40)
+ sinfo->rxrate.bw = RATE_INFO_BW_40;
+ else
+ sinfo->rxrate.bw = RATE_INFO_BW_20;
+ } else if (rate->param.data_rate.rx_rate_format ==
+ MLAN_RATE_FORMAT_VHT) {
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ sinfo->rxrate.nss = rate->param.data_rate.rx_nss + 1;
+ if (rate->param.data_rate.rx_ht_bw == MLAN_VHT_BW80)
+ sinfo->rxrate.bw = RATE_INFO_BW_80;
+ else if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40)
+ sinfo->rxrate.bw = RATE_INFO_BW_40;
+ else
+ sinfo->rxrate.bw = RATE_INFO_BW_20;
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 18)
+ else if (rate->param.data_rate.rx_rate_format ==
+ MLAN_RATE_FORMAT_HE) {
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ sinfo->rxrate.nss = rate->param.data_rate.rx_nss + 1;
+ sinfo->rxrate.mcs = rate->param.data_rate.rx_mcs_index;
+ sinfo->rxrate.he_gi = rate->param.data_rate.rx_ht_gi;
+ if (rate->param.data_rate.rx_ht_bw == MLAN_VHT_BW80)
+ sinfo->rxrate.bw = RATE_INFO_BW_80;
+ else if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40)
+ sinfo->rxrate.bw = RATE_INFO_BW_40;
+ else
+ sinfo->rxrate.bw = RATE_INFO_BW_20;
+ }
+#endif
+ if (rate->param.data_rate.rx_ht_gi == MLAN_HT_SGI)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->rxrate.mcs = rate->param.data_rate.rx_mcs_index;
+ } else {
+ /* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */
+ sinfo->rxrate.legacy =
+ Rates[rate->param.data_rate.rx_data_rate] * 5;
+ }
+#endif
+done:
+ if (ret != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return;
+}
+/**
+ * @brief Request the driver to dump the station information
+ *
+ * @param priv A pointer to moal_private structure
+ * @param sinfo A pointer to station_info structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static mlan_status woal_cfg80211_dump_station_info(moal_private *priv,
+ struct station_info *sinfo)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_ds_get_signal signal;
+ mlan_ds_get_stats stats;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+ mlan_bss_info bss_info;
+ t_u8 dtim_period = 0;
+#endif
+
+ ENTER();
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->filled = MBIT(NL80211_STA_INFO_RX_BYTES) |
+ MBIT(NL80211_STA_INFO_TX_BYTES) |
+ MBIT(NL80211_STA_INFO_RX_PACKETS) |
+ MBIT(NL80211_STA_INFO_TX_PACKETS) |
+ MBIT(NL80211_STA_INFO_SIGNAL) |
+ MBIT(NL80211_STA_INFO_TX_BITRATE) |
+ MBIT(NL80211_STA_INFO_RX_BITRATE);
+#else
+ sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
+ STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS |
+ STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE;
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->filled |= MBIT(NL80211_STA_INFO_TX_FAILED);
+#else
+ sinfo->filled |= STATION_INFO_TX_FAILED;
+#endif
+#endif
+
+ /* Get signal information from the firmware */
+ memset(&signal, 0, sizeof(mlan_ds_get_signal));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
+ PRINTM(MERROR, "Error getting signal information\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ /* Get stats information from the firmware */
+ memset(&stats, 0, sizeof(mlan_ds_get_stats));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) {
+ PRINTM(MERROR, "Error getting stats information\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ sinfo->rx_bytes = priv->stats.rx_bytes;
+ sinfo->tx_bytes = priv->stats.tx_bytes;
+ sinfo->rx_packets = priv->stats.rx_packets;
+ sinfo->tx_packets = priv->stats.tx_packets;
+ sinfo->signal = signal.bcn_rssi_avg;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ sinfo->tx_failed = stats.failed;
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+ /* Update BSS information */
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ sinfo->filled |= MBIT(NL80211_STA_INFO_BSS_PARAM);
+#else
+ sinfo->filled |= STATION_INFO_BSS_PARAM;
+#endif
+ sinfo->bss_param.flags = 0;
+ ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ if (ret)
+ goto done;
+ if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+ if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_SLOT_TIME)
+ sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+ sinfo->bss_param.beacon_interval = bss_info.beacon_interval;
+ /* Get DTIM period */
+ ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
+ &dtim_period);
+ if (ret) {
+ PRINTM(MERROR, "Get DTIM period failed\n");
+ goto done;
+ }
+ sinfo->bss_param.dtim_period = dtim_period;
+#endif
+ woal_cfg80211_fill_rate_info(priv, sinfo);
+done:
+ LEAVE();
+ return ret;
+}
+
+/********************************************************
+ Global Functions
+********************************************************/
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+/**
+ * @brief Set all radar channel's dfs_state
+ *
+ * @param wiphy A pointer to wiphy structure
+ *
+ * @return N/A
+ */
+void woal_update_radar_chans_dfs_state(struct wiphy *wiphy)
+{
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+ enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
+ int i;
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ if (sband->channels[i].flags & IEEE80211_CHAN_RADAR) {
+ if (moal_extflg_isset(handle, EXT_DFS_OFFLOAD))
+ sband->channels[i].dfs_state =
+ NL80211_DFS_AVAILABLE;
+ else
+ sband->channels[i].dfs_state =
+ NL80211_DFS_USABLE;
+ }
+ }
+ }
+ PRINTM(MCMND, "Set radar dfs_state: dfs_offload=%d\n",
+ moal_extflg_isset(handle, EXT_DFS_OFFLOAD));
+}
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+
+/**
+ * @brief This function will be used by sort, to compare LHS & RHS
+ *
+ * @param lhs LHS value
+ * @param rhs RHS value
+ * @return 0
+ */
+static int compare(const void *lhs, const void *rhs)
+{
+ const chan_freq_power_t *lhs_cfp = (const chan_freq_power_t *)(lhs);
+ const chan_freq_power_t *rhs_cfp = (const chan_freq_power_t *)(rhs);
+
+ if (lhs_cfp->channel < rhs_cfp->channel)
+ return -1;
+ if (lhs_cfp->channel > rhs_cfp->channel)
+ return 1;
+
+ return 0;
+}
+/**
+ * @brief This function update channel region config
+ *
+ * @param buf Buffer containing channel region config
+ * @param num_chan Length of buffer
+ * @param regd ieee80211_regdomain to be updated
+ *
+ * @return N/A
+ */
+static struct ieee80211_regdomain *
+create_custom_regdomain(mlan_ds_custom_reg_domain *custom_reg)
+{
+ struct ieee80211_reg_rule *rule;
+ bool new_rule;
+ int idx, freq, prev_freq = 0;
+ t_u8 chan;
+ t_u16 num_chan = 0;
+ t_u32 bw, prev_bw = 0;
+ t_u16 chflags, prev_chflags = 0, valid_rules = 0;
+ struct ieee80211_regdomain *regd = NULL;
+ int regd_size;
+ const struct ieee80211_freq_range *freq_range = NULL;
+
+ num_chan = custom_reg->num_bg_chan;
+ num_chan += custom_reg->num_a_chan;
+
+ sort(&custom_reg->cfp_tbl[custom_reg->num_bg_chan],
+ custom_reg->num_a_chan, sizeof(chan_freq_power_t), &compare, NULL);
+
+ regd_size = sizeof(struct ieee80211_regdomain) +
+ num_chan * sizeof(struct ieee80211_reg_rule);
+
+ regd = kzalloc(regd_size, GFP_KERNEL);
+ if (!regd) {
+ return NULL;
+ }
+#define NXP_CHANNEL_TMP_NOHT40 MBIT(15)
+ /* preprocess 2.4G 40MHz support */
+ for (idx = 0; idx < num_chan; idx++) {
+ chan = custom_reg->cfp_tbl[idx].channel;
+ if (!chan) {
+ if (regd)
+ kfree(regd);
+ return NULL;
+ }
+
+ if (chan > 14)
+ continue;
+
+ chflags = custom_reg->cfp_tbl[idx].dynamic.flags;
+
+ if (chflags & NXP_CHANNEL_DISABLED)
+ continue;
+
+ /* duplicate a temp flag */
+ if (chflags & NXP_CHANNEL_NOHT40)
+ chflags |= NXP_CHANNEL_TMP_NOHT40;
+ }
+ for (idx = 0; idx < num_chan; idx++) {
+ chan = custom_reg->cfp_tbl[idx].channel;
+ if (!chan) {
+ if (regd)
+ kfree(regd);
+ return NULL;
+ }
+
+ if (chan > 14)
+ continue;
+
+ chflags = custom_reg->cfp_tbl[idx].dynamic.flags;
+
+ if (chflags & NXP_CHANNEL_DISABLED)
+ continue;
+
+ /* 40 MHz band of one center channel will spread to upper and
+ * lower 2 channels */
+ /* Mark HT40 flag for all channels of this 40 MHz band */
+ if (!(chflags & NXP_CHANNEL_TMP_NOHT40)) {
+ if (idx >= 2)
+ custom_reg->cfp_tbl[idx - 2].dynamic.flags &=
+ ~NXP_CHANNEL_NOHT40;
+ if (idx >= 1)
+ custom_reg->cfp_tbl[idx - 1].dynamic.flags &=
+ ~NXP_CHANNEL_NOHT40;
+ if (idx < (num_chan - 1))
+ custom_reg->cfp_tbl[idx + 1].dynamic.flags &=
+ ~NXP_CHANNEL_NOHT40;
+ if (idx < (num_chan - 2))
+ custom_reg->cfp_tbl[idx + 2].dynamic.flags &=
+ ~NXP_CHANNEL_NOHT40;
+ } else {
+ custom_reg->cfp_tbl[idx].dynamic.flags &=
+ ~NXP_CHANNEL_TMP_NOHT40;
+ }
+ }
+ for (idx = 0; idx < num_chan; idx++) {
+ enum ieee80211_band band;
+
+ chan = custom_reg->cfp_tbl[idx].channel;
+ if (!chan) {
+ if (regd)
+ kfree(regd);
+ return NULL;
+ }
+ chflags = custom_reg->cfp_tbl[idx].dynamic.flags;
+ band = (chan <= 14) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ freq = ieee80211_channel_to_frequency(chan, band);
+ PRINTM(MINFO, "chan=%d freq=%d chan_flag=0x%x\n", chan, freq,
+ chflags);
+ new_rule = false;
+
+ if (chflags & NXP_CHANNEL_DISABLED)
+ continue;
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ if (!(chflags & NXP_CHANNEL_NOHT80))
+ bw = MHZ_TO_KHZ(80);
+ else if (!(chflags & NXP_CHANNEL_NOHT40))
+ bw = MHZ_TO_KHZ(40);
+ else
+ bw = MHZ_TO_KHZ(20);
+ } else {
+ if (!(chflags & NXP_CHANNEL_NOHT40))
+ bw = MHZ_TO_KHZ(40);
+ else
+ bw = MHZ_TO_KHZ(20);
+ }
+
+ if (idx == 0 || prev_chflags != chflags || prev_bw != bw ||
+ freq - prev_freq > 20) {
+ valid_rules++;
+ new_rule = true;
+ }
+
+ rule = &regd->reg_rules[valid_rules - 1];
+
+ rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10);
+
+ prev_chflags = chflags;
+ prev_freq = freq;
+ prev_bw = bw;
+
+ if (!new_rule)
+ continue;
+
+ rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10);
+ rule->power_rule.max_eirp = DBM_TO_MBM(19);
+ rule->flags = 0;
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (chflags & NXP_CHANNEL_PASSIVE)
+ rule->flags |= NL80211_RRF_NO_IR;
+#endif
+ if (chflags & NXP_CHANNEL_DFS)
+ rule->flags |= NL80211_RRF_DFS;
+ if (chflags & NXP_CHANNEL_NO_OFDM)
+ rule->flags |= NL80211_RRF_NO_OFDM;
+ rule->freq_range.max_bandwidth_khz = bw;
+ }
+
+ regd->n_reg_rules = valid_rules;
+
+ if (custom_reg->region.country_code[0] == 'W' &&
+ custom_reg->region.country_code[1] == 'W') {
+ regd->alpha2[0] = '0';
+ regd->alpha2[1] = '0';
+ } else {
+ /* set alpha2 from FW. */
+ regd->alpha2[0] = custom_reg->region.country_code[0];
+ regd->alpha2[1] = custom_reg->region.country_code[1];
+ }
+ PRINTM(MCMND, "create_custom_regdomain: %c%c rules=%d\n",
+ regd->alpha2[0], regd->alpha2[1], valid_rules);
+ for (idx = 0; idx < regd->n_reg_rules; idx++) {
+ rule = &regd->reg_rules[idx];
+ freq_range = &rule->freq_range;
+ PRINTM(MCMND,
+ "flags=0x%x star_freq=%d end_freq=%d freq_diff=%d max_bandwidth=%d\n",
+ rule->flags, freq_range->start_freq_khz,
+ freq_range->end_freq_khz,
+ freq_range->end_freq_khz - freq_range->start_freq_khz,
+ freq_range->max_bandwidth_khz);
+ }
+ return regd;
+}
+
+/**
+ * @brief create custom channel regulatory config
+ *
+ * @param priv A pointer to moal_private structure
+ *
+ * @return 0-success, otherwise failure
+ */
+static int woal_update_custom_regdomain(moal_private *priv, struct wiphy *wiphy)
+{
+ mlan_ds_misc_cfg *misc = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ t_u8 country_code[COUNTRY_CODE_LEN];
+
+ int ret = 0;
+ struct ieee80211_regdomain *regd = NULL;
+
+ ENTER();
+
+ if (!priv || !wiphy) {
+ LEAVE();
+ return -EFAULT;
+ }
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
+ if (req == NULL) {
+ ret = -EFAULT;
+ goto done;
+ }
+ misc = (mlan_ds_misc_cfg *)req->pbuf;
+ misc->sub_command = MLAN_OID_MISC_GET_CHAN_REGION_CFG;
+ req->req_id = MLAN_IOCTL_MISC_CFG;
+ req->action = MLAN_ACT_GET;
+ memset(&misc->param.custom_reg_domain, 0,
+ sizeof(misc->param.custom_reg_domain));
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto done;
+ }
+ memset(country_code, 0, sizeof(country_code));
+ if (MTRUE ==
+ is_cfg80211_special_region_code(priv->phandle->country_code)) {
+ country_code[0] = 'W';
+ country_code[1] = 'W';
+ } else {
+ country_code[0] = priv->phandle->country_code[0];
+ country_code[1] = priv->phandle->country_code[1];
+ }
+ if (misc->param.custom_reg_domain.region.country_code[0] !=
+ country_code[0] ||
+ misc->param.custom_reg_domain.region.country_code[1] !=
+ country_code[1]) {
+ PRINTM(MERROR, "country code %c%c not match %c%c\n",
+ misc->param.custom_reg_domain.region.country_code[0],
+ misc->param.custom_reg_domain.region.country_code[1],
+ country_code[0], country_code[1]);
+ ret = -EFAULT;
+ goto done;
+ }
+ regd = create_custom_regdomain(&misc->param.custom_reg_domain);
+ if (regd) {
+ PRINTM(MMSG, "call regulatory_set_wiphy_regd %c%c",
+ misc->param.custom_reg_domain.region.country_code[0],
+ misc->param.custom_reg_domain.region.country_code[1]);
+ wiphy->regulatory_flags &=
+ ~(REGULATORY_STRICT_REG | REGULATORY_CUSTOM_REG);
+ wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+ regulatory_set_wiphy_regd(wiphy, regd);
+ kfree(regd);
+ }
+done:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+#endif
+
+/**
+ * @brief Request the driver to change regulatory domain
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param request A pointer to regulatory_request structure
+ *
+ * @return 0
+ */
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+static void
+#else
+static int
+#endif
+woal_cfg80211_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ moal_private *priv = NULL;
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+ t_u8 region[COUNTRY_CODE_LEN];
+ enum ieee80211_band band;
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ int ret = 0;
+#endif
+ mlan_fw_info fw_info;
+
+ ENTER();
+
+ priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
+ if (!priv) {
+ PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__);
+ LEAVE();
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ return -EINVAL;
+#else
+ return;
+#endif
+ }
+
+ PRINTM(MCMND,
+ "cfg80211 regulatory domain callback "
+ "%c%c initiator=%d\n",
+ request->alpha2[0], request->alpha2[1], request->initiator);
+ memset(&fw_info, 0, sizeof(mlan_fw_info));
+ woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
+ if (fw_info.force_reg) {
+ PRINTM(MINFO,
+ "Regulatory domain is enforced in the on-chip OTP\n");
+ LEAVE();
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ return -EINVAL;
+#else
+ return;
+#endif
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (moal_extflg_isset(handle, EXT_DFS_OFFLOAD))
+ woal_update_radar_chans_dfs_state(wiphy);
+#endif
+ memset(region, 0, sizeof(region));
+ moal_memcpy_ext(priv->phandle, region, request->alpha2,
+ sizeof(request->alpha2), sizeof(region));
+ region[2] = ' ';
+ if ((handle->country_code[0] != request->alpha2[0]) ||
+ (handle->country_code[1] != request->alpha2[1])) {
+ if (moal_extflg_isset(handle, EXT_CNTRY_TXPWR)) {
+ t_u8 country_code[COUNTRY_CODE_LEN];
+ handle->country_code[0] = request->alpha2[0];
+ handle->country_code[1] = request->alpha2[1];
+ handle->country_code[2] = ' ';
+ memset(country_code, 0, sizeof(country_code));
+ if (MTRUE == is_cfg80211_special_region_code(region)) {
+ country_code[0] = 'W';
+ country_code[1] = 'W';
+ } else {
+ country_code[0] = request->alpha2[0];
+ country_code[1] = request->alpha2[1];
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_request_country_power_table(priv,
+ country_code)) {
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ return -EFAULT;
+#else
+ return;
+#endif
+ }
+ }
+ }
+ if (MTRUE != is_cfg80211_special_region_code(region)) {
+ if (!moal_extflg_isset(handle, EXT_CNTRY_TXPWR)) {
+ handle->country_code[0] = request->alpha2[0];
+ handle->country_code[1] = request->alpha2[1];
+ handle->country_code[2] = ' ';
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_region_code(priv, handle->country_code))
+ PRINTM(MERROR, "Set country code failed!\n");
+ }
+ switch (request->initiator) {
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ PRINTM(MCMND, "Regulatory domain BY_DRIVER\n");
+ break;
+ case NL80211_REGDOM_SET_BY_CORE:
+ PRINTM(MCMND, "Regulatory domain BY_CORE\n");
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ PRINTM(MCMND, "Regulatory domain BY_USER\n");
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ if (fw_region && moal_extflg_isset(handle, EXT_CNTRY_TXPWR))
+ woal_update_custom_regdomain(priv, wiphy);
+#endif
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ PRINTM(MCMND, "Regulatory domain BY_COUNTRY_IE\n");
+ break;
+ }
+ if (priv->wdev && priv->wdev->wiphy &&
+ (request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) &&
+ (MTRUE != is_cfg80211_special_region_code(region))) {
+ band = priv->phandle->band;
+ priv->phandle->band = IEEE80211_BAND_2GHZ;
+ woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
+ priv->phandle->band = IEEE80211_BAND_5GHZ;
+ woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
+ priv->phandle->band = band;
+ }
+
+ LEAVE();
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ return ret;
+#endif
+}
+
+#ifdef UAP_CFG80211
+/**
+ * @brief Swithces BSS role of interface
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT)
+ * @param bss_role bss role
+ *
+ * @return 0 --success, otherwise fail
+ */
+mlan_status woal_role_switch(moal_private *priv, t_u8 wait_option,
+ t_u8 bss_role)
+{
+ int ret = 0;
+ mlan_ds_bss *bss = NULL;
+ mlan_ioctl_req *req = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ bss = (mlan_ds_bss *)req->pbuf;
+ bss->sub_command = MLAN_OID_BSS_ROLE;
+ req->req_id = MLAN_IOCTL_BSS;
+ req->action = MLAN_ACT_SET;
+ bss->param.bss_role = bss_role;
+
+ status = woal_request_ioctl(priv, req, wait_option);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto done;
+ }
+done:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief set/get bandcfg
+ *
+ * @param priv A pointer to moal_private structure
+ * @param action get or set action
+ * @param band_cfg A pointer to mlan_ds_band_cfg structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_setget_bandcfg(moal_private *priv, t_u8 action,
+ mlan_ds_band_cfg *band_cfg)
+{
+ int ret = 0;
+ mlan_ioctl_req *req = NULL;
+ mlan_ds_radio_cfg *radio_cfg = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
+ radio_cfg->sub_command = MLAN_OID_BAND_CFG;
+ req->req_id = MLAN_IOCTL_RADIO_CFG;
+ req->action = action;
+
+ if (req->action == MLAN_ACT_SET)
+ moal_memcpy_ext(priv->phandle, &radio_cfg->param.band_cfg,
+ band_cfg, sizeof(mlan_ds_band_cfg),
+ sizeof(radio_cfg->param.band_cfg));
+
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status != MLAN_STATUS_SUCCESS) {
+ ret = -EFAULT;
+ goto error;
+ }
+ moal_memcpy_ext(priv->phandle, band_cfg, &radio_cfg->param.band_cfg,
+ sizeof(mlan_ds_band_cfg), sizeof(mlan_ds_band_cfg));
+error:
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief request scan
+ *
+ * @param priv A pointer to moal_private structure
+ * @param scan_cfg A pointer to wlan_user_scan_cfg structure
+ *
+ * @return MLAN_STATUS_SUCCESS -- success, otherwise fail
+ */
+mlan_status woal_uap_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ moal_handle *handle = priv->phandle;
+ moal_private *tmp_priv;
+ u8 role;
+ mlan_ds_band_cfg org_bandcfg;
+ mlan_ds_band_cfg bandcfg;
+ u8 band_change = MFALSE;
+ ENTER();
+ if (priv->bss_index > 0)
+ tmp_priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
+ else
+ tmp_priv = priv;
+ if (!tmp_priv) {
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+ role = GET_BSS_ROLE(tmp_priv);
+ if (role == MLAN_BSS_ROLE_UAP)
+ woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_STA);
+ if (tmp_priv != priv) {
+ woal_setget_bandcfg(priv, MLAN_ACT_GET, &bandcfg);
+ woal_setget_bandcfg(tmp_priv, MLAN_ACT_GET, &org_bandcfg);
+ if (bandcfg.config_bands != org_bandcfg.config_bands) {
+ woal_setget_bandcfg(tmp_priv, MLAN_ACT_SET, &bandcfg);
+ band_change = MTRUE;
+ }
+ }
+#ifdef REASSOCIATION
+ if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) {
+ PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n");
+ goto done;
+ }
+#endif /* REASSOCIATION */
+ tmp_priv->report_scan_result = MTRUE;
+ ret = woal_request_userscan(tmp_priv, MOAL_IOCTL_WAIT, scan_cfg);
+ woal_sched_timeout(5);
+#ifdef REASSOCIATION
+ MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
+#endif
+done:
+ if (role == MLAN_BSS_ROLE_UAP)
+ woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_UAP);
+ if (band_change)
+ woal_setget_bandcfg(tmp_priv, MLAN_ACT_SET, &org_bandcfg);
+ LEAVE();
+ return ret;
+}
+#endif
+
+static int woal_find_wps_ie_in_probereq(const t_u8 *ie, int len)
+{
+ int left_len = len;
+ const t_u8 *pos = ie;
+ t_u8 ie_id, ie_len;
+ IEEEtypes_VendorSpecific_t *pvendor_ie = NULL;
+ const u8 wps_oui[4] = {0x00, 0x50, 0xf2, 0x04};
+
+ while (left_len >= 2) {
+ ie_id = *pos;
+ ie_len = *(pos + 1);
+ if ((ie_len + 2) > left_len)
+ break;
+ if (ie_id == VENDOR_SPECIFIC_221) {
+ pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos;
+ if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui,
+ sizeof(pvendor_ie->vend_hdr.oui)) &&
+ pvendor_ie->vend_hdr.oui_type == wps_oui[3])
+ return MTRUE;
+ }
+
+ pos += (ie_len + 2);
+ left_len -= (ie_len + 2);
+ }
+
+ return MFALSE;
+}
+
+/**
+ * @brief check if the scan result expired
+ *
+ * @param priv A pointer to moal_private
+ *
+ *
+ * @return MTRUE/MFALSE;
+ */
+t_u8 woal_is_scan_result_expired(moal_private *priv)
+{
+ mlan_scan_resp scan_resp;
+ wifi_timeval t;
+ ENTER();
+ // Don't block ACS scan
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return MTRUE;
+ }
+ // Don't block scan when non any interface active
+ if (!woal_is_any_interface_active(priv->phandle)) {
+ LEAVE();
+ return MTRUE;
+ }
+#if defined(WIFI_DIRECT_SUPPORT)
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ // Do not skip p2p interface connect scan
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
+ LEAVE();
+ return MTRUE;
+ }
+#endif
+#endif
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
+ LEAVE();
+ return MTRUE;
+ }
+ woal_get_monotonic_time(&t);
+/** scan result expired value */
+#define SCAN_RESULT_EXPIRTED 1
+ if (t.time_sec > (scan_resp.age_in_secs + SCAN_RESULT_EXPIRTED)) {
+ LEAVE();
+ return MTRUE;
+ }
+ LEAVE();
+ return MFALSE;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+/**
+ * @brief Request the driver to do a scan. Always returning
+ * zero meaning that the scan request is given to driver,
+ * and will be valid until passed to cfg80211_scan_done().
+ * To inform scan results, call cfg80211_inform_bss().
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param request A pointer to cfg80211_scan_request structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+#else
+/**
+ * @brief Request the driver to do a scan. Always returning
+ * zero meaning that the scan request is given to driver,
+ * and will be valid until passed to cfg80211_scan_done().
+ * To inform scan results, call cfg80211_inform_bss().
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param request A pointer to cfg80211_scan_request structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_scan_request *request)
+#endif
+{
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct net_device *dev = request->wdev->netdev;
+#endif
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ wlan_user_scan_cfg *scan_req = NULL;
+ mlan_bss_info bss_info;
+ mlan_scan_cfg scan_cfg;
+ struct ieee80211_channel *chan;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ t_u8 buf[ETH_ALEN];
+#endif
+ int ret = 0, i;
+ unsigned long flags;
+
+ ENTER();
+
+ PRINTM(MINFO, "Received scan request on %s\n", dev->name);
+ if (priv->phandle->scan_pending_on_block == MTRUE) {
+ PRINTM(MCMND, "scan already in processing...\n");
+ LEAVE();
+ return -EAGAIN;
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ if (priv->last_event & EVENT_BG_SCAN_REPORT) {
+ PRINTM(MCMND, "block scan while pending BGSCAN result\n");
+ priv->last_event = 0;
+ LEAVE();
+ return -EAGAIN;
+ }
+#endif
+#if defined(STA_CFG80211) || defined(UAP_CFG80211)
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (priv->phandle->is_go_timer_set &&
+ priv->wdev->iftype != NL80211_IFTYPE_P2P_GO) {
+ PRINTM(MCMND, "block scan in go timer....\n");
+ LEAVE();
+ return -EAGAIN;
+ }
+#endif
+#endif
+#endif
+ if (priv->fake_scan_complete || !woal_is_scan_result_expired(priv)) {
+ PRINTM(MEVENT,
+ "scan result not expired or fake scan complete flag is on\n");
+ return -EAGAIN;
+ }
+ memset(&bss_info, 0, sizeof(bss_info));
+ if (MLAN_STATUS_SUCCESS ==
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
+ if (bss_info.scan_block) {
+ PRINTM(MEVENT, "Block scan in mlan module\n");
+ return -EAGAIN;
+ }
+ }
+ if (priv->phandle->scan_request &&
+ priv->phandle->scan_request != request) {
+ PRINTM(MCMND,
+ "different scan_request is coming before previous one is finished on %s...\n",
+ dev->name);
+ LEAVE();
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&priv->phandle->scan_req_lock, flags);
+ priv->phandle->scan_request = request;
+ spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags);
+ if (is_zero_timeval(priv->phandle->scan_time_start)) {
+ woal_get_monotonic_time(&priv->phandle->scan_time_start);
+ PRINTM(MINFO, "%s : start_timeval=%d:%d \n", __func__,
+ priv->phandle->scan_time_start.time_sec,
+ priv->phandle->scan_time_start.time_usec);
+ }
+ scan_req = kmalloc(sizeof(wlan_user_scan_cfg), GFP_KERNEL);
+ memset(scan_req, 0x00, sizeof(wlan_user_scan_cfg));
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+ if (!is_broadcast_ether_addr(request->bssid)) {
+ moal_memcpy_ext(priv->phandle, scan_req->specific_bssid,
+ request->bssid, ETH_ALEN,
+ sizeof(scan_req->specific_bssid));
+ PRINTM(MIOCTL, "scan: bssid=" MACSTR "\n",
+ MAC2STR(scan_req->specific_bssid));
+ }
+#endif
+
+ if (priv->phandle->scan_request->n_channels <= 38)
+ scan_req->ext_scan_type = EXT_SCAN_ENHANCE;
+
+ memset(&scan_cfg, 0, sizeof(mlan_scan_cfg));
+#ifdef WIFI_DIRECT_SUPPORT
+ if (priv->phandle->miracast_mode)
+ scan_req->scan_chan_gap = priv->phandle->scan_chan_gap;
+ else {
+#endif
+ woal_get_scan_config(priv, &scan_cfg);
+ if (scan_cfg.scan_chan_gap)
+ scan_req->scan_chan_gap = scan_cfg.scan_chan_gap;
+ else if (woal_is_any_interface_active(priv->phandle))
+ scan_req->scan_chan_gap = priv->phandle->scan_chan_gap;
+ else
+ scan_req->scan_chan_gap = 0;
+#ifdef WIFI_DIRECT_SUPPORT
+ }
+#endif
+ /** indicate FW, gap is optional */
+ if (scan_req->scan_chan_gap && priv->phandle->pref_mac)
+ scan_req->scan_chan_gap |= GAP_FLAG_OPTIONAL;
+
+ for (i = 0; i < priv->phandle->scan_request->n_ssids; i++) {
+ moal_memcpy_ext(priv->phandle, scan_req->ssid_list[i].ssid,
+ priv->phandle->scan_request->ssids[i].ssid,
+ priv->phandle->scan_request->ssids[i].ssid_len,
+ sizeof(scan_req->ssid_list[i].ssid));
+ if (priv->phandle->scan_request->ssids[i].ssid_len)
+ scan_req->ssid_list[i].max_len = 0;
+ else
+ scan_req->ssid_list[i].max_len = 0xff;
+ PRINTM(MIOCTL, "scan: ssid=%s\n", scan_req->ssid_list[i].ssid);
+ }
+#if defined(WIFI_DIRECT_SUPPORT)
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
+ priv->phandle->scan_request->n_ssids) {
+ if (!memcmp(scan_req->ssid_list[0].ssid, "DIRECT-", 7))
+ scan_req->ssid_list[0].max_len = 0xfe;
+ }
+#endif
+#endif
+ for (i = 0; i < MIN(WLAN_USER_SCAN_CHAN_MAX,
+ priv->phandle->scan_request->n_channels);
+ i++) {
+ chan = priv->phandle->scan_request->channels[i];
+ scan_req->chan_list[i].chan_number = chan->hw_value;
+ scan_req->chan_list[i].radio_type = chan->band;
+ if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) ||
+ !priv->phandle->scan_request->n_ssids)
+ scan_req->chan_list[i].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE;
+ else if (chan->flags & IEEE80211_CHAN_RADAR)
+ scan_req->chan_list[i].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
+ else
+ scan_req->chan_list[i].scan_type =
+ MLAN_SCAN_TYPE_ACTIVE;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ scan_req->chan_list[i].scan_time =
+ priv->phandle->scan_request->duration;
+#else
+ scan_req->chan_list[i].scan_time = 0;
+#endif
+#if defined(WIFI_DIRECT_SUPPORT)
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
+ priv->phandle->scan_request->n_ssids) {
+ if (!memcmp(scan_req->ssid_list[0].ssid, "DIRECT-", 7))
+ scan_req->chan_list[i].scan_time =
+ MIN_SPECIFIC_SCAN_CHAN_TIME;
+ }
+#endif
+#endif
+#ifdef WIFI_DIRECT_SUPPORT
+ if (priv->phandle->miracast_mode)
+ scan_req->chan_list[i].scan_time =
+ priv->phandle->miracast_scan_time;
+ else if (woal_is_any_interface_active(priv->phandle)) {
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ scan_req->chan_list[i].scan_time =
+ INIT_PASSIVE_SCAN_CHAN_TIME;
+ else
+ scan_req->chan_list[i].scan_time =
+ MIN_SPECIFIC_SCAN_CHAN_TIME;
+ }
+#endif
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)
+ scan_req->chan_list[i].scan_time =
+ MIN_SPECIFIC_SCAN_CHAN_TIME;
+#endif
+ }
+ if (priv->phandle->scan_request->ie &&
+ priv->phandle->scan_request->ie_len) {
+ if (woal_find_wps_ie_in_probereq(
+ (t_u8 *)priv->phandle->scan_request->ie,
+ priv->phandle->scan_request->ie_len)) {
+ PRINTM(MIOCTL,
+ "Notify firmware only keep probe response\n");
+ scan_req->proberesp_only = MTRUE;
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(
+ priv, NULL, 0, NULL, 0, NULL, 0,
+ (t_u8 *)priv->phandle->scan_request->ie,
+ priv->phandle->scan_request->ie_len,
+ MGMT_MASK_PROBE_REQ, MOAL_IOCTL_WAIT)) {
+ PRINTM(MERROR, "Fail to set scan request IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ /** Clear SCAN IE in Firmware */
+ if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK)
+ woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0,
+ NULL, 0, NULL, 0,
+ MGMT_MASK_PROBE_REQ,
+ MOAL_IOCTL_WAIT);
+ }
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ /** use sync scan for uap */
+ ret = woal_uap_scan(priv, scan_req);
+ if (!ret) {
+ kfree(scan_req);
+ LEAVE();
+ return ret;
+ } else {
+ PRINTM(MERROR, "Uap SCAN failure\n");
+ goto done;
+ }
+ }
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ PRINTM(MIOCTL, "NL80211_SCAN_FLAG_RANDOM_ADDR is set\n");
+ get_random_bytes(buf, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ buf[i] &= ~request->mac_addr_mask[i];
+ buf[i] |= request->mac_addr[i] &
+ request->mac_addr_mask[i];
+ }
+ moal_memcpy_ext(priv->phandle, scan_req->random_mac, buf,
+ ETH_ALEN, sizeof(scan_req->random_mac));
+ } else
+#endif
+ moal_memcpy_ext(priv->phandle, scan_req->random_mac,
+ priv->random_mac, ETH_ALEN,
+ sizeof(scan_req->random_mac));
+
+ PRINTM(MCMND, "wlan:random_mac " MACSTR "\n",
+ MAC2STR(scan_req->random_mac));
+ if (MLAN_STATUS_SUCCESS != woal_do_scan(priv, scan_req)) {
+ PRINTM(MERROR, "woal_do_scan fails!\n");
+ ret = -EAGAIN;
+ goto done;
+ }
+done:
+ if (ret) {
+ spin_lock_irqsave(&priv->phandle->scan_req_lock, flags);
+ woal_cfg80211_scan_done(request, MTRUE);
+ priv->phandle->scan_request = NULL;
+ priv->phandle->scan_priv = NULL;
+ spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags);
+ } else
+ PRINTM(MMSG, "wlan: %s START SCAN\n", dev->name);
+ kfree(scan_req);
+ LEAVE();
+ return ret;
+}
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+static void woal_cfg80211_abort_scan(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
+ ENTER();
+ PRINTM(MMSG, "wlan: ABORT SCAN start\n");
+ woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
+ LEAVE();
+ return;
+}
+#endif
+/**
+ * @brief construct and send ft action request
+ *
+ * @param priv A pointer to moal_private structure
+ * @param ie A pointer to ft ie
+ * @param le Value of ie len
+ * @param bssid A pointer to target ap bssid
+ * @
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_send_ft_action_requst(moal_private *priv, t_u8 *ie, t_u8 len,
+ t_u8 *bssid, t_u8 *target_ap)
+{
+ IEEE80211_MGMT *mgmt = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ pmlan_buffer pmbuf = NULL;
+ t_u32 pkt_type;
+ t_u32 tx_control;
+ t_u16 packet_len = 0;
+ t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ int ret = 0;
+
+ ENTER();
+
+ /* pkt_type + tx_control */
+#define HEADER_SIZE 8
+ /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4*/
+#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6)
+ /* 14 = category + action + sta addr + target ap */
+#define FT_REQUEST_LEN 14
+ packet_len = (t_u16)len + MGMT_HEADER_LEN + FT_REQUEST_LEN;
+ pmbuf = woal_alloc_mlan_buffer(priv->phandle,
+ MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE +
+ packet_len + sizeof(packet_len));
+ if (!pmbuf) {
+ PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
+ pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
+ tx_control = 0;
+ /* Add pkt_type and tx_control */
+ moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset,
+ &pkt_type, sizeof(pkt_type), sizeof(pkt_type));
+ moal_memcpy_ext(priv->phandle,
+ pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
+ &tx_control, sizeof(tx_control), sizeof(tx_control));
+ /*Add packet len*/
+ moal_memcpy_ext(priv->phandle,
+ pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
+ &packet_len, sizeof(packet_len), sizeof(packet_len));
+
+ mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
+ HEADER_SIZE + sizeof(packet_len));
+ memset(mgmt, 0, MGMT_HEADER_LEN);
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
+ moal_memcpy_ext(priv->phandle, mgmt->da, bssid, ETH_ALEN,
+ sizeof(mgmt->da));
+ moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
+ sizeof(mgmt->sa));
+ moal_memcpy_ext(priv->phandle, mgmt->bssid, bssid, ETH_ALEN,
+ sizeof(mgmt->bssid));
+ moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN,
+ sizeof(mgmt->addr4));
+
+ mgmt->u.ft_req.category = 0x06; /**ft action code 0x6*/
+ mgmt->u.ft_req.action = 0x1; /**ft action request*/
+ moal_memcpy_ext(priv->phandle, mgmt->u.ft_req.sta_addr,
+ priv->current_addr, ETH_ALEN,
+ sizeof(mgmt->u.ft_req.sta_addr));
+ moal_memcpy_ext(priv->phandle, mgmt->u.ft_req.target_ap_addr, target_ap,
+ ETH_ALEN, sizeof(mgmt->u.ft_req.target_ap_addr));
+
+ if (ie && len)
+ moal_memcpy_ext(priv->phandle,
+ (t_u8 *)(&mgmt->u.ft_req.variable), ie, len,
+ len);
+
+ pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len);
+ pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
+ pmbuf->bss_index = priv->bss_index;
+ pmbuf->priority = 7;
+
+ status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
+
+ switch (status) {
+ case MLAN_STATUS_PENDING:
+ atomic_inc(&priv->phandle->tx_pending);
+ queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
+ break;
+ case MLAN_STATUS_SUCCESS:
+ woal_free_mlan_buffer(priv->phandle, pmbuf);
+ break;
+ case MLAN_STATUS_FAILURE:
+ default:
+ woal_free_mlan_buffer(priv->phandle, pmbuf);
+ ret = -EFAULT;
+ break;
+ }
+
+done:
+ LEAVE();
+ return ret;
+}
+/**
+ * @brief construct and send ft auth request
+ *
+ * @param priv A pointer to moal_private structure
+ * @param ie A pointer to ft ie
+ * @param le Value of ie len
+ * @param bssid A pointer to target ap bssid
+ * @
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_send_ft_auth_requst(moal_private *priv, t_u8 *ie, t_u8 len,
+ t_u8 *bssid)
+{
+ IEEE80211_MGMT *mgmt = NULL;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ pmlan_buffer pmbuf = NULL;
+ t_u32 pkt_type;
+ t_u32 tx_control;
+ t_u16 packet_len = 0;
+ t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ int ret = 0;
+
+ ENTER();
+ /* pkt_type + tx_control */
+#define HEADER_SIZE 8
+ /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4*/
+#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6)
+ /* 6 = auth_alg + auth_transaction +auth_status*/
+#define AUTH_BODY_LEN 6
+ packet_len = (t_u16)len + MGMT_HEADER_LEN + AUTH_BODY_LEN;
+ pmbuf = woal_alloc_mlan_buffer(priv->phandle,
+ MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE +
+ packet_len + sizeof(packet_len));
+ if (!pmbuf) {
+ PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
+ pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
+ tx_control = 0;
+ /* Add pkt_type and tx_control */
+ moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset,
+ &pkt_type, sizeof(pkt_type), sizeof(pkt_type));
+ moal_memcpy_ext(priv->phandle,
+ pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
+ &tx_control, sizeof(tx_control), sizeof(tx_control));
+ /*Add packet len*/
+ moal_memcpy_ext(priv->phandle,
+ pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
+ &packet_len, sizeof(packet_len), sizeof(packet_len));
+
+ mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
+ HEADER_SIZE + sizeof(packet_len));
+ memset(mgmt, 0, MGMT_HEADER_LEN);
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ moal_memcpy_ext(priv->phandle, mgmt->da, bssid, ETH_ALEN,
+ sizeof(mgmt->da));
+ moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
+ sizeof(mgmt->sa));
+ moal_memcpy_ext(priv->phandle, mgmt->bssid, bssid, ETH_ALEN,
+ sizeof(mgmt->bssid));
+ moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN,
+ sizeof(mgmt->addr4));
+
+ mgmt->u.auth.auth_alg = cpu_to_le16(WLAN_AUTH_FT);
+ mgmt->u.auth.auth_transaction = cpu_to_le16(1);
+ mgmt->u.auth.status_code = cpu_to_le16(0);
+ if (ie && len)
+ moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable),
+ ie, len, len);
+
+ pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len);
+ pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
+ pmbuf->bss_index = priv->bss_index;
+ pmbuf->priority = 7;
+
+ status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
+
+ switch (status) {
+ case MLAN_STATUS_PENDING:
+ atomic_inc(&priv->phandle->tx_pending);
+ queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
+ break;
+ case MLAN_STATUS_SUCCESS:
+ woal_free_mlan_buffer(priv->phandle, pmbuf);
+ break;
+ case MLAN_STATUS_FAILURE:
+ default:
+ woal_free_mlan_buffer(priv->phandle, pmbuf);
+ ret = -EFAULT;
+ break;
+ }
+
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief connect the AP through ft over air.
+ *
+ * @param priv A pointer to moal_private structure
+ * @param bssid A pointer to bssid
+ * @param chan struct ieee80211_channel
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_connect_ft_over_air(moal_private *priv, t_u8 *bssid,
+ struct ieee80211_channel *chan)
+{
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ t_u8 status = 0;
+#endif
+
+ t_u8 wait_option = MOAL_IOCTL_WAIT;
+ int ret = 0;
+ long timeout = 0;
+
+ ENTER();
+
+ if (!bssid) {
+ PRINTM(MERROR,
+ "Invalid bssid, unable to connect AP to through FT\n");
+ LEAVE();
+ return -EFAULT;
+ }
+
+ /*enable auth register frame*/
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MTRUE);
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+#define AUTH_TX_DEFAULT_WAIT_TIME 1200
+ woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE, &status,
+ chan, 0, AUTH_TX_DEFAULT_WAIT_TIME);
+#endif
+
+ /*construct auth request and send out*/
+ woal_send_ft_auth_requst(priv, priv->ft_ie, priv->ft_ie_len, bssid);
+ PRINTM(MMSG, "wlan: send out FT auth,wait for auth response\n");
+ /*wait until received auth response*/
+ priv->ft_wait_condition = MFALSE;
+ timeout = wait_event_timeout(priv->ft_wait_q, priv->ft_wait_condition,
+ 1 * HZ);
+ if (!timeout) {
+ /*connet fail */
+ if (!priv->ft_roaming_triggered_by_driver) {
+ woal_inform_bss_from_scan_result(priv, NULL,
+ wait_option);
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
+ NULL, 0, NULL, 0,
+ WLAN_STATUS_SUCCESS,
+ GFP_KERNEL);
+ }
+ priv->ft_roaming_triggered_by_driver = MFALSE;
+ PRINTM(MMSG, "wlan: keep connected to bssid " MACSTR "\n",
+ MAC2STR(priv->cfg_bssid));
+ } else {
+ PRINTM(MMSG, "wlan: FT auth received \n");
+ moal_memcpy_ext(priv->phandle, priv->target_ap_bssid, bssid,
+ ETH_ALEN, sizeof(priv->target_ap_bssid));
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE, &status,
+ NULL, 0, 0);
+#endif
+
+ woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief connect the AP through ft over DS.
+ *
+ * @param priv A pointer to moal_private structure
+ * @param bssid A pointer to bssid
+ * @param chan struct ieee80211_channel
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_connect_ft_over_ds(moal_private *priv, t_u8 *bssid,
+ struct ieee80211_channel *pchan)
+{
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ t_u8 status = 0;
+#endif
+ t_u8 wait_option = MOAL_IOCTL_WAIT;
+ int ret = 0;
+ long timeout = 0;
+
+ ENTER();
+
+ if (priv->media_connected) {
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE,
+ &status, pchan, 0, 1200);
+#endif
+ /*construct ft action request and send out*/
+ woal_send_ft_action_requst(priv, priv->ft_ie, priv->ft_ie_len,
+ (t_u8 *)priv->cfg_bssid, bssid);
+ PRINTM(MMSG,
+ "wlan: send out FT request,wait for FT response\n");
+ /*wait until received auth response*/
+ priv->ft_wait_condition = MFALSE;
+ timeout = wait_event_timeout(priv->ft_wait_q,
+ priv->ft_wait_condition, 1 * HZ);
+ if (!timeout) {
+ /*go over air, as current AP may be unreachable */
+ PRINTM(MMSG, "wlan: go over air\n");
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ woal_cfg80211_remain_on_channel_cfg(
+ priv, wait_option, MTRUE, &status, NULL, 0, 0);
+#endif
+ woal_connect_ft_over_air(priv, bssid, pchan);
+ LEAVE();
+ return ret;
+ } else {
+ PRINTM(MMSG, "wlan: received FT response\n");
+ moal_memcpy_ext(priv->phandle, priv->target_ap_bssid,
+ bssid, ETH_ALEN,
+ sizeof(priv->target_ap_bssid));
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE,
+ &status, NULL, 0, 0);
+#endif
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief start FT Roaming.
+ *
+ * @param priv A pointer to moal_private structure
+ * @param ssid_bssid A pointer to mlan_ssid_bssid structure
+ *
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_start_ft_roaming(moal_private *priv,
+ mlan_ssid_bssid *ssid_bssid)
+{
+ struct ieee80211_channel chan;
+ int ret = 0;
+
+ ENTER();
+
+ PRINTM(MEVENT, "Try to start FT roaming......\n");
+ chan.band = (ssid_bssid->channel < 36) ? IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ;
+ chan.center_freq = ieee80211_channel_to_frequency(ssid_bssid->channel
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ ,
+ chan.band
+#endif
+ );
+
+ if (!(priv->last_event & EVENT_PRE_BCN_LOST) &&
+ (ssid_bssid->ft_cap & MBIT(0))) {
+ woal_connect_ft_over_ds(priv, (t_u8 *)&ssid_bssid->bssid,
+ &chan);
+ } else {
+ /*if pre beacon lost, it need to send auth request instead ft
+ * action request when ft over ds */
+ woal_connect_ft_over_air(priv, (t_u8 *)&ssid_bssid->bssid,
+ &chan);
+ }
+
+ LEAVE();
+ return ret;
+}
+/**
+ * @brief Request the driver to connect to the ESS with
+ * the specified parameters from kernel
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param sme A pointer to cfg80211_connect_params structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ int ret = 0;
+ mlan_bss_info bss_info;
+ unsigned long flags;
+ mlan_ds_misc_assoc_rsp *assoc_rsp = NULL;
+ IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
+ mlan_ssid_bssid ssid_bssid;
+ moal_handle *handle = priv->phandle;
+ int i;
+
+ ENTER();
+
+ PRINTM(MINFO, "Received association request on %s\n", dev->name);
+ priv->cfg_disconnect = MFALSE;
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return 0;
+ }
+#endif
+ if (priv->wdev->iftype != NL80211_IFTYPE_STATION
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ && priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT
+#endif /* KERNEL_VERSION */
+#endif /* WIFI_DIRECT_SUPPORT */
+ ) {
+ PRINTM(MERROR,
+ "Received infra assoc request when station not in infra mode\n");
+ LEAVE();
+ return -EINVAL;
+ }
+ memset(&ssid_bssid, 0, sizeof(ssid_bssid));
+ moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid.ssid, sme->ssid,
+ sme->ssid_len, sizeof(ssid_bssid.ssid.ssid));
+ ssid_bssid.ssid.ssid_len = sme->ssid_len;
+ if (sme->bssid)
+ moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, sme->bssid,
+ ETH_ALEN, sizeof(ssid_bssid.bssid));
+ /* Not allowed to connect to the same AP which is already connected
+ with other interface */
+ for (i = 0; i < handle->priv_num; i++) {
+ if (handle->priv[i] != priv &&
+ MTRUE == woal_is_connected(handle->priv[i], &ssid_bssid)) {
+ PRINTM(MMSG,
+ "wlan: already connected with other interface, bssid " MACSTR
+ "\n",
+ MAC2STR(handle->priv[i]->cfg_bssid));
+ LEAVE();
+ return -EINVAL;
+ }
+ }
+
+ /** cancel pending scan */
+ woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
+
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
+ (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ /* if bsstype == wifi direct, and iftype == station or p2p
+ * client, that means wpa_supplicant wants to enable wifi direct
+ * functionality, so we should init p2p client.
+ *
+ * Note that due to kernel iftype check, ICS wpa_supplicant
+ * could not updaet iftype to init p2p client, so we have to
+ * done it here.
+ * */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_init_p2p_client(priv)) {
+ PRINTM(MERROR,
+ "Init p2p client for wpa_supplicant failed.\n");
+ ret = -EFAULT;
+
+ LEAVE();
+ return ret;
+ }
+ }
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
+ /* WAR for P2P connection with vendor TV */
+ woal_sched_timeout(200);
+ }
+#endif
+#endif
+ /*11r roaming triggered by supplicant */
+ if (priv->media_connected && priv->ft_ie_len &&
+ !(priv->ft_cap & MBIT(0))) {
+ /** get current bss info */
+ memset(&bss_info, 0, sizeof(bss_info));
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ /** get target bss info */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_find_essid(priv, &ssid_bssid, MOAL_IOCTL_WAIT)) {
+ ret = woal_cfg80211_connect_scan(priv, sme,
+ MOAL_IOCTL_WAIT);
+ if (!ret) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_find_best_network(priv,
+ MOAL_IOCTL_WAIT,
+ &ssid_bssid)) {
+ PRINTM(MERROR,
+ "can't find targe AP \n");
+ // LEAVE();
+ // return -EFAULT;
+ }
+ }
+ }
+ if (bss_info.mdid == ssid_bssid.ft_md &&
+ bss_info.ft_cap == ssid_bssid.ft_cap) {
+ ret = woal_start_ft_roaming(priv, &ssid_bssid);
+ LEAVE();
+ return 0;
+ }
+ }
+
+ priv->cfg_connect = MTRUE;
+ if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)
+ woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE);
+ priv->assoc_status = 0;
+ assoc_rsp = kzalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_ATOMIC);
+ if (!assoc_rsp) {
+ PRINTM(MERROR, "Failed to allocate memory for assoc_rsp\n");
+ ret = -ENOMEM;
+ LEAVE();
+ return ret;
+ }
+ ret = woal_cfg80211_assoc(priv, (void *)sme, MOAL_IOCTL_WAIT,
+ assoc_rsp);
+
+ if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)
+ woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE);
+ if (!ret) {
+ passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp->assoc_resp_buf;
+ priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
+ if (priv->bss_type == MLAN_BSS_TYPE_STA)
+ woal_save_conn_params(priv, sme);
+ memset(&bss_info, 0, sizeof(bss_info));
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ priv->channel = bss_info.bss_chan;
+ if (!ssid_bssid.ft_md) {
+ priv->ft_ie_len = 0;
+ priv->ft_pre_connect = MFALSE;
+ priv->ft_md = 0;
+ priv->ft_cap = 0;
+ }
+ }
+ spin_lock_irqsave(&priv->connect_lock, flags);
+ priv->cfg_connect = MFALSE;
+ if (!ret && priv->media_connected) {
+ PRINTM(MMSG,
+ "wlan: Connected to bssid " MACSTR " successfully\n",
+ MAC2STR(priv->cfg_bssid));
+ spin_unlock_irqrestore(&priv->connect_lock, flags);
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
+ passoc_rsp->ie_buffer,
+ assoc_rsp->assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE,
+ WLAN_STATUS_SUCCESS, GFP_KERNEL);
+ } else {
+ PRINTM(MINFO, "wlan: Failed to connect to bssid " MACSTR "\n",
+ MAC2STR(priv->cfg_bssid));
+ memset(priv->cfg_bssid, 0, ETH_ALEN);
+ spin_unlock_irqrestore(&priv->connect_lock, flags);
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
+ NULL, 0, woal_get_assoc_status(priv),
+ GFP_KERNEL);
+ }
+
+ kfree(assoc_rsp);
+ assoc_rsp = NULL;
+ LEAVE();
+ return 0;
+}
+
+/**
+ * @brief This function will print diconnect reason code according
+ * to IEEE 802.11 spec
+ *
+ * @param reason_code reason code for the deauth/disaccoc
+ * received from firmware
+ * @return N/A
+ */
+static void woal_print_disconnect_reason(t_u16 reason_code)
+{
+ ENTER();
+
+ switch (reason_code) {
+ case MLAN_REASON_UNSPECIFIED:
+ PRINTM(MMSG, "wlan: REASON: Unspecified reason\n");
+ break;
+ case MLAN_REASON_PREV_AUTH_NOT_VALID:
+ PRINTM(MMSG,
+ "wlan: REASON: Previous authentication no longer valid\n");
+ break;
+ case MLAN_REASON_DEAUTH_LEAVING:
+ PRINTM(MMSG,
+ "wlan: REASON: (Deauth) Sending STA is leaving (or has left) IBSS or ESS\n");
+ break;
+ case MLAN_REASON_DISASSOC_DUE_TO_INACTIVITY:
+ PRINTM(MMSG,
+ "wlan: REASON: Disassociated due to inactivity \n");
+ break;
+ case MLAN_REASON_DISASSOC_AP_BUSY:
+ PRINTM(MMSG,
+ "wlan: REASON: (Disassociated) AP unable to handle all connected STAs\n");
+ break;
+ case MLAN_REASON_CLASS2_FRAME_FROM_NOAUTH_STA:
+ PRINTM(MMSG,
+ "wlan: REASON: Class 2 frame was received from nonauthenticated STA\n");
+ break;
+ case MLAN_REASON_CLASS3_FRAME_FROM_NOASSOC_STA:
+ PRINTM(MMSG,
+ "wlan: REASON: Class 3 frame was received from nonassociated STA\n");
+ break;
+ case MLAN_REASON_DISASSOC_STA_HAS_LEFT:
+ PRINTM(MMSG,
+ "wlan: REASON: (Disassocated) Sending STA is leaving (or has left) BSS\n");
+ break;
+ case MLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH:
+ PRINTM(MMSG,
+ "wlan: REASON: STA requesting (re)assoc is not authenticated with responding STA\n");
+ break;
+ default:
+ break;
+ }
+
+ LEAVE();
+ return;
+}
+
+/**
+ * @brief Request the driver to disconnect
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param reason_code Reason code
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ t_u16 reason_code)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+
+ ENTER();
+ PRINTM(MMSG,
+ "wlan: Received disassociation request on %s, reason: %u\n",
+ dev->name, reason_code);
+ woal_print_disconnect_reason(reason_code);
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return 0;
+ }
+#endif
+ if (priv->phandle->driver_status) {
+ PRINTM(MERROR,
+ "Block woal_cfg80211_disconnect in abnormal driver state\n");
+ LEAVE();
+ return -EFAULT;
+ }
+
+ if (priv->cfg_disconnect) {
+ PRINTM(MERROR, "Disassociation already in progress\n");
+ LEAVE();
+ return 0;
+ }
+
+ if (priv->media_connected == MFALSE) {
+ PRINTM(MMSG, " Already disconnected\n");
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ if (priv->wdev->current_bss &&
+ (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ priv->cfg_disconnect = MTRUE;
+ cfg80211_disconnected(priv->netdev, 0, NULL, 0,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ true,
+#endif
+ GFP_KERNEL);
+ }
+#endif
+ LEAVE();
+ return 0;
+ }
+
+ priv->cfg_disconnect = MTRUE;
+ if (woal_disconnect(priv, MOAL_IOCTL_WAIT_TIMEOUT, priv->cfg_bssid,
+ reason_code) != MLAN_STATUS_SUCCESS) {
+ priv->cfg_disconnect = MFALSE;
+ LEAVE();
+ return -EFAULT;
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ if (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)
+ cfg80211_disconnected(priv->netdev, 0, NULL, 0,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ true,
+#endif
+ GFP_KERNEL);
+#endif
+
+ memset(priv->cfg_bssid, 0, ETH_ALEN);
+ if (priv->bss_type == MLAN_BSS_TYPE_STA)
+ woal_clear_conn_params(priv);
+ priv->channel = 0;
+
+ LEAVE();
+ return 0;
+}
+
+/**
+ * @brief This function is deauthentication handler when host MLME
+ * enable.
+ * In this case driver will prepare and send Deauth Req.
+ *
+ * @param wiphy A pointer to wiphy.
+ *
+ * @param dev A pointer to net_device
+ *
+ * @param req A pointer to cfg80211_deauth_request
+ *
+ * @return 0 -- success, otherwise fail
+ */
+
+static int woal_cfg80211_deauthenticate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_deauth_request *req)
+{
+ int ret = 0;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ if (priv->host_mlme) {
+ priv->host_mlme = MFALSE;
+ priv->auth_flag = 0;
+ priv->auth_alg = 0xFFFF;
+ /*send deauth packet to notify disconnection to wpa_supplicant
+ */
+ woal_deauth_event(priv, req->reason_code);
+ }
+#endif
+
+ ret = woal_cfg80211_disconnect(wiphy, dev, req->reason_code);
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+ if (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)
+ cfg80211_disconnected(priv->netdev, 0, NULL, 0, GFP_KERNEL);
+#endif
+ return ret;
+}
+
+/**
+ * @brief This function is disassociation handler when host MLME
+ * enable.
+ * In this case driver will prepare and send Disassoc frame.
+ *
+ * @param wiphy A pointer to wiphy.
+ *
+ * @param dev A pointer to net_device
+ *
+ * @param req A pointer to cfg80211_disassoc_request
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_disassociate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_disassoc_request *req)
+{
+ int ret = 0;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ if (priv->host_mlme) {
+ priv->host_mlme = MFALSE;
+ priv->auth_flag = 0;
+ priv->auth_alg = 0xFFFF;
+ /*send deauth packet to notify disconnection to wpa_supplicant
+ */
+ woal_deauth_event(priv, req->reason_code);
+ }
+#endif
+
+ ret = woal_cfg80211_disconnect(wiphy, dev, req->reason_code);
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+ if (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
+ priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)
+ cfg80211_disconnected(priv->netdev, 0, NULL, 0, GFP_KERNEL);
+#endif
+ return ret;
+}
+
+/**
+ * @brief Request the driver to get the station information
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param mac MAC address of the station
+ * @param sinfo A pointer to station_info structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *mac,
+#else
+ u8 *mac,
+#endif
+ struct station_info *sinfo)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+
+ ENTER();
+
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return woal_uap_cfg80211_get_station(wiphy, dev, mac, sinfo);
+ }
+#endif
+ if (priv->media_connected == MFALSE) {
+ PRINTM(MINFO, "cfg80211: Media not connected!\n");
+ LEAVE();
+ return -ENOENT;
+ }
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_dump_station_info(priv, sinfo)) {
+ PRINTM(MERROR, "cfg80211: Failed to get station info\n");
+ ret = -EFAULT;
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+#endif
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Request the driver to dump the station information
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param idx Station index
+ * @param mac MAC address of the station
+ * @param sinfo A pointer to station_info structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_dump_station(struct wiphy *wiphy,
+ struct net_device *dev, int idx,
+ t_u8 *mac, struct station_info *sinfo)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+
+ ENTER();
+
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return woal_uap_cfg80211_dump_station(wiphy, dev, idx, mac,
+ sinfo);
+ }
+#endif
+
+ if (!priv->media_connected || idx != 0) {
+ PRINTM(MINFO,
+ "cfg80211: Media not connected or not for this station!\n");
+ LEAVE();
+ return -ENOENT;
+ }
+
+ moal_memcpy_ext(priv->phandle, mac, priv->cfg_bssid, ETH_ALEN,
+ ETH_ALEN);
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_dump_station_info(priv, sinfo)) {
+ PRINTM(MERROR, "cfg80211: Failed to get station info\n");
+ ret = -EFAULT;
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Convert driver band configuration to IEEE band type
+ *
+ * @param band Driver band configuration
+ *
+ * @return IEEE band type
+ */
+t_u8 woal_bandcfg_to_ieee_band(Band_Config_t bandcfg)
+{
+ t_u8 ret_radio_type = 0;
+
+ ENTER();
+
+ switch (bandcfg.chanBand) {
+ case BAND_5GHZ:
+ ret_radio_type = IEEE80211_BAND_5GHZ;
+ break;
+ case BAND_2GHZ:
+ default:
+ ret_radio_type = IEEE80211_BAND_2GHZ;
+ break;
+ }
+ LEAVE();
+ return ret_radio_type;
+}
+
+/**
+ * @brief Request the driver to dump survey info
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param idx Station index
+ * @param survey A pointer to survey_info structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_dump_survey(struct wiphy *wiphy,
+ struct net_device *dev, int idx,
+ struct survey_info *survey)
+{
+ int ret = -ENOENT;
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ enum ieee80211_band band;
+ ChanStatistics_t *pchan_stats = NULL;
+ mlan_scan_resp scan_resp;
+
+ ENTER();
+ PRINTM(MIOCTL, "dump_survey idx=%d\n", idx);
+
+ memset(&scan_resp, 0, sizeof(scan_resp));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ pchan_stats = (ChanStatistics_t *)scan_resp.pchan_stats;
+ if (idx > scan_resp.num_in_chan_stats || idx < 0) {
+ ret = -EFAULT;
+ goto done;
+ }
+ if (idx == scan_resp.num_in_chan_stats ||
+ !pchan_stats[idx].cca_scan_duration)
+ goto done;
+ ret = 0;
+ memset(survey, 0, sizeof(*survey));
+ band = woal_bandcfg_to_ieee_band(pchan_stats[idx].bandcfg);
+ survey->channel = ieee80211_get_channel(
+ wiphy, ieee80211_channel_to_frequency(pchan_stats[idx].chan_num
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ ,
+ band
+#endif
+ ));
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ survey->noise = pchan_stats[idx].noise;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ survey->filled |= SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY;
+ survey->time = pchan_stats[idx].cca_scan_duration;
+ survey->time_busy = pchan_stats[idx].cca_busy_duration;
+#else
+ survey->filled |=
+ SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY;
+ survey->channel_time = pchan_stats[idx].cca_scan_duration;
+ survey->channel_time_busy = pchan_stats[idx].cca_busy_duration;
+#endif
+#endif
+done:
+ LEAVE();
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+static int woal_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
+ chan_band_info channel;
+
+ memset(&channel, 0x00, sizeof(channel));
+
+#ifdef UAP_SUPPORT
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ if (priv->bss_started == MTRUE) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_ap_channel(priv, MLAN_ACT_GET,
+ MOAL_IOCTL_WAIT,
+ &channel)) {
+ PRINTM(MERROR, "Fail to get ap channel \n");
+ return -EFAULT;
+ }
+ } else {
+ PRINTM(MIOCTL, "get_channel when AP is not started\n");
+ return -EFAULT;
+ }
+ } else
+#endif
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
+ if (priv->media_connected == MTRUE) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_sta_channel(priv, MOAL_IOCTL_WAIT,
+ &channel)) {
+ PRINTM(MERROR, "Fail to get sta channel \n");
+ return -EFAULT;
+ }
+ } else {
+ PRINTM(MIOCTL,
+ "get_channel when STA is not connected\n");
+ return -EFAULT;
+ }
+ } else {
+ PRINTM(MERROR, "BssRole not support %d.\n", GET_BSS_ROLE(priv));
+ return -EFAULT;
+ }
+
+ if (MLAN_STATUS_FAILURE == woal_chandef_create(priv, chandef, &channel))
+ return -EFAULT;
+ else
+ return 0;
+}
+#endif
+
+/**
+ * @brief Request the driver to change the IEEE power save
+ * mdoe
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param enabled Enable or disable
+ * @param timeout Timeout value
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev, bool enabled,
+ int timeout)
+{
+ int ret = 0, disabled;
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+
+ ENTER();
+ if (moal_extflg_isset(priv->phandle, EXT_HW_TEST) ||
+ (priv->phandle->params.ps_mode == MLAN_INIT_PARA_DISABLED)) {
+ PRINTM(MIOCTL, "block set power hw_test=%d ps_mode=%d\n",
+ moal_extflg_isset(priv->phandle, EXT_HW_TEST),
+ priv->phandle->params.ps_mode);
+ LEAVE();
+ return -EFAULT;
+ }
+
+ if (priv->phandle->driver_status) {
+ PRINTM(MERROR,
+ "Block woal_cfg80211_set_power_mgmt in abnormal driver state\n");
+ LEAVE();
+ return -EFAULT;
+ }
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
+ PRINTM(MIOCTL, "skip set power for p2p interface\n");
+ LEAVE();
+ return ret;
+ }
+#endif
+#endif
+ if (enabled)
+ disabled = 0;
+ else
+ disabled = 1;
+
+ if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_SET,
+ &disabled, timeout,
+ MOAL_IOCTL_WAIT)) {
+ ret = -EOPNOTSUPP;
+ }
+
+ LEAVE();
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+/**
+ * @brief Request the driver to get the transmit power info
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param type TX power adjustment type
+ * @param dbm TX power in dbm
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_get_tx_power(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev,
+#endif
+ int *dbm)
+{
+ int ret = 0;
+ moal_private *priv = NULL;
+ mlan_power_cfg_t power_cfg;
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+
+ ENTER();
+
+ if (!handle) {
+ PRINTM(MFATAL, "Unable to get handle\n");
+ LEAVE();
+ return -EFAULT;
+ }
+
+ priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
+
+ if (!priv) {
+ PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__);
+ LEAVE();
+ return -EFAULT;
+ }
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) {
+ LEAVE();
+ return -EFAULT;
+ }
+
+ *dbm = power_cfg.power_level;
+
+ LEAVE();
+ return ret;
+}
+/**
+ * @brief Request the driver to change the transmit power
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param type TX power adjustment type
+ * @param dbm TX power in dbm
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_set_tx_power(struct wiphy *wiphy,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev,
+#endif
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ enum tx_power_setting type,
+#else
+ enum nl80211_tx_power_setting type,
+#endif
+ int dbm)
+{
+ int ret = 0;
+ moal_private *priv = NULL;
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+ mlan_power_cfg_t power_cfg;
+
+ ENTER();
+
+ priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
+ if (!priv) {
+ PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__);
+ LEAVE();
+ return -EFAULT;
+ }
+
+ if (type) {
+ power_cfg.is_power_auto = 0;
+ power_cfg.power_level = dbm;
+ } else
+ power_cfg.is_power_auto = 1;
+
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg))
+ ret = -EFAULT;
+
+ LEAVE();
+ return ret;
+}
+#endif
+
+#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
+/**
+ * CFG802.11 operation handler for connection quality monitoring.
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param rssi_thold rssi threshold
+ * @param rssi_hyst rssi hysteresis
+ */
+static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ ENTER();
+ priv->cqm_rssi_thold = rssi_thold;
+ priv->cqm_rssi_high_thold = rssi_thold;
+ priv->cqm_rssi_hyst = rssi_hyst;
+
+ PRINTM(MIOCTL, "rssi_thold=%d rssi_hyst=%d\n", (int)rssi_thold,
+ (int)rssi_hyst);
+ woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT);
+ LEAVE();
+ return 0;
+}
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+/**
+ * @brief remain on channel config
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ * @param cancel cancel remain on channel flag
+ * @param status A pointer to status, success, in process or reject
+ * @param chan A pointer to ieee80211_channel structure
+ * @param channel_type channel_type,
+ * @param duration Duration wait to receive frame
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_remain_on_channel_cfg(moal_private *priv, t_u8 wait_option,
+ t_u8 remove, t_u8 *status,
+ struct ieee80211_channel *chan,
+ enum mlan_channel_type channel_type,
+ t_u32 duration)
+{
+ mlan_ds_remain_chan chan_cfg;
+ int ret = 0;
+
+ ENTER();
+
+ if (!status || (!chan && !remove)) {
+ PRINTM(MERROR,
+ "Invalid parameter status=%p, chan=%p, remove=%d\n",
+ status, chan, remove);
+ LEAVE();
+ return -EFAULT;
+ }
+ memset(&chan_cfg, 0, sizeof(mlan_ds_remain_chan));
+ if (remove) {
+ chan_cfg.remove = MTRUE;
+ } else {
+#ifdef WIFI_DIRECT_SUPPORT
+ if (priv->phandle->is_go_timer_set) {
+ PRINTM(MINFO,
+ "block remain on channel while go timer is on\n");
+ LEAVE();
+ return -EBUSY;
+ }
+#endif
+ if (chan->band == IEEE80211_BAND_2GHZ)
+ chan_cfg.bandcfg.chanBand = BAND_2GHZ;
+ else if (chan->band == IEEE80211_BAND_5GHZ)
+ chan_cfg.bandcfg.chanBand = BAND_5GHZ;
+ switch (channel_type) {
+ case CHAN_HT40MINUS:
+ chan_cfg.bandcfg.chan2Offset = SEC_CHAN_BELOW;
+ chan_cfg.bandcfg.chanWidth = CHAN_BW_40MHZ;
+ break;
+ case CHAN_HT40PLUS:
+ chan_cfg.bandcfg.chan2Offset = SEC_CHAN_ABOVE;
+ chan_cfg.bandcfg.chanWidth = CHAN_BW_40MHZ;
+ break;
+ case CHAN_VHT80:
+ chan_cfg.bandcfg.chanWidth = CHAN_BW_80MHZ;
+ break;
+ case CHAN_NO_HT:
+ case CHAN_HT20:
+ default:
+ break;
+ }
+ chan_cfg.channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ chan_cfg.remain_period = duration;
+ PRINTM(MCMND,
+ "Remain on Channel: chan=%d, offset=%d width=%d\n",
+ chan_cfg.channel, chan_cfg.bandcfg.chan2Offset,
+ chan_cfg.bandcfg.chanWidth);
+ }
+ if (MLAN_STATUS_SUCCESS ==
+ woal_set_remain_channel_ioctl(priv, wait_option, &chan_cfg))
+ *status = chan_cfg.status;
+ else
+ ret = -EFAULT;
+ LEAVE();
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+/**
+ * @brief tx mgmt frame
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param wdev A pointer to wireless_dev structure
+ * @param cookie A pointer to frame cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+#else
+/**
+ * @brief tx mgmt frame
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param cookie A pointer to frame cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct net_device *dev, u64 cookie)
+#endif
+{
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct net_device *dev = wdev->netdev;
+#endif
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ int ret = 0;
+ t_u8 status = 1;
+ moal_private *remain_priv = NULL;
+
+ ENTER();
+
+ if (priv->phandle->remain_on_channel) {
+ remain_priv =
+ priv->phandle->priv[priv->phandle->remain_bss_index];
+ if (!remain_priv) {
+ PRINTM(MERROR,
+ "mgmt_tx_cancel_wait: Wrong remain_bss_index=%d\n",
+ priv->phandle->remain_bss_index);
+ ret = -EFAULT;
+ goto done;
+ }
+ if (woal_cfg80211_remain_on_channel_cfg(remain_priv,
+ MOAL_IOCTL_WAIT, MTRUE,
+ &status, NULL, 0, 0)) {
+ PRINTM(MERROR,
+ "mgmt_tx_cancel_wait: Fail to cancel remain on channel\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ if (priv->phandle->cookie) {
+ cfg80211_remain_on_channel_expired(
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ remain_priv->netdev,
+#else
+ remain_priv->wdev,
+#endif
+ priv->phandle->cookie, &priv->phandle->chan,
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ priv->phandle->channel_type,
+#endif
+ GFP_ATOMIC);
+ priv->phandle->cookie = 0;
+ }
+ priv->phandle->remain_on_channel = MFALSE;
+ }
+
+done:
+ LEAVE();
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+/**
+ * @brief Make chip remain on channel
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param wdev A pointer to wireless_dev structure
+ * @param chan A pointer to ieee80211_channel structure
+ * @param channel_type Channel type
+ * @param duration Duration for timer
+ * @param cookie A pointer to timer cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int
+woal_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ enum nl80211_channel_type channel_type,
+#endif
+ unsigned int duration, u64 *cookie)
+#else
+/**
+ * @brief Make chip remain on channel
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param chan A pointer to ieee80211_channel structure
+ * @param channel_type Channel type
+ * @param duration Duration for timer
+ * @param cookie A pointer to timer cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int
+woal_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ unsigned int duration, u64 *cookie)
+#endif
+{
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct net_device *dev = wdev->netdev;
+#endif
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ int ret = 0;
+ t_u8 status = 1;
+ moal_private *remain_priv = NULL;
+
+ ENTER();
+
+ if (!chan || !cookie) {
+ PRINTM(MERROR, "Invalid parameter for remain on channel\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ /** cancel previous remain on channel */
+ if (priv->phandle->remain_on_channel &&
+ ((priv->phandle->chan.center_freq != chan->center_freq))) {
+ remain_priv =
+ priv->phandle->priv[priv->phandle->remain_bss_index];
+ if (!remain_priv) {
+ PRINTM(MERROR,
+ "remain_on_channel: Wrong remain_bss_index=%d\n",
+ priv->phandle->remain_bss_index);
+ ret = -EFAULT;
+ goto done;
+ }
+ if (woal_cfg80211_remain_on_channel_cfg(remain_priv,
+ MOAL_IOCTL_WAIT, MTRUE,
+ &status, NULL, 0, 0)) {
+ PRINTM(MERROR,
+ "remain_on_channel: Fail to cancel remain on channel\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ priv->phandle->cookie = 0;
+ priv->phandle->remain_on_channel = MFALSE;
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE,
+ &status, chan,
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ channel_type,
+#else
+ 0,
+#endif
+ (t_u32)duration)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (status) {
+ PRINTM(MMSG,
+ "%s: Set remain on Channel: channel=%d with status=%d\n",
+ dev->name,
+ ieee80211_frequency_to_channel(chan->center_freq),
+ status);
+ if (!priv->phandle->remain_on_channel) {
+ priv->phandle->is_remain_timer_set = MTRUE;
+ woal_mod_timer(&priv->phandle->remain_timer, duration);
+ }
+ }
+
+ /* remain on channel operation success */
+ /* we need update the value cookie */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ *cookie = (u64)random32() | 1;
+#else
+ *cookie = (u64)prandom_u32() | 1;
+#endif
+ priv->phandle->remain_on_channel = MTRUE;
+ priv->phandle->remain_bss_index = priv->bss_index;
+ priv->phandle->cookie = *cookie;
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ priv->phandle->channel_type = channel_type;
+#endif
+ moal_memcpy_ext(priv->phandle, &priv->phandle->chan, chan,
+ sizeof(struct ieee80211_channel),
+ sizeof(priv->phandle->chan));
+
+ if (status == 0)
+ PRINTM(MIOCTL,
+ "%s: Set remain on Channel: channel=%d cookie = %#llx\n",
+ dev->name,
+ ieee80211_frequency_to_channel(chan->center_freq),
+ priv->phandle->cookie);
+
+ cfg80211_ready_on_channel(
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ dev,
+#else
+ priv->wdev,
+#endif
+ *cookie, chan,
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ channel_type,
+#endif
+ duration, GFP_KERNEL);
+
+done:
+ LEAVE();
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+/**
+ * @brief Cancel remain on channel
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param wdev A pointer to wireless_dev structure
+ * @param cookie A pointer to timer cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+#else
+/**
+ * @brief Cancel remain on channel
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param cookie A pointer to timer cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct net_device *dev,
+ u64 cookie)
+#endif
+{
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct net_device *dev = wdev->netdev;
+#endif
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ moal_private *remain_priv = NULL;
+ int ret = 0;
+ t_u8 status = 1;
+
+ ENTER();
+ PRINTM(MIOCTL, "Cancel remain on Channel: cookie = %#llx\n", cookie);
+ remain_priv = priv->phandle->priv[priv->phandle->remain_bss_index];
+ if (!remain_priv) {
+ PRINTM(MERROR,
+ "cancel_remain_on_channel: Wrong remain_bss_index=%d\n",
+ priv->phandle->remain_bss_index);
+ ret = -EFAULT;
+ goto done;
+ }
+ if (woal_cfg80211_remain_on_channel_cfg(remain_priv, MOAL_IOCTL_WAIT,
+ MTRUE, &status, NULL, 0, 0)) {
+ PRINTM(MERROR,
+ "cancel_remain_on_channel: Fail to cancel remain on channel\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ priv->phandle->remain_on_channel = MFALSE;
+ if (priv->phandle->cookie)
+ priv->phandle->cookie = 0;
+done:
+ LEAVE();
+ return ret;
+}
+#endif /* KERNEL_VERSION */
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+/**
+ * @brief start sched scan
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param request A pointer to struct cfg80211_sched_scan_request
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_sched_scan_request *request)
+{
+ struct ieee80211_channel *chan = NULL;
+ int i = 0;
+ int ret = 0;
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ struct cfg80211_ssid *ssid = NULL;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ t_u8 buf[ETH_ALEN];
+#endif
+ ENTER();
+
+#ifdef UAP_CFG80211
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
+ LEAVE();
+ return -EFAULT;
+ }
+#endif
+
+ memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg));
+ if (!request) {
+ PRINTM(MERROR, "Invalid sched_scan req parameter\n");
+ LEAVE();
+ return -EINVAL;
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+ PRINTM(MIOCTL,
+ "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n",
+ priv->netdev->name, request->n_ssids, request->n_match_sets,
+ request->n_channels, request->scan_plans[0].interval,
+ (int)request->ie_len);
+#else
+ PRINTM(MIOCTL,
+ "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n",
+ priv->netdev->name, request->n_ssids, request->n_match_sets,
+ request->n_channels, request->interval, (int)request->ie_len);
+#endif
+ /** We have pending scan, start bgscan later */
+ if (priv->phandle->scan_pending_on_block)
+ priv->scan_cfg.start_later = MTRUE;
+ for (i = 0; i < request->n_match_sets; i++) {
+ ssid = &request->match_sets[i].ssid;
+ strncpy(priv->scan_cfg.ssid_list[i].ssid, ssid->ssid,
+ ssid->ssid_len);
+ priv->scan_cfg.ssid_list[i].max_len = 0;
+ PRINTM(MIOCTL, "sched scan: ssid=%s\n", ssid->ssid);
+ }
+ /** Add broadcast scan, when n_match_sets = 0 */
+ if (!request->n_match_sets)
+ priv->scan_cfg.ssid_list[0].max_len = 0xff;
+ for (i = 0; i < MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels); i++) {
+ chan = request->channels[i];
+ priv->scan_cfg.chan_list[i].chan_number = chan->hw_value;
+ priv->scan_cfg.chan_list[i].radio_type = chan->band;
+ if (chan->flags &
+ (IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_RADAR))
+ priv->scan_cfg.chan_list[i].scan_type =
+ MLAN_SCAN_TYPE_PASSIVE;
+ else
+ priv->scan_cfg.chan_list[i].scan_type =
+ MLAN_SCAN_TYPE_ACTIVE;
+ priv->scan_cfg.chan_list[i].scan_time = 0;
+#ifdef WIFI_DIRECT_SUPPORT
+ if (priv->phandle->miracast_mode)
+ priv->scan_cfg.chan_list[i].scan_time =
+ priv->phandle->miracast_scan_time;
+#endif
+ }
+ priv->scan_cfg.chan_per_scan =
+ MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels);
+
+ /** set scan request IES */
+ if (request->ie && request->ie_len) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_cfg80211_mgmt_frame_ie(
+ priv, NULL, 0, NULL, 0, NULL, 0,
+ (t_u8 *)request->ie, request->ie_len,
+ MGMT_MASK_PROBE_REQ, MOAL_IOCTL_WAIT)) {
+ PRINTM(MERROR, "Fail to set sched scan IE\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ } else {
+ /** Clear SCAN IE in Firmware */
+ if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK)
+ woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0,
+ NULL, 0, NULL, 0,
+ MGMT_MASK_PROBE_REQ,
+ MOAL_IOCTL_WAIT);
+ }
+
+ /* Interval between scan cycles in milliseconds,supplicant set to 10
+ * second */
+ /* We want to use 30 second for per scan cycle */
+ priv->scan_cfg.scan_interval = MIN_BGSCAN_INTERVAL;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+ if (request->scan_plans[0].interval * 1000 > MIN_BGSCAN_INTERVAL)
+ priv->scan_cfg.scan_interval =
+ request->scan_plans[0].interval * 1000;
+ if (request->n_scan_plans >= 2) {
+ priv->scan_cfg.config_ees = MTRUE;
+ priv->scan_cfg.ees_mode =
+ MBIT(EES_MODE_HIGH) | MBIT(EES_MODE_MID);
+ priv->scan_cfg.high_period =
+ request->scan_plans[0].interval * 1000;
+ priv->scan_cfg.high_period_count =
+ request->scan_plans[0].iterations;
+ priv->scan_cfg.mid_period = request->scan_plans[1].interval;
+ if (request->scan_plans[1].iterations == 0)
+ priv->scan_cfg.mid_period_count = DEF_REPEAT_COUNT;
+ else
+ priv->scan_cfg.mid_period_count =
+ request->scan_plans[1].iterations;
+ if (request->n_scan_plans == 3) {
+ priv->scan_cfg.ees_mode |= MBIT(EES_MODE_LOW);
+ priv->scan_cfg.low_period =
+ request->scan_plans[2].interval;
+ priv->scan_cfg.low_period_count = DEF_REPEAT_COUNT;
+ }
+ }
+#else
+ if (request->interval > MIN_BGSCAN_INTERVAL)
+ priv->scan_cfg.scan_interval = request->interval;
+#endif
+ priv->scan_cfg.repeat_count = DEF_REPEAT_COUNT;
+ priv->scan_cfg.report_condition =
+ BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE;
+ priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA;
+ priv->scan_cfg.action = BG_SCAN_ACT_SET;
+ priv->scan_cfg.enable = MTRUE;
+#ifdef WIFI_DIRECT_SUPPORT
+ if (priv->phandle->miracast_mode)
+ priv->scan_cfg.scan_chan_gap = priv->phandle->scan_chan_gap;
+ else
+ priv->scan_cfg.scan_chan_gap = 0;
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ PRINTM(MIOCTL, "NL80211_SCAN_FLAG_RANDOM_ADDR is set\n");
+ get_random_bytes(buf, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ buf[i] &= ~request->mac_addr_mask[i];
+ buf[i] |= request->mac_addr[i] &
+ request->mac_addr_mask[i];
+ }
+ moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac, buf,
+ ETH_ALEN, sizeof(priv->scan_cfg.random_mac));
+ } else
+#endif
+ moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac,
+ priv->random_mac, ETH_ALEN,
+ sizeof(priv->scan_cfg.random_mac));
+
+ PRINTM(MCMND, "wlan:random_mac " MACSTR "\n",
+ MAC2STR(priv->scan_cfg.random_mac));
+ if (MLAN_STATUS_SUCCESS ==
+ woal_request_bgscan(priv, MOAL_IOCTL_WAIT, &priv->scan_cfg)) {
+ priv->sched_scanning = MTRUE;
+ priv->bg_scan_start = MTRUE;
+ priv->bg_scan_reported = MFALSE;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ priv->bg_scan_reqid = request->reqid;
+#endif
+ } else
+ ret = -EFAULT;
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief stop sched scan
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ ,
+ u64 reqid
+#endif
+)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ ENTER();
+ PRINTM(MIOCTL, "sched scan stop\n");
+ priv->sched_scanning = MFALSE;
+ woal_stop_bg_scan(priv, MOAL_NO_WAIT);
+ priv->bg_scan_start = MFALSE;
+ priv->bg_scan_reported = MFALSE;
+ LEAVE();
+ return 0;
+}
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+/**
+ * @brief cfg80211_resume handler
+ *
+ * @param wiphy A pointer to wiphy structure
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_resume(struct wiphy *wiphy)
+{
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+ moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM)
+ struct cfg80211_wowlan_wakeup wakeup_report;
+#endif
+ mlan_ds_hs_wakeup_reason wakeup_reason;
+ int i;
+
+ PRINTM(MCMND, "<--- Enter woal_cfg80211_resume --->\n");
+
+ if (!priv) {
+ PRINTM(MERROR, "woal_cfg80211_resume: priv is NULL\n");
+ goto done;
+ }
+
+ for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
+ if (handle->priv[i] &&
+ (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) {
+ if (handle->priv[i]->last_event &
+ EVENT_BG_SCAN_REPORT) {
+ if (handle->priv[i]->sched_scanning) {
+ woal_inform_bss_from_scan_result(
+ handle->priv[i], NULL,
+ MOAL_IOCTL_WAIT);
+ cfg80211_sched_scan_results(
+ handle->priv[i]->wdev->wiphy
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ ,
+ 0
+#endif
+ );
+ woal_bgscan_stop_event(handle->priv[i]);
+ handle->priv[i]->last_event = 0;
+ PRINTM(MIOCTL,
+ "Report sched scan result in cfg80211 resume\n");
+ }
+ if (!moal_extflg_isset(handle, EXT_HW_TEST) &&
+ handle->priv[i]->roaming_enabled) {
+ handle->priv[i]->roaming_required =
+ MTRUE;
+#ifdef ANDROID_KERNEL
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+ __pm_wakeup_event(
+ &handle->ws,
+ ROAMING_WAKE_LOCK_TIMEOUT);
+#else
+ wake_lock_timeout(
+ &handle->wake_lock,
+ msecs_to_jiffies(
+ ROAMING_WAKE_LOCK_TIMEOUT));
+#endif
+#endif
+ wake_up_interruptible(
+ &handle->reassoc_thread.wait_q);
+ }
+ }
+ }
+ }
+
+ woal_get_wakeup_reason(priv, &wakeup_reason);
+
+#ifdef STA_CFG80211
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext))
+ woal_wake_reason_logger(priv, wakeup_reason);
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM)
+ memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup));
+ wakeup_report.pattern_idx = -1;
+
+ switch (wakeup_reason.hs_wakeup_reason) {
+ case NO_HSWAKEUP_REASON:
+ break;
+ case BCAST_DATA_MATCHED:
+ break;
+ case MCAST_DATA_MATCHED:
+ break;
+ case UCAST_DATA_MATCHED:
+ break;
+ case MASKTABLE_EVENT_MATCHED:
+ break;
+ case NON_MASKABLE_EVENT_MATCHED:
+ break;
+ case NON_MASKABLE_CONDITION_MATCHED:
+ if (wiphy->wowlan_config && wiphy->wowlan_config->disconnect)
+ wakeup_report.disconnect = true;
+ break;
+ case MAGIC_PATTERN_MATCHED:
+ if (wiphy->wowlan_config && wiphy->wowlan_config->magic_pkt)
+ wakeup_report.magic_pkt = true;
+ if (wiphy->wowlan_config && wiphy->wowlan_config->n_patterns)
+ wakeup_report.pattern_idx = 1;
+ break;
+ case CONTROL_FRAME_MATCHED:
+ break;
+ case MANAGEMENT_FRAME_MATCHED:
+ break;
+ case GTK_REKEY_FAILURE:
+ if (wiphy->wowlan_config &&
+ wiphy->wowlan_config->gtk_rekey_failure)
+ wakeup_report.gtk_rekey_failure = true;
+ break;
+ default:
+ break;
+ }
+
+ if ((wakeup_reason.hs_wakeup_reason > 0) &&
+ (wakeup_reason.hs_wakeup_reason <= 10)) {
+ cfg80211_report_wowlan_wakeup(priv->wdev, &wakeup_report,
+ GFP_KERNEL);
+ }
+#endif
+
+done:
+ handle->cfg80211_suspend = MFALSE;
+ PRINTM(MCMND, "<--- Leave woal_cfg80211_resume --->\n");
+ return 0;
+}
+
+/**
+ * @brief is_wowlan_pattern_supported
+ *
+ * @param priv A pointer to moal_private
+ * @param pat A pointer to wowlan pattern
+ * @param byte_seq A pointer to byte_seq
+ *
+ * @return 1 -- support, 0 -- not support
+ */
+static t_bool is_wowlan_pattern_supported(moal_private *priv,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ struct cfg80211_pkt_pattern *pat,
+#else
+ struct cfg80211_wowlan_trig_pkt_pattern
+ *pat,
+#endif
+ s8 *byte_seq)
+{
+ int j, k, valid_byte_cnt = 0;
+ t_bool dont_care_byte = MFALSE;
+
+ for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) {
+ for (k = 0; k < 8; k++) {
+ if (pat->mask[j] & 1 << k) {
+ moal_memcpy_ext(priv->phandle,
+ byte_seq + valid_byte_cnt,
+ &pat->pattern[j * 8 + k], 1, 1);
+ valid_byte_cnt++;
+ if (dont_care_byte)
+ return MFALSE;
+ } else {
+ if (valid_byte_cnt)
+ dont_care_byte = MTRUE;
+ }
+
+ if (valid_byte_cnt > MAX_NUM_BYTE_SEQ)
+ return MFALSE;
+ }
+ }
+
+ byte_seq[MAX_NUM_BYTE_SEQ] = valid_byte_cnt;
+
+ return MTRUE;
+}
+
+/**
+ * @brief cfg80211_suspend handler
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param wow A pointer to cfg80211_wowlan
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
+{
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+ int i;
+ int ret = 0;
+ mlan_status status = MLAN_STATUS_SUCCESS;
+ mlan_ds_misc_mef_flt_cfg mef_cfg;
+ mef_entry_t *mef_entry = NULL;
+ int filt_num = 0;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ t_bool first_pat = MTRUE;
+#endif
+ t_u8 byte_seq[MAX_NUM_BYTE_SEQ + 1];
+ const t_u8 ipv4_mc_mac[] = {0x33, 0x33};
+ const t_u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+ moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_STA);
+ mlan_ds_hs_cfg hscfg;
+
+ PRINTM(MCMND, "<--- Enter woal_cfg80211_suspend --->\n");
+ for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
+ if (handle->priv[i] &&
+ (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) {
+ if (handle->scan_request) {
+ PRINTM(MIOCTL,
+ "Cancel pending scan in woal_cfg80211_suspend\n");
+ woal_cancel_scan(handle->priv[i],
+ MOAL_IOCTL_WAIT);
+ }
+ handle->priv[i]->last_event = 0;
+ }
+ }
+
+ handle->cfg80211_suspend = MTRUE;
+ if (!wow) {
+ PRINTM(MERROR, "None of the WOWLAN triggers enabled\n");
+ ret = 0;
+ goto done;
+ }
+
+ if (!priv || !priv->media_connected) {
+ PRINTM(MERROR,
+ "Can not configure WOWLAN in disconnected state\n");
+ ret = 0;
+ goto done;
+ }
+
+ PRINTM(MCMND, "wow->n_patterns=%d\n", wow->n_patterns);
+ PRINTM(MCMND, "wow->any=%d\n", wow->any);
+ PRINTM(MCMND, "wow->disconnect=%d\n", wow->disconnect);
+ PRINTM(MCMND, "wow->magic_pkt=%d\n", wow->magic_pkt);
+ PRINTM(MCMND, "wow->gtk_rekey_failure=%d\n", wow->gtk_rekey_failure);
+ PRINTM(MCMND, "wow->eap_identity_req=%d\n", wow->eap_identity_req);
+ PRINTM(MCMND, "wow->four_way_handshake=%d\n", wow->four_way_handshake);
+ PRINTM(MCMND, "wow->rfkill_release=%d\n", wow->rfkill_release);
+
+ if (!(wow->n_patterns) && !(wow->magic_pkt)) {
+ PRINTM(MCMND, "No pattern or magic packet configured\n");
+ ret = 0;
+ goto done;
+ }
+
+ memset(&mef_cfg, 0, sizeof(mef_cfg));
+ mef_cfg.mef_act_type = MEF_ACT_WOWLAN;
+ mef_entry = &mef_cfg.mef_entry;
+
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
+
+ for (i = 0; i < wow->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!is_wowlan_pattern_supported(priv, &wow->patterns[i],
+ byte_seq)) {
+ PRINTM(MERROR, "Pattern not supported\n");
+ ret = -EOPNOTSUPP;
+ goto done;
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ if (!wow->patterns[i].pkt_offset) {
+#endif
+ if (!(byte_seq[0] & 0x01) &&
+ (byte_seq[MAX_NUM_BYTE_SEQ] == 1)) {
+ mef_cfg.criteria |= CRITERIA_UNICAST;
+ continue;
+ } else if (is_broadcast_ether_addr(byte_seq)) {
+ mef_cfg.criteria |= CRITERIA_BROADCAST;
+ continue;
+ } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ (byte_seq[MAX_NUM_BYTE_SEQ] == 2)) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ (byte_seq[MAX_NUM_BYTE_SEQ] == 3))) {
+ mef_cfg.criteria |= CRITERIA_MULTICAST;
+ continue;
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ }
+
+ mef_entry->filter_item[filt_num].fill_flag =
+ (FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ |
+ FILLING_OFFSET);
+ mef_entry->filter_item[filt_num].repeat = 1;
+ mef_entry->filter_item[filt_num].offset =
+ wow->patterns[i].pkt_offset;
+ moal_memcpy_ext(
+ priv->phandle,
+ mef_entry->filter_item[filt_num].byte_seq, byte_seq,
+ MAX_NUM_BYTE_SEQ,
+ sizeof(mef_entry->filter_item[filt_num].byte_seq));
+ mef_entry->filter_item[filt_num].num_byte_seq =
+ byte_seq[MAX_NUM_BYTE_SEQ];
+ mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ;
+
+ if (first_pat)
+ first_pat = MFALSE;
+ else
+ mef_entry->rpn[filt_num] = RPN_TYPE_OR;
+
+ filt_num++;
+#endif
+ }
+
+ if (wow->magic_pkt) {
+ mef_cfg.criteria |= CRITERIA_UNICAST | CRITERIA_BROADCAST |
+ CRITERIA_MULTICAST;
+ mef_entry->filter_item[filt_num].fill_flag =
+ (FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ |
+ FILLING_OFFSET);
+ mef_entry->filter_item[filt_num].repeat = 16;
+ moal_memcpy_ext(
+ priv->phandle,
+ mef_entry->filter_item[filt_num].byte_seq,
+ priv->current_addr, ETH_ALEN,
+ sizeof(mef_entry->filter_item[filt_num].byte_seq));
+ mef_entry->filter_item[filt_num].num_byte_seq = ETH_ALEN;
+ mef_entry->filter_item[filt_num].offset = 56;
+ mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ;
+ if (filt_num)
+ mef_entry->rpn[filt_num] = RPN_TYPE_OR;
+ filt_num++;
+ mef_entry->filter_item[filt_num].fill_flag =
+ (FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ |
+ FILLING_OFFSET);
+ mef_entry->filter_item[filt_num].repeat = 16;
+ moal_memcpy_ext(
+ priv->phandle,
+ mef_entry->filter_item[filt_num].byte_seq,
+ priv->current_addr, ETH_ALEN,
+ sizeof(mef_entry->filter_item[filt_num].byte_seq));
+ mef_entry->filter_item[filt_num].num_byte_seq = ETH_ALEN;
+ mef_entry->filter_item[filt_num].offset = 28;
+ mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ;
+ if (filt_num)
+ mef_entry->rpn[filt_num] = RPN_TYPE_OR;
+ filt_num++;
+ }
+
+ mef_entry->filter_num = filt_num;
+
+ if (!mef_cfg.criteria)
+ mef_cfg.criteria = CRITERIA_BROADCAST | CRITERIA_UNICAST |
+ CRITERIA_MULTICAST;
+
+ status = woal_set_get_wowlan_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT,
+ &mef_cfg);
+ if (status != MLAN_STATUS_SUCCESS) {
+ PRINTM(MERROR, "woal_set_get_wowlan_config fail!\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg));
+ status = woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
+ &hscfg);
+ if (status != MLAN_STATUS_SUCCESS) {
+ PRINTM(MERROR,
+ "Fail to get HS parameter in woal_cfg80211_suspend: 0x%x 0x%x 0x%x\n",
+ hscfg.conditions, hscfg.gap, hscfg.gpio);
+ ret = -EFAULT;
+ goto done;
+ }
+ hscfg.is_invoke_hostcmd = MFALSE;
+ if (wow->n_patterns || wow->magic_pkt)
+ hscfg.conditions = 0;
+ status = woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT,
+ &hscfg);
+ if (status != MLAN_STATUS_SUCCESS) {
+ PRINTM(MERROR,
+ "Fail to set HS parameter in woal_cfg80211_suspend: 0x%x 0x%x 0x%x\n",
+ hscfg.conditions, hscfg.gap, hscfg.gpio);
+ ret = -EFAULT;
+ goto done;
+ }
+
+done:
+ PRINTM(MCMND, "<--- Leave woal_cfg80211_suspend --->\n");
+ return ret;
+}
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+static void woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled)
+{
+ moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
+
+ device_set_wakeup_enable(handle->hotplug_device, enabled);
+}
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+/**
+ * @brief change station info
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param mac A pointer to peer mac
+ * @param params station parameters
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_change_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *mac,
+#else
+ u8 *mac,
+#endif
+ struct station_parameters *params)
+{
+ int ret = 0;
+
+ ENTER();
+
+ /**do nothing*/
+
+ LEAVE();
+ return ret;
+}
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+#ifdef UAP_SUPPORT
+/**
+ * @brief add station
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param mac A pointer to peer mac
+ * @param params station parameters
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_add_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *mac,
+#else
+ u8 *mac,
+#endif
+ struct station_parameters *params)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ int ret = 0;
+
+ ENTER();
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+#ifdef UAP_SUPPORT
+ if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME) &&
+ (priv->bss_role == MLAN_BSS_ROLE_UAP)) {
+ ret = woal_cfg80211_uap_add_station(wiphy, dev, (u8 *)mac,
+ params);
+ LEAVE();
+ return ret;
+ }
+#endif
+#endif
+ LEAVE();
+ return ret;
+}
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+/**
+ * @brief Update ft ie for Fast BSS Transition
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param ftie A pointer to cfg80211_update_ft_ies_params structure
+ *
+ * @return 0 success , other failure
+ */
+int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ IEEEtypes_MobilityDomain_t *md_ie = NULL;
+ int ret = 0;
+ mlan_ds_misc_assoc_rsp assoc_rsp;
+ IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
+ mlan_bss_info bss_info;
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ struct cfg80211_roam_info roam_info = {};
+#endif
+
+ ENTER();
+
+ if (!ftie) {
+ LEAVE();
+ return ret;
+ }
+#ifdef MLAN_64BIT
+ PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %lx \n", ftie->ie_len);
+#else
+ PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %x \n", ftie->ie_len);
+#endif
+ md_ie = (IEEEtypes_MobilityDomain_t *)woal_parse_ie_tlv(
+ ftie->ie, ftie->ie_len, MOBILITY_DOMAIN);
+ if (!md_ie) {
+ PRINTM(MERROR, "No Mobility domain IE\n");
+ LEAVE();
+ return ret;
+ }
+ priv->ft_cap = md_ie->ft_cap;
+ if (priv->ft_ie_len) {
+ priv->pre_ft_ie_len = priv->ft_ie_len;
+ moal_memcpy_ext(priv->phandle, priv->pre_ft_ie, priv->ft_ie,
+ priv->ft_ie_len, MAX_IE_SIZE);
+ }
+ memset(priv->ft_ie, 0, MAX_IE_SIZE);
+ moal_memcpy_ext(priv->phandle, priv->ft_ie, ftie->ie,
+ MIN(ftie->ie_len, MAX_IE_SIZE), sizeof(priv->ft_ie));
+ priv->ft_ie_len = ftie->ie_len;
+ priv->ft_md = ftie->md;
+
+ if (!priv->ft_pre_connect) {
+ LEAVE();
+ return ret;
+ }
+ /* check if is different AP */
+ if (!memcmp(&priv->target_ap_bssid, priv->cfg_bssid,
+ MLAN_MAC_ADDR_LENGTH)) {
+ PRINTM(MMSG, "This is the same AP, no Fast bss transition\n");
+ priv->ft_pre_connect = MFALSE;
+ priv->ft_ie_len = 0;
+ LEAVE();
+ return 0;
+ }
+
+ /* start fast BSS transition to target AP */
+ priv->assoc_status = 0;
+ priv->sme_current.bssid = priv->conn_bssid;
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
+ &priv->target_ap_bssid, MLAN_MAC_ADDR_LENGTH,
+ sizeof(priv->conn_bssid));
+ memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp));
+ ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current,
+ MOAL_IOCTL_WAIT, &assoc_rsp);
+
+ if ((priv->ft_cap & MBIT(0)) || priv->ft_roaming_triggered_by_driver) {
+ if (!ret) {
+ woal_inform_bss_from_scan_result(priv, NULL,
+ MOAL_IOCTL_WAIT);
+ passoc_rsp = (IEEEtypes_AssocRsp_t *)
+ assoc_rsp.assoc_resp_buf;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ roam_info.bssid = priv->cfg_bssid;
+ roam_info.req_ie = priv->sme_current.ie;
+ roam_info.req_ie_len = priv->sme_current.ie_len;
+ roam_info.resp_ie = passoc_rsp->ie_buffer;
+ roam_info.resp_ie_len = assoc_rsp.assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE;
+ cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL);
+#else
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+ cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid,
+ priv->sme_current.ie,
+ priv->sme_current.ie_len,
+ passoc_rsp->ie_buffer,
+ assoc_rsp.assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE,
+ GFP_KERNEL);
+#else
+ cfg80211_roamed(priv->netdev, priv->cfg_bssid,
+ priv->sme_current.ie,
+ priv->sme_current.ie_len,
+ passoc_rsp->ie_buffer,
+ assoc_rsp.assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE,
+ GFP_KERNEL);
+#endif
+#endif
+ PRINTM(MMSG,
+ "Fast BSS transition to bssid " MACSTR
+ " successfully\n",
+ MAC2STR(priv->cfg_bssid));
+ } else {
+ PRINTM(MMSG,
+ "Fast BSS transition failed, keep connect to " MACSTR
+ " \n",
+ MAC2STR(priv->cfg_bssid));
+ moal_memcpy_ext(priv->phandle,
+ (void *)priv->sme_current.bssid,
+ &priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH,
+ sizeof(priv->conn_bssid));
+ priv->ft_ie_len = priv->pre_ft_ie_len;
+ moal_memcpy_ext(priv->phandle, priv->ft_ie,
+ priv->pre_ft_ie, priv->pre_ft_ie_len,
+ MAX_IE_SIZE);
+ }
+ priv->ft_roaming_triggered_by_driver = MFALSE;
+
+ } else {
+ if (!ret) {
+ memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp));
+ woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT);
+ passoc_rsp = (IEEEtypes_AssocRsp_t *)
+ assoc_rsp.assoc_resp_buf;
+ cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
+ NULL, 0, passoc_rsp->ie_buffer,
+ assoc_rsp.assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE,
+ WLAN_STATUS_SUCCESS,
+ GFP_KERNEL);
+ PRINTM(MMSG,
+ "wlan: Fast Bss transition to bssid " MACSTR
+ " successfully\n",
+ MAC2STR(priv->cfg_bssid));
+
+ memset(&bss_info, 0, sizeof(bss_info));
+ woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ priv->channel = bss_info.bss_chan;
+ } else {
+ PRINTM(MMSG,
+ "wlan: Failed to connect to bssid " MACSTR "\n",
+ MAC2STR(priv->target_ap_bssid));
+ cfg80211_connect_result(priv->netdev,
+ priv->target_ap_bssid, NULL, 0,
+ NULL, 0,
+ woal_get_assoc_status(priv),
+ GFP_KERNEL);
+ moal_memcpy_ext(priv->phandle,
+ (void *)priv->sme_current.bssid,
+ &priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH,
+ sizeof(priv->conn_bssid));
+ memset(priv->target_ap_bssid, 0, ETH_ALEN);
+ priv->ft_ie_len = priv->pre_ft_ie_len;
+ moal_memcpy_ext(priv->phandle, priv->ft_ie,
+ priv->pre_ft_ie, priv->pre_ft_ie_len,
+ MAX_IE_SIZE);
+ // priv->ft_ie_len = 0;
+ }
+ }
+
+ priv->ft_pre_connect = MFALSE;
+ LEAVE();
+ return 0;
+}
+#endif
+
+/**
+ * @brief Save connect parameters for roaming
+ *
+ * @param priv A pointer to moal_private
+ * @param sme A pointer to cfg80211_connect_params structure
+ */
+void woal_save_conn_params(moal_private *priv,
+ struct cfg80211_connect_params *sme)
+{
+ ENTER();
+ woal_clear_conn_params(priv);
+ moal_memcpy_ext(priv->phandle, &priv->sme_current, sme,
+ sizeof(struct cfg80211_connect_params),
+ sizeof(priv->sme_current));
+ if (sme->channel) {
+ priv->sme_current.channel = &priv->conn_chan;
+ moal_memcpy_ext(priv->phandle, priv->sme_current.channel,
+ sme->channel, sizeof(struct ieee80211_channel),
+ sizeof(priv->conn_chan));
+ }
+ if (sme->bssid) {
+ priv->sme_current.bssid = priv->conn_bssid;
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
+ sme->bssid, MLAN_MAC_ADDR_LENGTH,
+ sizeof(priv->conn_bssid));
+ }
+ if (sme->ssid && sme->ssid_len) {
+ priv->sme_current.ssid = priv->conn_ssid;
+ memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH);
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ssid,
+ sme->ssid, sme->ssid_len,
+ sizeof(priv->conn_ssid));
+ }
+ if (sme->ie && sme->ie_len) {
+ priv->sme_current.ie = kzalloc(sme->ie_len, GFP_KERNEL);
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ie,
+ sme->ie, sme->ie_len, sme->ie_len);
+ }
+ if (sme->key && sme->key_len && (sme->key_len <= MAX_WEP_KEY_SIZE)) {
+ priv->sme_current.key = priv->conn_wep_key;
+ moal_memcpy_ext(priv->phandle, (t_u8 *)priv->sme_current.key,
+ sme->key, sme->key_len,
+ sizeof(priv->conn_wep_key));
+ }
+}
+
+/**
+ * @brief clear connect parameters for ing
+ *
+ * @param priv A pointer to moal_private
+ */
+void woal_clear_conn_params(moal_private *priv)
+{
+ ENTER();
+ if (priv->sme_current.ie_len)
+ kfree(priv->sme_current.ie);
+ memset(&priv->sme_current, 0, sizeof(struct cfg80211_connect_params));
+ priv->roaming_required = MFALSE;
+ LEAVE();
+}
+
+/**
+ * @brief Build new roaming connect ie for okc
+ *
+ * @param priv A pointer to moal_private
+ * @param entry A pointer to pmksa_entry
+ **/
+int woal_update_okc_roaming_ie(moal_private *priv, struct pmksa_entry *entry)
+{
+ struct cfg80211_connect_params *sme = &priv->sme_current;
+ int ret = MLAN_STATUS_SUCCESS;
+ const t_u8 *sme_pos, *sme_ptr;
+ t_u8 *okc_ie_pos;
+ t_u8 id, ie_len;
+ int left_len;
+
+ ENTER();
+
+ if (!sme->ie || !sme->ie_len) {
+ PRINTM(MERROR, "No connect ie saved in driver\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ if (!entry) {
+ PRINTM(MERROR, "No roaming ap pmkid\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ if (!priv->okc_roaming_ie) {
+ int okc_ie_len = sme->ie_len + sizeof(t_u16) + PMKID_LEN;
+
+ /** Alloc new buffer for okc roaming ie */
+ priv->okc_roaming_ie = kzalloc(okc_ie_len, GFP_KERNEL);
+ if (!priv->okc_roaming_ie) {
+ PRINTM(MERROR, "Fail to allocate assoc req ie\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ }
+
+ /* Build OKC RSN IE with PMKID list
+ * Format of RSN IE: length(bytes) and container
+ * | 1| 1 | 2 | 4 | 2 |
+ * |id|len|version|group data cipher suite|pairwise cipher suite count|
+ * | 4 * m | 2 | 4 * n | 2 |
+ * |pairwise cipher suite list|AKM suite count|AKM suite list|RSN Cap |
+ * | 2 | 16 * s | 4 |
+ * |PMKIDCount|PMKID List|Group Management Cipher Suite|
+ */
+#define PAIRWISE_CIPHER_COUNT_OFFSET 8
+#define AKM_SUITE_COUNT_OFFSET(n) (10 + (n)*4)
+#define PMKID_COUNT_OFFSET(n) (14 + (n)*4)
+
+ sme_pos = sme->ie;
+ left_len = sme->ie_len;
+ okc_ie_pos = priv->okc_roaming_ie;
+ priv->okc_ie_len = 0;
+
+ while (left_len >= 2) {
+ id = *sme_pos;
+ ie_len = *(sme_pos + 1);
+ if ((ie_len + 2) > left_len) {
+ PRINTM(MERROR, "Invalid ie len %d\n", ie_len);
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+
+ if (id == RSN_IE) {
+ t_u16 pairwise_count, akm_count;
+ t_u8 *rsn_ie_len;
+ int rsn_offset;
+
+ pairwise_count =
+ *(t_u16 *)(sme_pos +
+ PAIRWISE_CIPHER_COUNT_OFFSET);
+ akm_count =
+ *(t_u16 *)(sme_pos + AKM_SUITE_COUNT_OFFSET(
+ pairwise_count));
+ rsn_offset =
+ PMKID_COUNT_OFFSET(pairwise_count + akm_count);
+ sme_ptr = (t_u8 *)(sme_pos + rsn_offset);
+
+ moal_memcpy_ext(priv->phandle, okc_ie_pos, sme_pos,
+ rsn_offset, rsn_offset);
+ rsn_ie_len = okc_ie_pos + 1;
+ okc_ie_pos += rsn_offset;
+ *(t_u16 *)okc_ie_pos = 1;
+ okc_ie_pos += sizeof(t_u16);
+ moal_memcpy_ext(priv->phandle, okc_ie_pos, entry->pmkid,
+ PMKID_LEN, PMKID_LEN);
+ okc_ie_pos += PMKID_LEN;
+ priv->okc_ie_len +=
+ rsn_offset + sizeof(t_u16) + PMKID_LEN;
+ *rsn_ie_len =
+ rsn_offset - 2 + sizeof(t_u16) + PMKID_LEN;
+
+ if ((ie_len + 2) > rsn_offset) {
+ /** Previous conn ie include pmkid list */
+ u16 pmkid_count = *(t_u16 *)sme_ptr;
+ rsn_offset += (sizeof(t_u16) +
+ PMKID_LEN * pmkid_count);
+ if ((ie_len + 2) > rsn_offset) {
+ sme_ptr += (sizeof(t_u16) +
+ PMKID_LEN * pmkid_count);
+ moal_memcpy_ext(
+ priv->phandle, okc_ie_pos,
+ sme_ptr,
+ (ie_len + 2 - rsn_offset),
+ (ie_len + 2 - rsn_offset));
+ okc_ie_pos += (ie_len + 2 - rsn_offset);
+ priv->okc_ie_len +=
+ (ie_len + 2 - rsn_offset);
+ *rsn_ie_len +=
+ (ie_len + 2 - rsn_offset);
+ }
+ }
+ } else {
+ moal_memcpy_ext(priv->phandle, okc_ie_pos, sme_pos,
+ ie_len + 2, ie_len + 2);
+ okc_ie_pos += ie_len + 2;
+ priv->okc_ie_len += ie_len + 2;
+ }
+
+ sme_pos += (ie_len + 2);
+ left_len -= (ie_len + 2);
+ }
+
+done:
+ if (ret != MLAN_STATUS_SUCCESS) {
+ if (priv->okc_roaming_ie) {
+ kfree(priv->okc_roaming_ie);
+ priv->okc_roaming_ie = NULL;
+ priv->okc_ie_len = 0;
+ }
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Start roaming: driver handle roaming
+ *
+ * @param priv A pointer to moal_private structure
+ *
+ * @return N/A
+ */
+void woal_start_roaming(moal_private *priv)
+{
+ mlan_ds_get_signal signal;
+ mlan_ssid_bssid ssid_bssid;
+ char rssi_low[10];
+ int ret = 0;
+ mlan_ds_misc_assoc_rsp *assoc_rsp;
+ IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ struct cfg80211_roam_info roam_info = {};
+#endif
+
+ ENTER();
+ if (priv->ft_roaming_triggered_by_driver) {
+ PRINTM(MIOCTL, "FT roaming is in processing ...... \n");
+ LEAVE();
+ return;
+ }
+
+ if (priv->last_event & EVENT_BG_SCAN_REPORT) {
+ woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT);
+ PRINTM(MIOCTL, "Report bgscan result\n");
+ }
+ if (priv->media_connected == MFALSE || !priv->sme_current.ssid_len) {
+ PRINTM(MIOCTL, "Not connected, ignore roaming\n");
+ LEAVE();
+ return;
+ }
+
+ /* Get signal information from the firmware */
+ memset(&signal, 0, sizeof(mlan_ds_get_signal));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
+ PRINTM(MERROR, "Error getting signal information\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid));
+ ssid_bssid.ssid.ssid_len = priv->sme_current.ssid_len;
+ moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid,
+ priv->sme_current.ssid, priv->sme_current.ssid_len,
+ sizeof(ssid_bssid.ssid.ssid));
+ if (MLAN_STATUS_SUCCESS !=
+ woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) {
+ PRINTM(MIOCTL, "Can not find better network\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ /* check if we found different AP */
+ if (!memcmp(&ssid_bssid.bssid, priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH)) {
+ PRINTM(MIOCTL, "This is the same AP, no roaming\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ PRINTM(MIOCTL, "Find AP: bssid=" MACSTR ", signal=%d\n",
+ MAC2STR(ssid_bssid.bssid), ssid_bssid.rssi);
+ /* check signal */
+ if (!(priv->last_event & EVENT_PRE_BCN_LOST)) {
+ if ((abs(signal.bcn_rssi_avg) - abs(ssid_bssid.rssi)) <
+ DELTA_RSSI) {
+ PRINTM(MERROR, "New AP's signal is not good too.\n");
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ /**check if need start FT Roaming*/
+ if (priv->ft_ie_len && (priv->ft_md == ssid_bssid.ft_md) &&
+ (priv->ft_cap == ssid_bssid.ft_cap)) {
+ priv->ft_roaming_triggered_by_driver = MTRUE;
+ woal_start_ft_roaming(priv, &ssid_bssid);
+ goto done;
+ }
+ /* start roaming to new AP */
+ priv->sme_current.bssid = priv->conn_bssid;
+ moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
+ &ssid_bssid.bssid, MLAN_MAC_ADDR_LENGTH,
+ sizeof(priv->conn_bssid));
+
+#ifdef STA_CFG80211
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) {
+ /** Check if current roaming support OKC offload roaming */
+ if (priv->sme_current.crypto.n_akm_suites &&
+ priv->sme_current.crypto.akm_suites[0] ==
+ WLAN_AKM_SUITE_8021X) {
+ struct pmksa_entry *entry = NULL;
+
+ /** Get OKC PMK Cache entry
+ * Firstly try to get pmksa from cfg80211
+ */
+ priv->wait_target_ap_pmkid = MTRUE;
+ cfg80211_pmksa_candidate_notify(priv->netdev, 0,
+ priv->sme_current.bssid,
+ MTRUE, GFP_ATOMIC);
+ if (wait_event_interruptible_timeout(
+ priv->okc_wait_q,
+ !priv->wait_target_ap_pmkid,
+ OKC_WAIT_TARGET_PMKSA_TIMEOUT)) {
+ PRINTM(MIOCTL, "OKC Roaming is ready\n");
+ entry = priv->target_ap_pmksa;
+ } else {
+ /** Try to get pmksa from pmksa list */
+ priv->wait_target_ap_pmkid = MFALSE;
+ entry = woal_get_pmksa_entry(
+ priv, priv->sme_current.bssid);
+ }
+ /** Build okc roaming ie */
+ woal_update_okc_roaming_ie(priv, entry);
+ priv->target_ap_pmksa = NULL;
+ }
+ }
+#endif
+#endif
+ assoc_rsp = kzalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_ATOMIC);
+ if (!assoc_rsp) {
+ PRINTM(MERROR, "Failed to allocate memory for assoc_rsp\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+ ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current,
+ MOAL_IOCTL_WAIT, assoc_rsp);
+ if (!ret) {
+ const t_u8 *ie;
+ int ie_len;
+
+ woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT);
+ passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp->assoc_resp_buf;
+
+ /** Update connect ie in roam event */
+ ie = priv->sme_current.ie;
+ ie_len = priv->sme_current.ie_len;
+#ifdef STA_CFG80211
+ if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) {
+ /** Check if current roaming support OKC offload roaming
+ */
+ if (priv->sme_current.crypto.n_akm_suites &&
+ priv->sme_current.crypto.akm_suites[0] ==
+ WLAN_AKM_SUITE_8021X) {
+ if (priv->okc_roaming_ie && priv->okc_ie_len) {
+ ie = priv->okc_roaming_ie;
+ ie_len = priv->okc_ie_len;
+ }
+ }
+ }
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ roam_info.bssid = priv->cfg_bssid;
+ roam_info.req_ie = ie;
+ roam_info.req_ie_len = ie_len;
+ roam_info.resp_ie = passoc_rsp->ie_buffer;
+ roam_info.resp_ie_len =
+ assoc_rsp->assoc_resp_len - ASSOC_RESP_FIXED_SIZE;
+ cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL);
+#else
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+ cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, ie, ie_len,
+ passoc_rsp->ie_buffer,
+ assoc_rsp->assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE,
+ GFP_KERNEL);
+#else
+ cfg80211_roamed(priv->netdev, priv->cfg_bssid, ie, ie_len,
+ passoc_rsp->ie_buffer,
+ assoc_rsp->assoc_resp_len -
+ ASSOC_RESP_FIXED_SIZE,
+ GFP_KERNEL);
+#endif
+#endif
+ PRINTM(MMSG, "Roamed to bssid " MACSTR " successfully\n",
+ MAC2STR(priv->cfg_bssid));
+ } else {
+ PRINTM(MIOCTL, "Roaming to bssid " MACSTR " failed\n",
+ MAC2STR(ssid_bssid.bssid));
+ }
+ kfree(assoc_rsp);
+done:
+ /* config rssi low threshold again */
+ priv->last_event = 0;
+ priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
+ sprintf(rssi_low, "%d", priv->rssi_low);
+ woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT);
+ LEAVE();
+ return;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+#ifdef UAP_SUPPORT
+/**
+ * @brief add uap station
+ *
+ * @param wiphy A pointer to wiphy structure
+ * @param dev A pointer to net_device structure
+ * @param mac A pointer to peer mac
+ * @param params station parameters
+ *
+ * @return 0 -- success, otherwise fail
+ */
+int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac, struct station_parameters *params)
+{
+ moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
+ mlan_ioctl_req *req = NULL;
+ t_u32 req_len = 0;
+ mlan_ds_bss *bss = NULL;
+ t_u8 *pos;
+ t_u8 qosinfo;
+ MrvlIEtypes_Data_t *tlv;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ MrvlExtIEtypes_Data_t *ext_tlv;
+#endif
+ mlan_status status;
+ int ret = 0;
+
+ ENTER();
+
+ req_len = sizeof(mlan_ds_bss);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ if (params->ext_capab_len)
+ req_len += sizeof(MrvlIEtypesHeader_t) + params->ext_capab_len;
+#endif
+ if (params->supported_rates_len)
+ req_len += sizeof(MrvlIEtypesHeader_t) +
+ params->supported_rates_len;
+ if (params->uapsd_queues || params->max_sp)
+ req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(qosinfo);
+ if (params->ht_capa)
+ req_len += sizeof(MrvlIEtypesHeader_t) +
+ sizeof(struct ieee80211_ht_cap);
+ if (params->vht_capa)
+ req_len += sizeof(MrvlIEtypesHeader_t) +
+ sizeof(struct ieee80211_vht_cap);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (params->opmode_notif_used)
+ req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(u8);
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ if (params->he_capa_len)
+ req_len += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
+#endif
+ req = woal_alloc_mlan_ioctl_req(req_len);
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ bss = (mlan_ds_bss *)req->pbuf;
+ bss->sub_command = MLAN_OID_UAP_ADD_STATION;
+ req->req_id = MLAN_IOCTL_BSS;
+ req->action = MLAN_ACT_SET;
+ bss->param.sta_info.listen_interval = params->listen_interval;
+ bss->param.sta_info.aid = params->aid;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ bss->param.sta_info.cap_info = params->capability;
+#else
+ bss->param.sta_info.cap_info = 0;
+#endif
+ bss->param.sta_info.tlv_len = 0;
+ bss->param.sta_info.sta_flags = params->sta_flags_set;
+ moal_memcpy_ext(priv->phandle, bss->param.sta_info.peer_mac, mac,
+ MLAN_MAC_ADDR_LENGTH,
+ sizeof(bss->param.sta_info.peer_mac));
+ PRINTM(MMSG, "wlan: UAP/GO add peer station, address =" MACSTR "\n",
+ MAC2STR(mac));
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ PRINTM(MCMND,
+ "sta_flags=0x%x listen_interval=%d aid=%d cap_info=0x%x\n",
+ params->sta_flags_set, params->listen_interval, params->aid,
+ params->capability);
+#else
+ PRINTM(MCMND, "sta_flags=0x%x listen_interval=%d aid=%d\n",
+ params->sta_flags_set, params->listen_interval, params->aid);
+#endif
+ pos = &bss->param.sta_info.tlv[0];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ if (params->ext_capab_len) {
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ tlv->header.type = EXT_CAPABILITY;
+ tlv->header.len = params->ext_capab_len;
+ moal_memcpy_ext(priv->phandle, tlv->data, params->ext_capab,
+ tlv->header.len, tlv->header.len);
+ pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+#endif
+ if (params->supported_rates_len) {
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ tlv->header.type = SUPPORTED_RATES;
+ tlv->header.len = params->supported_rates_len;
+ moal_memcpy_ext(priv->phandle, tlv->data,
+ params->supported_rates, tlv->header.len,
+ tlv->header.len);
+ pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+ if (params->uapsd_queues || params->max_sp) {
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ tlv->header.type = QOS_INFO;
+ tlv->header.len = sizeof(qosinfo);
+ qosinfo = params->uapsd_queues | (params->max_sp << 5);
+ moal_memcpy_ext(priv->phandle, tlv->data, &qosinfo,
+ tlv->header.len, tlv->header.len);
+ pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+ if (params->ht_capa) {
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ tlv->header.type = HT_CAPABILITY;
+ tlv->header.len = sizeof(struct ieee80211_ht_cap);
+ moal_memcpy_ext(priv->phandle, tlv->data, params->ht_capa,
+ tlv->header.len, tlv->header.len);
+ pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+ if (params->vht_capa) {
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ tlv->header.type = VHT_CAPABILITY;
+ tlv->header.len = sizeof(struct ieee80211_vht_cap);
+ moal_memcpy_ext(priv->phandle, tlv->data, params->vht_capa,
+ tlv->header.len, tlv->header.len);
+ pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (params->opmode_notif_used) {
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ tlv->header.type = OPER_MODE_NTF;
+ tlv->header.len = sizeof(u8);
+ moal_memcpy_ext(priv->phandle, tlv->data, &params->opmode_notif,
+ tlv->header.len, tlv->header.len);
+ pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ if (params->he_capa_len) {
+ ext_tlv = (MrvlExtIEtypes_Data_t *)pos;
+ ext_tlv->header.type = EXTENSION;
+ ext_tlv->header.len = params->he_capa_len + sizeof(u8);
+ ext_tlv->header.ext_id = HE_CAPABILITY;
+ moal_memcpy_ext(priv->phandle, ext_tlv->data,
+ (u8 *)params->he_capa, params->he_capa_len,
+ params->he_capa_len);
+ pos += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
+ bss->param.sta_info.tlv_len +=
+ sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
+ tlv = (MrvlIEtypes_Data_t *)pos;
+ }
+#endif
+ DBG_HEXDUMP(MCMD_D, "sta tlv", &bss->param.sta_info.tlv[0],
+ bss->param.sta_info.tlv_len);
+ /* Send IOCTL request to MLAN */
+ status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
+ if (status != MLAN_STATUS_PENDING)
+ kfree(req);
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function is probe client handle.
+ *
+ * @param wiphy A pointer to wiphy.
+ *
+ * @param dev A pointer to net_device
+ *
+ * @param peer A pointer to peer
+ *
+ * @param cookie A pointer to cookie
+ *
+ * @return 0 -- success, otherwise fail
+ */
+static int woal_cfg80211_probe_client(struct wiphy *wiphy,
+ struct net_device *dev, const u8 *peer,
+ u64 *cookie)
+{
+ return -1;
+}
+#endif
+
+/**
+ * @brief Sends deauth packet to kernel
+ *
+ * @param priv A pointer to moal_private struct
+ * @param sa A pointer to source address
+ * @param reason_code disconnect reason code
+ * @return N/A
+ */
+void woal_host_mlme_disconnect(moal_private *priv, u16 reason_code, u8 *sa)
+{
+ t_u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ t_u8 frame_buf[26];
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf;
+ ENTER();
+
+ mgmt->frame_control = IEEE80211_STYPE_DEAUTH;
+ mgmt->duration = 0;
+ mgmt->seq_ctrl = 0;
+ mgmt->u.deauth.reason_code = reason_code;
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
+ moal_memcpy_ext(priv->phandle, mgmt->da, broadcast_addr,
+ ETH_ALEN, sizeof(mgmt->da));
+ moal_memcpy_ext(priv->phandle, mgmt->sa,
+ priv->sme_current.bssid, ETH_ALEN,
+ sizeof(mgmt->sa));
+ moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->cfg_bssid,
+ ETH_ALEN, sizeof(mgmt->bssid));
+ priv->host_mlme = MFALSE;
+ priv->auth_flag = 0;
+ } else {
+ moal_memcpy_ext(priv->phandle, mgmt->da, priv->current_addr,
+ ETH_ALEN, sizeof(mgmt->da));
+ moal_memcpy_ext(priv->phandle, mgmt->sa, sa, ETH_ALEN,
+ sizeof(mgmt->sa));
+ moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->current_addr,
+ ETH_ALEN, sizeof(mgmt->bssid));
+ PRINTM(MMSG,
+ "wlan: hostmlme notify deauth station " MACSTR "\n",
+ MAC2STR(sa));
+ }
+
+ if (GET_BSS_ROLE(priv) != MLAN_BSS_ROLE_UAP) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ mutex_lock(&priv->wdev->mtx);
+ cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26);
+ mutex_unlock(&priv->wdev->mtx);
+#else
+ cfg80211_send_deauth(priv->netdev, frame_buf, 26);
+#endif
+
+ } else {
+ int freq = ieee80211_channel_to_frequency(
+ priv->channel
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ ,
+ (priv->channel <= 14 ? IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ)
+#endif
+ );
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ cfg80211_rx_mgmt(
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ priv->wdev,
+#else
+ priv->netdev,
+#endif
+ freq, 0, frame_buf, 26
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ ,
+ 0
+#endif
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
+ ,
+ GFP_ATOMIC
+#endif
+ );
+#else
+ cfg80211_rx_mgmt(priv->netdev, freq, frame_buf, 26, GFP_ATOMIC);
+#endif
+ }
+
+ LEAVE();
+ return;
+}
+#endif
+
+/**
+ * @brief Register the device with cfg80211
+ *
+ * @param dev A pointer to net_device structure
+ * @param bss_type BSS type
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status woal_register_sta_cfg80211(struct net_device *dev, t_u8 bss_type)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ moal_private *priv = (moal_private *)netdev_priv(dev);
+ struct wireless_dev *wdev = NULL;
+ int psmode = 0;
+
+ ENTER();
+
+ wdev = (struct wireless_dev *)&priv->w_dev;
+ memset(wdev, 0, sizeof(struct wireless_dev));
+ wdev->wiphy = priv->phandle->wiphy;
+ if (!wdev->wiphy) {
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+ if (bss_type == MLAN_BSS_TYPE_STA) {
+ wdev->iftype = NL80211_IFTYPE_STATION;
+ priv->roaming_enabled = MFALSE;
+ priv->roaming_required = MFALSE;
+ }
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
+ wdev->iftype = NL80211_IFTYPE_STATION;
+#endif
+#endif
+ dev_net_set(dev, wiphy_net(wdev->wiphy));
+ dev->ieee80211_ptr = wdev;
+ SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
+ priv->wdev = wdev;
+ /* Get IEEE power save mode */
+ if (MLAN_STATUS_SUCCESS == woal_set_get_power_mgmt(priv, MLAN_ACT_GET,
+ &psmode, 0,
+ MOAL_IOCTL_WAIT)) {
+ /* Save the IEEE power save mode to wiphy, because after
+ * warmreset wiphy power save should be updated instead
+ * of using the last saved configuration */
+ if (psmode)
+ priv->wdev->ps = MTRUE;
+ else
+ priv->wdev->ps = MFALSE;
+ }
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief Initialize the wiphy
+ *
+ * @param priv A pointer to moal_private structure
+ * @param wait_option Wait option
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status woal_cfg80211_init_wiphy(moal_private *priv, t_u8 wait_option)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ int retry_count, rts_thr, frag_thr;
+ struct wiphy *wiphy = NULL;
+ mlan_ioctl_req *req = NULL;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ mlan_ds_radio_cfg *radio = NULL;
+#endif
+ pmlan_ds_11n_cfg cfg_11n = NULL;
+ t_u32 hw_dev_cap;
+#ifdef UAP_SUPPORT
+ pmlan_uap_bss_param sys_cfg = NULL;
+#endif
+ int mcs_supp = 0;
+
+ ENTER();
+ wiphy = priv->phandle->wiphy;
+ /* Get 11n tx parameters from MLAN */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
+ if (req == NULL) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ cfg_11n = (mlan_ds_11n_cfg *)req->pbuf;
+ cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+ req->action = MLAN_ACT_GET;
+ cfg_11n->param.htcap_cfg.hw_cap_req = MTRUE;
+
+ ret = woal_request_ioctl(priv, req, wait_option);
+ if (ret != MLAN_STATUS_SUCCESS)
+ goto done;
+ hw_dev_cap = cfg_11n->param.htcap_cfg.htcap;
+
+ /* Get supported MCS sets */
+ memset(req->pbuf, 0, sizeof(mlan_ds_11n_cfg));
+ cfg_11n->sub_command = MLAN_OID_11N_CFG_SUPPORTED_MCS_SET;
+ req->req_id = MLAN_IOCTL_11N_CFG;
+ req->action = MLAN_ACT_GET;
+
+ ret = woal_request_ioctl(priv, req, wait_option);
+ if (ret != MLAN_STATUS_SUCCESS)
+ goto done;
+
+ /* Initialize parameters for 2GHz and 5GHz bands */
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ if (IS_CARD9098(priv->phandle->card_type) ||
+ IS_CARD9097(priv->phandle->card_type)) {
+ mcs_supp = priv->phandle->params.antcfg & 0xf;
+ if (mcs_supp != 3 && mcs_supp != 0)
+ cfg_11n->param.supported_mcs_set[1] = 0;
+ cfg_11n->param.supported_mcs_set[4] = 0;
+ }
+ woal_cfg80211_setup_ht_cap(
+ &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, hw_dev_cap,
+ cfg_11n->param.supported_mcs_set);
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ woal_cfg80211_setup_he_cap(priv,
+ wiphy->bands[IEEE80211_BAND_2GHZ]);
+#endif
+ }
+ /* For 2.4G band only card, this shouldn't be set */
+ if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
+ if (IS_CARD9098(priv->phandle->card_type) ||
+ IS_CARD9097(priv->phandle->card_type)) {
+ mcs_supp = (priv->phandle->params.antcfg & 0xf00) >> 8;
+ if (mcs_supp != 3 && mcs_supp != 0)
+ cfg_11n->param.supported_mcs_set[1] = 0;
+ }
+ woal_cfg80211_setup_ht_cap(
+ &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, hw_dev_cap,
+ cfg_11n->param.supported_mcs_set);
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ woal_cfg80211_setup_vht_cap(
+ priv, &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap);
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ woal_cfg80211_setup_he_cap(priv,
+ wiphy->bands[IEEE80211_BAND_5GHZ]);
+#endif
+ }
+ kfree(req);
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ /* Get antenna modes */
+ req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
+ if (req == NULL) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ radio = (mlan_ds_radio_cfg *)req->pbuf;
+ radio->sub_command = MLAN_OID_ANT_CFG;
+ req->req_id = MLAN_IOCTL_RADIO_CFG;
+ req->action = MLAN_ACT_GET;
+
+ ret = woal_request_ioctl(priv, req, wait_option);
+ if (ret != MLAN_STATUS_SUCCESS)
+ goto done;
+
+ /* Set available antennas to wiphy */
+ wiphy->available_antennas_tx = radio->param.ant_cfg.tx_antenna;
+ wiphy->available_antennas_rx = radio->param.ant_cfg.rx_antenna;
+#endif /* CFG80211_VERSION_CODE */
+
+ /* Set retry limit count to wiphy */
+ if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_retry(priv, MLAN_ACT_GET, wait_option,
+ &retry_count)) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ }
+#ifdef UAP_SUPPORT
+ else {
+ sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
+ if (!sys_cfg) {
+ PRINTM(MERROR,
+ "Fail to alloc memory for mlan_uap_bss_param\n");
+ ret = MLAN_STATUS_FAILURE;
+ kfree(sys_cfg);
+ goto done;
+ }
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_sys_config(priv, MLAN_ACT_GET, wait_option,
+ sys_cfg)) {
+ ret = MLAN_STATUS_FAILURE;
+ kfree(sys_cfg);
+ goto done;
+ }
+ retry_count = sys_cfg->retry_limit;
+ kfree(sys_cfg);
+ }
+#endif
+ wiphy->retry_long = (t_u8)retry_count;
+ wiphy->retry_short = (t_u8)retry_count;
+ wiphy->max_scan_ie_len = MAX_IE_SIZE;
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ wiphy->mgmt_stypes = ieee80211_mgmt_stypes;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION;
+#endif /* KERNEL_VERSION */
+
+ /* Set RTS threshold to wiphy */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_rts(priv, MLAN_ACT_GET, wait_option, &rts_thr)) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ if (rts_thr < MLAN_RTS_MIN_VALUE || rts_thr > MLAN_RTS_MAX_VALUE)
+ rts_thr = MLAN_FRAG_RTS_DISABLED;
+ wiphy->rts_threshold = (t_u32)rts_thr;
+
+ /* Set fragment threshold to wiphy */
+ if (MLAN_STATUS_SUCCESS !=
+ woal_set_get_frag(priv, MLAN_ACT_GET, wait_option, &frag_thr)) {
+ ret = MLAN_STATUS_FAILURE;
+ goto done;
+ }
+ if (frag_thr < MLAN_RTS_MIN_VALUE || frag_thr > MLAN_RTS_MAX_VALUE)
+ frag_thr = MLAN_FRAG_RTS_DISABLED;
+ wiphy->frag_threshold = (t_u32)frag_thr;
+
+done:
+ LEAVE();
+ if (ret != MLAN_STATUS_PENDING)
+ kfree(req);
+ return ret;
+}
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+/**
+ * @brief Update channel flag
+ *
+ * @param wiphy A pointer to wiphy structure
+ *
+ * @return N/A
+ */
+void woal_update_channel_flag(struct wiphy *wiphy, mlan_fw_info *fw_info)
+{
+ enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
+ int i;
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ sband = wiphy->bands[band];
+ if (!sband)
+ continue;
+ if (sband->band & IEEE80211_BAND_5GHZ &&
+ fw_info->prohibit_80mhz) {
+ for (i = 0; i < sband->n_channels; i++) {
+ sband->channels[i].flags |=
+ IEEE80211_CHAN_NO_80MHZ;
+ PRINTM(MCMND, "hw_value=%d channel flag %x\n",
+ sband->channels[i].hw_value,
+ sband->channels[i].flags);
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * This function registers the device with CFG802.11 subsystem.
+ *
+ * @param priv A pointer to moal_private
+ *
+ */
+mlan_status woal_register_cfg80211(moal_private *priv)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ struct wiphy *wiphy;
+ void *wdev_priv = NULL;
+ mlan_fw_info fw_info;
+ char *country = NULL, *reg_alpha2 = NULL;
+ int index = 0;
+
+ ENTER();
+
+ memset(&fw_info, 0, sizeof(mlan_fw_info));
+ woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
+ reg_alpha2 = priv->phandle->params.reg_alpha2;
+ wiphy = wiphy_new(&woal_cfg80211_ops, sizeof(moal_handle *));
+ if (!wiphy) {
+ PRINTM(MERROR, "Could not allocate wiphy device\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto err_wiphy;
+ }
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) {
+ woal_cfg80211_ops.auth = woal_cfg80211_authenticate;
+ woal_cfg80211_ops.assoc = woal_cfg80211_associate;
+ woal_cfg80211_ops.disconnect = NULL;
+ woal_cfg80211_ops.connect = NULL;
+#ifdef UAP_SUPPORT
+ woal_cfg80211_ops.probe_client = woal_cfg80211_probe_client;
+#endif
+ }
+#endif
+#ifdef CONFIG_PM
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ if (fw_info.fw_supplicant_support)
+ wiphy->wowlan = &wowlan_support_with_gtk;
+ else
+ wiphy->wowlan = &wowlan_support;
+#else
+ wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT;
+ if (fw_info.fw_supplicant_support) {
+ wiphy->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE;
+ }
+ wiphy->wowlan.n_patterns = MAX_NUM_FILTERS;
+ wiphy->wowlan.pattern_min_len = 1;
+ wiphy->wowlan.pattern_max_len = WOWLAN_MAX_PATTERN_LEN;
+ wiphy->wowlan.max_pkt_offset = WOWLAN_MAX_OFFSET_LEN;
+#endif
+#endif
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ wiphy->coalesce = &coalesce_support;
+#endif
+ wiphy->max_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH;
+ wiphy->max_scan_ie_len = MAX_IE_SIZE;
+ wiphy->interface_modes = 0;
+ wiphy->interface_modes =
+ MBIT(NL80211_IFTYPE_STATION) | MBIT(NL80211_IFTYPE_AP);
+
+#ifdef WIFI_DIRECT_SUPPORT
+#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
+ wiphy->interface_modes |=
+ MBIT(NL80211_IFTYPE_P2P_GO) | MBIT(NL80211_IFTYPE_P2P_CLIENT);
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ woal_register_cfg80211_vendor_command(wiphy);
+#endif
+ /* Make this wiphy known to this driver only */
+ wiphy->privid = mrvl_wiphy_privid;
+
+ if (!fw_info.fw_bands)
+ fw_info.fw_bands = BAND_B | BAND_G;
+ if (fw_info.fw_bands & BAND_A) {
+ if (priv->phandle->second_mac)
+ wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &mac1_cfg80211_band_5ghz;
+ else
+
+ wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ woal_update_channel_flag(wiphy, &fw_info);
+#endif
+ priv->phandle->band = IEEE80211_BAND_5GHZ;
+ }
+ /* Supported bands */
+ if (fw_info.fw_bands & (BAND_B | BAND_G | BAND_GN | BAND_GAC)) {
+ if (priv->phandle->second_mac)
+ wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &mac1_cfg80211_band_2ghz;
+ else
+ wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz;
+ /* If 2.4G enable, it will overwrite default to 2.4G*/
+ priv->phandle->band = IEEE80211_BAND_2GHZ;
+ }
+
+ if (fw_info.fw_bands & BAND_A) {
+ /** reduce scan time from 110ms to 80ms */
+ woal_set_scan_time(priv, INIT_ACTIVE_SCAN_CHAN_TIME,
+ INIT_PASSIVE_SCAN_CHAN_TIME,
+ INIT_SPECIFIC_SCAN_CHAN_TIME);
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ cfg80211_iface_comb_ap_sta.radar_detect_widths |=
+ MBIT(NL80211_CHAN_WIDTH_40);
+ if (fw_info.fw_bands & BAND_AAC)
+ cfg80211_iface_comb_ap_sta.radar_detect_widths |=
+ MBIT(NL80211_CHAN_WIDTH_80);
+#endif
+ } else
+ woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME,
+ PASSIVE_SCAN_CHAN_TIME,
+ SPECIFIC_SCAN_CHAN_TIME);
+
+ /* Initialize cipher suits */
+ wiphy->cipher_suites = cfg80211_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites);
+#ifdef UAP_CFG80211
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ if (!moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
+#endif
+ wiphy->max_acl_mac_addrs = MAX_MAC_FILTER_NUM;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ if (fw_info.max_ap_assoc_sta) {
+ wiphy->max_ap_assoc_sta = fw_info.max_ap_assoc_sta;
+ PRINTM(MCMND, "Set wiphy max_ap_assoc_sta=%d\n",
+ wiphy->max_ap_assoc_sta);
+ }
+#endif
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+ /* Initialize interface combinations */
+ wiphy->iface_combinations = &cfg80211_iface_comb_ap_sta;
+ wiphy->n_iface_combinations = 1;
+#endif
+
+ moal_memcpy_ext(priv->phandle, wiphy->perm_addr, priv->current_addr,
+ ETH_ALEN, sizeof(wiphy->perm_addr));
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wiphy->flags = 0;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ wiphy->flags |= WIPHY_FLAG_NETNS_OK;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+ wiphy->flags |=
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_OFFCHAN_TX;
+ wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
+ wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
+ else
+#endif
+ wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
+#endif
+#ifdef ANDROID_KERNEL
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ if (!moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
+#endif
+ wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+#else
+ wiphy->max_sched_scan_reqs = 1;
+#endif
+ wiphy->max_sched_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH;
+ wiphy->max_sched_scan_ie_len = MAX_IE_SIZE;
+ wiphy->max_match_sets = MRVDRV_MAX_SSID_LIST_LENGTH;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+ wiphy->max_sched_scan_plans = 3;
+ wiphy->max_sched_scan_plan_iterations = 100;
+#endif
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
+ wiphy->features |= NL80211_FEATURE_SAE;
+#endif
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN;
+#endif
+
+ wiphy->reg_notifier = woal_cfg80211_reg_notifier;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ /* Indicate to cfg80211 that the driver can support
+ * CSA and ESCA,i.e., both types of channel switch
+ * Applications like hostapd 2.6 will append CSA IE
+ * and ECSA IE and expect the driver to advertise 2
+ * in max_num_csa_counters to successfully issue a
+ * channel switch
+ */
+ wiphy->max_num_csa_counters = MAX_CSA_COUNTERS_NUM;
+#endif
+ wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
+#endif
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+#endif
+ /* Set struct moal_handle pointer in wiphy_priv */
+ wdev_priv = wiphy_priv(wiphy);
+ *(unsigned long *)wdev_priv = (unsigned long)priv->phandle;
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ set_wiphy_dev(wiphy, (struct device *)priv->phandle->hotplug_device);
+#endif
+ /* Set phy name*/
+ for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
+ if (m_handle[index] == priv->phandle) {
+ dev_set_name(&wiphy->dev, mwiphy_name, index);
+ break;
+ }
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (moal_extflg_isset(priv->phandle, EXT_BEACON_HINTS)) {
+ /* REGULATORY_DISABLE_BEACON_HINTS: NO-IR flag won't be removed
+ * on chn where an AP is visible! */
+ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
+ }
+ if (moal_extflg_isset(priv->phandle, EXT_COUNTRY_IE_IGNORE)) {
+ PRINTM(MIOCTL, "Don't follow countryIE provided by AP.\n");
+ wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
+ } else {
+ PRINTM(MIOCTL, "Follow countryIE provided by AP.\n");
+ }
+#endif
+
+ priv->phandle->country_code[0] = '0';
+ priv->phandle->country_code[1] = '0';
+ priv->phandle->country_code[2] = ' ';
+
+ if (reg_alpha2 && !strncmp(reg_alpha2, "99", strlen("99"))) {
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_DISABLE_BEACON_HINTS |
+ REGULATORY_COUNTRY_IE_IGNORE;
+#else
+ wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+#endif
+ wiphy_apply_custom_regulatory(wiphy, &mrvl_regdom);
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ if (woal_request_extcap(priv, (t_u8 *)&priv->extended_capabilities,
+ sizeof(priv->extended_capabilities)) < 0)
+ PRINTM(MERROR,
+ "Failed to get driver extended capability, use default\n");
+ DBG_HEXDUMP(MCMD_D, "wiphy ext cap",
+ (t_u8 *)&priv->extended_capabilities,
+ sizeof(priv->extended_capabilities));
+ wiphy->extended_capabilities = (t_u8 *)&priv->extended_capabilities;
+ wiphy->extended_capabilities_mask =
+ (t_u8 *)&priv->extended_capabilities;
+ wiphy->extended_capabilities_len = sizeof(priv->extended_capabilities);
+#endif
+ if (wiphy_register(wiphy) < 0) {
+ PRINTM(MERROR, "Wiphy device registration failed!\n");
+ ret = MLAN_STATUS_FAILURE;
+ goto err_wiphy;
+ }
+
+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ if (fw_info.force_reg ||
+ (fw_region && priv->phandle->params.txpwrlimit_cfg)) {
+ PRINTM(MCMND, "FW region_code=%d force_reg=%d\n",
+ fw_info.region_code, fw_info.force_reg);
+ country = region_code_2_string(fw_info.region_code);
+ if (country) {
+ moal_memcpy_ext(priv->phandle,
+ priv->phandle->country_code, country, 2,
+ 2);
+ woal_update_custom_regdomain(priv, wiphy);
+ }
+ }
+#endif
+ if ((!reg_alpha2 || strncmp(reg_alpha2, "99", strlen("99")))
+
+ ) {
+ /** we will try driver parameter first */
+ if (reg_alpha2 && woal_is_valid_alpha2(reg_alpha2)) {
+ PRINTM(MIOCTL, "Notify reg_alpha2 %c%c\n",
+ reg_alpha2[0], reg_alpha2[1]);
+ if (!moal_extflg_isset(priv->phandle,
+ EXT_DISABLE_REGD_BY_DRIVER))
+ regulatory_hint(wiphy, reg_alpha2);
+ } else {
+ country = region_code_2_string(fw_info.region_code);
+ if (country) {
+ if (fw_info.region_code != 0) {
+ PRINTM(MIOCTL,
+ "Notify hw region code=%d %c%c\n",
+ fw_info.region_code, country[0],
+ country[1]);
+ if (!moal_extflg_isset(
+ priv->phandle,
+ EXT_DISABLE_REGD_BY_DRIVER))
+ regulatory_hint(wiphy, country);
+ }
+ } else
+ PRINTM(MCMND,
+ "hw region code=%d not supported\n",
+ fw_info.region_code);
+ }
+ }
+ priv->phandle->wiphy = wiphy;
+ woal_cfg80211_init_wiphy(priv, MOAL_IOCTL_WAIT);
+
+ return ret;
+err_wiphy:
+ if (wiphy)
+ wiphy_free(wiphy);
+ LEAVE();
+ return ret;
+}