/** @file moal_sta_cfg80211.c
 *
 * @brief This file contains the functions for STA CFG80211.
 *
 *
 * Copyright 2011-2022 NXP
 *
 * NXP CONFIDENTIAL
 * The source code contained or described herein and all documents related to
 * the source code (Materials) are owned by NXP, its
 * suppliers and/or its licensors. Title to the Materials remains with NXP,
 * its suppliers and/or its licensors. The Materials contain
 * trade secrets and proprietary and confidential information of NXP, its
 * suppliers and/or its licensors. The Materials are protected by worldwide
 * copyright and trade secret laws and treaty provisions. No part of the
 * Materials may be used, copied, reproduced, modified, published, uploaded,
 * posted, transmitted, distributed, or disclosed in any way without NXP's prior
 * express written permission.
 *
 * No license under any patent, copyright, trade secret or other intellectual
 * property right is granted to or conferred upon you by disclosure or delivery
 * of the Materials, either expressly, by implication, inducement, estoppel or
 * otherwise. Any license under such intellectual property rights must be
 * express and approved by NXP in writing.
 *
 *  Alternatively, this software may be distributed under the terms of GPL v2.
 *  SPDX-License-Identifier:    GPL-2.0
 *
 */

#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 */
static 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_128, 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
};

#ifdef UAP_SUPPORT
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int woal_cfg80211_set_monitor_channel(struct wiphy *wiphy,
					     struct cfg80211_chan_def *chandef);
#endif
#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,
#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13)
				     unsigned int link_id,
#endif
				     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)
void woal_check_auto_tdls(struct wiphy *wiphy, struct net_device *dev);
int woal_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
			    const u8 *peer,
#else
			    u8 *peer,
#endif
			    enum nl80211_tdls_operation oper);
int woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
			    const u8 *peer,
#else
			    u8 *peer,
#endif
			    u8 action_code, u8 dialog_token, u16 status_code,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
			    u32 peer_capability,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
			    bool initiator,
#endif
			    const u8 *extra_ies, size_t extra_ies_len);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
static int woal_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
					     struct net_device *dev,
					     const u8 *addr, u8 oper_class,
					     struct cfg80211_chan_def *chandef);

void woal_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
					      struct net_device *dev,
					      const u8 *addr);
#endif
#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

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
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);
#endif

#ifdef UAP_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
int woal_cfg80211_set_radar_background(struct wiphy *wiphy,
				       struct cfg80211_chan_def *chandef);
#endif
#endif

/** 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,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
	.deauth = woal_cfg80211_deauthenticate,
	.disassoc = woal_cfg80211_disassociate,
#endif
	.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(5, 10, 0)
	.set_default_beacon_key = woal_cfg80211_set_default_beacon_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)
	.tdls_oper = woal_cfg80211_tdls_oper,
	.tdls_mgmt = woal_cfg80211_tdls_mgmt,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
	.tdls_channel_switch = woal_cfg80211_tdls_channel_switch,
	.tdls_cancel_channel_switch = woal_cfg80211_tdls_cancel_channel_switch,
#endif
#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) || CFG80211_VERSION_CODE == KERNEL_VERSION(4, 19, 206)
	.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 UAP_SUPPORT
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
	.set_monitor_channel = woal_cfg80211_set_monitor_channel,
#endif
#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),
	}};

#define AUTH_TX_DEFAULT_WAIT_TIME 2400
/********************************************************
				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) | MBIT(NL80211_IFTYPE_MONITOR)
#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
********************************************************/
#ifdef UAP_SUPPORT
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int woal_cfg80211_set_monitor_channel(struct wiphy *wiphy,
					     struct cfg80211_chan_def *chandef)
{
	moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
	moal_private *priv =
		(moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_STA);
	netmon_band_chan_cfg band_chan_cfg;
	t_u32 bandwidth = 0;
	int ret = -EFAULT;

	ENTER();

	if (!priv) {
		ret = -EFAULT;
		goto done;
	}
	if (handle->mon_if) {
		if (cfg80211_chandef_identical(&handle->mon_if->chandef,
					       chandef)) {
			ret = 0;
			goto done;
		}
		if (woal_is_any_interface_active(handle)) {
			PRINTM(MERROR,
			       "Cannot change monitor channel for an active"
			       " interface\n");
			goto done;
		}
		memset(&band_chan_cfg, 0x00, sizeof(band_chan_cfg));
		/* Set channel */
		band_chan_cfg.channel = ieee80211_frequency_to_channel(
			chandef->chan->center_freq);
		/* Set band */
		if (chandef->chan->band == IEEE80211_BAND_2GHZ)
			band_chan_cfg.band |= (BAND_B | BAND_G);
		if (chandef->chan->band == IEEE80211_BAND_5GHZ)
			band_chan_cfg.band |= BAND_A;
		if (chandef->chan->band == IEEE80211_BAND_2GHZ)
			band_chan_cfg.band |= BAND_GN;
		if (chandef->chan->band == IEEE80211_BAND_5GHZ)
			band_chan_cfg.band |= BAND_AN;
		if (chandef->chan->band == IEEE80211_BAND_2GHZ)
			band_chan_cfg.band |= BAND_GAC;
		if (chandef->chan->band == IEEE80211_BAND_5GHZ)
			band_chan_cfg.band |= BAND_AAC;
		/* Set bandwidth */
		if (chandef->width == NL80211_CHAN_WIDTH_20)
			bandwidth = CHANNEL_BW_20MHZ;
		else if (chandef->width == NL80211_CHAN_WIDTH_40)
			bandwidth = chandef->center_freq1 >
						    chandef->chan->center_freq ?
					    CHANNEL_BW_40MHZ_ABOVE :
					    CHANNEL_BW_40MHZ_BELOW;
		else if (chandef->width == NL80211_CHAN_WIDTH_80)
			bandwidth = CHANNEL_BW_80MHZ;
		band_chan_cfg.chan_bandwidth = bandwidth;

		if (MLAN_STATUS_SUCCESS !=
		    woal_set_net_monitor(priv, MOAL_IOCTL_WAIT, MTRUE,
					 handle->mon_if->flag,
					 &band_chan_cfg)) {
			PRINTM(MERROR, "%s: woal_set_net_monitor fail\n",
			       __func__);
			ret = -EFAULT;
			goto done;
		}

		moal_memcpy_ext(priv->phandle, &handle->mon_if->band_chan_cfg,
				&band_chan_cfg,
				sizeof(handle->mon_if->band_chan_cfg),
				sizeof(handle->mon_if->band_chan_cfg));
		handle->mon_if->chandef = *chandef;

		if (handle->mon_if->chandef.chan)
			PRINTM(MINFO,
			       "set_monitor_channel+++ chan[band=%d center_freq=%d hw_value=%d] width=%d center_freq1=%d center_freq2=%d\n",
			       handle->mon_if->chandef.chan->band,
			       handle->mon_if->chandef.chan->center_freq,
			       handle->mon_if->chandef.chan->hw_value,
			       handle->mon_if->chandef.width,
			       handle->mon_if->chandef.center_freq1,
			       handle->mon_if->chandef.center_freq2);
		PRINTM(MINFO,
		       "set_monitor_channel+++ band=%x channel=%d bandwidth=%d\n",
		       handle->mon_if->band_chan_cfg.band,
		       handle->mon_if->band_chan_cfg.channel,
		       handle->mon_if->band_chan_cfg.chan_bandwidth);
		ret = 0;
	}

done:
	LEAVE();
	return ret;
}
#endif
#endif

/**
 *  @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,
				       size_t 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");
				if (woal_wps_cfg(priv, MTRUE)) {
					PRINTM(MERROR,
					       "%s: Enable WPS session failed\n",
					       __func__);
					ret = -EFAULT;
					goto done;
				}
			}

			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;
	}

	PRINTM(MCMD_D, "Send domain info: country=%c%c band=%d dfs_region=%d\n",
	       priv->phandle->country_code[0], priv->phandle->country_code[1],
	       band, priv->phandle->dfs_region);
	/* 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;
	cfg_11d->param.domain_info.dfs_region = priv->phandle->dfs_region;
	if (is_cfg80211_special_region_code(priv->phandle->country_code)) {
		/* Set country code */
		cfg_11d->param.domain_info.country_code[0] = 'W';
		cfg_11d->param.domain_info.country_code[1] = 'W';
	} else {
		/* 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
 */
static mlan_status woal_set_ewpa_mode(moal_private *priv, t_u8 wait_option,
				      mlan_ssid_bssid *ssid_bssid)
{
	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)
		goto error;

	/* Allocate an IOCTL request buffer */
	req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
	if (req == NULL) {
		status = MLAN_STATUS_FAILURE;
		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, t_u8 group_encrypt)
{
	int ret = 0;

	ENTER();

	if (MLAN_STATUS_SUCCESS !=
	    woal_set_encrypt_mode(priv, wait_option, encrypt_mode, group_encrypt))
		ret = -EFAULT;

	if (wpa_enabled) {
		if (MLAN_STATUS_SUCCESS !=
		    woal_set_wpa_enable(priv, wait_option, 1))
			ret = -EFAULT;
	}

	LEAVE();
	return ret;
}

/**
 * @brief Reset the wifi
 *
 * @param handle        A pointer to moal_handle structure
 * @param cnt           wifi reset count
 * @param reason        wifi reset reason
 *
 * @return              MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING
 */
mlan_status woal_reset_wifi(moal_handle *handle, t_u8 cnt, char *reason)
{
	static wifi_timeval reset_time;
	wifi_timeval ts;
	t_u64 diff;

#define MAX_WIFI_RESET_INTERVAL 15 * 60 * 1000000 // 15 minute
	woal_get_monotonic_time(&ts);
	diff = (t_u64)(timeval_to_usec(ts) - timeval_to_usec(reset_time));
	PRINTM(MERROR, "WiFi Reset diff %lld\n", diff);
	if (reset_time.time_sec == 0 || diff >= MAX_WIFI_RESET_INTERVAL) {
		reset_time = ts;
		PRINTM(MERROR, "WiFi Reset due to %s cnt %d\n", reason, cnt);
		/* Do wifi independent reset */
		woal_process_hang(handle);
		return MLAN_STATUS_SUCCESS;
	}
	return MLAN_STATUS_PENDING;
}

/**
 * @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 < (int)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();
	rcu_read_lock();
	country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
	if (!country_ie) {
		rcu_read_unlock();
		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) {
		rcu_read_unlock();
		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)) {
		rcu_read_unlock();
		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) {
		rcu_read_unlock();
		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;

	cfg_11d->param.domain_info.dfs_region = NXP_DFS_UNKNOWN;
	/* 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);
	rcu_read_unlock();

	/* 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
 */
static 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 */
	scan_req = (wlan_user_scan_cfg *)kmalloc(sizeof(wlan_user_scan_cfg),
						 GFP_KERNEL);
	if (!scan_req) {
		PRINTM(MERROR, "Malloc buffer failed\n");
		LEAVE();
		return -ENOMEM;
	}

	priv->report_scan_result = MTRUE;
	memset(scan_req, 0x00, sizeof(wlan_user_scan_cfg));
	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 =
			woal_ieee_band_to_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 =
					woal_ieee_band_to_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);
	kfree(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
 */
static void woal_save_assoc_params(moal_private *priv,
				   struct cfg80211_assoc_request *req,
				   mlan_ssid_bssid *ssid_bssid)
{
	ENTER();

	priv->assoc_bss = req->bss;
	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_ATOMIC);
		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;
		priv->sme_current.ssid_len = ssid_bssid->ssid.ssid_len;
		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;
	}
	if (priv->sinfo)
		memset(priv->sinfo, 0, sizeof(struct station_info));
	else
		priv->sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
	LEAVE();
}

/**
 * @brief Save auth parameters for roaming
 *
 * @param priv            A pointer to moal_private
 * @param req             A pointer to struct cfg80211_auth_request
 */
static void woal_save_auth_params(moal_private *priv,
				  struct cfg80211_auth_request *req)
{
	ENTER();
	woal_clear_conn_params(priv);
	priv->assoc_bss = req->bss;
	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 Request scan based on auth_request parameter
 *
 * @param priv            A pointer to moal_private structure
 * @param req             A pointer to cfg80211_auth_request
 * @param wait_option     wait option
 *
 * @return                0 -- success, otherwise fail
 */
static int woal_cfg80211_auth_scan(moal_private *priv,
				   struct cfg80211_auth_request *req,
				   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;
	const u8 *ssid;

	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 */
	scan_req = (wlan_user_scan_cfg *)kmalloc(sizeof(wlan_user_scan_cfg),
						 GFP_KERNEL);
	if (!scan_req) {
		PRINTM(MERROR, "Malloc buffer failed\n");
		LEAVE();
		return -ENOMEM;
	}

	priv->report_scan_result = MTRUE;
	memset(scan_req, 0x00, sizeof(wlan_user_scan_cfg));
	rcu_read_lock();
	ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
	if (ssid) {
		moal_memcpy_ext(priv->phandle, scan_req->ssid_list[0].ssid,
				ssid + 2, ssid[1],
				sizeof(scan_req->ssid_list[0].ssid));
		scan_req->ssid_list[0].max_len = 0;
	}
	rcu_read_unlock();
	moal_memcpy_ext(priv->phandle, scan_req->specific_bssid,
			req->bss->bssid, ETH_ALEN, ETH_ALEN);
	if (req->bss->channel) {
		scan_req->chan_list[0].chan_number =
			req->bss->channel->hw_value;
		scan_req->chan_list[0].radio_type =
			woal_ieee_band_to_radio_type(req->bss->channel->band);
		if (req->bss->channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
			scan_req->chan_list[0].scan_type =
				MLAN_SCAN_TYPE_PASSIVE;
		else if (req->bss->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 =
					woal_ieee_band_to_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);
	kfree(scan_req);
#ifdef REASSOCIATION
	MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
#endif
	LEAVE();
	return ret;
}

/**
 *  @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_u16 pkt_len;
	t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	int ret = 0;

	t_u8 trans = 1, status_code = 0;
	t_u8 *varptr = NULL;
	moal_handle *handle = priv->phandle;
	mlan_ssid_bssid *ssid_bssid;
	int i;

	ENTER();

#ifdef REASSOCIATION
	// disable reassoc_on
	handle->reassoc_on &= ~MBIT(priv->bss_index);
	priv->reassoc_on = MFALSE;
	priv->reassoc_required = MFALSE;
	if (!handle->reassoc_on && handle->is_reassoc_timer_set == MTRUE) {
		woal_cancel_timer(&handle->reassoc_timer);
		handle->is_reassoc_timer_set = MFALSE;
	}
#endif

	priv->cfg_disconnect = MFALSE;
#ifdef UAP_CFG80211
	if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
		PRINTM(MERROR, "ERR: Role is AP\n");
		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;
	}

	/** cancel pending scan */
	woal_cancel_scan(priv, MOAL_IOCTL_WAIT);

	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)) {
		woal_cfg80211_auth_scan(priv, req, MOAL_IOCTL_WAIT);
		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;
	}
#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 */
		PRINTM(MERROR, "Fail to clear previous keys\n");
		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)) {
		PRINTM(MERROR, "Fail to set auth mode\n");
		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)) {
			PRINTM(MERROR, "Fail to set wep key idx %d\n",
			       req->key_idx);
			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)) {
			PRINTM(MERROR, "Fail to enable wep key idx %d\n",
			       req->key_idx);
			ret = -EFAULT;
			goto done;
		}
	}

	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 =
		woal_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*/
	pkt_len = woal_cpu_to_le16(packet_len);
	moal_memcpy_ext(priv->phandle,
			pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
			&pkt_len, sizeof(pkt_len), sizeof(pkt_len));

	/**Authentication Frame: Authentication Alg*/
	mgmt->u.auth.auth_alg = woal_cpu_to_le16(auth_alg);
	mgmt->u.auth.auth_transaction = woal_cpu_to_le16(trans);
	/**Authentication Frame: Status code*/
	mgmt->u.auth.status_code = woal_cpu_to_le16(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 = woal_cpu_to_le16(auth_alg);

	PRINTM(MMSG, "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);
	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);
#define MAX_AUTH_COUNTER 5
		priv->auth_tx_cnt++;
		if (priv->auth_tx_cnt >= MAX_AUTH_COUNTER) {
			if (woal_reset_wifi(priv->phandle, priv->auth_tx_cnt,
					    "auth timeout") ==
			    MLAN_STATUS_SUCCESS) {
				priv->auth_tx_cnt = 0;
			}
		}
		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;
		PRINTM(MERROR, "Fail to send packet status=%d\n", status);
		break;
	}
done:
	if (ret) {
		woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
		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,
				       "Fail to cancel remain on channel\n");
			}
			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) {
				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");
				}
				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_info	pointer to mlan_ds_assoc_info
 *
 *  @return        N/A
 */
void woal_host_mlme_process_assoc_resp(moal_private *priv,
				       mlan_ds_assoc_info *assoc_info)
{
	struct cfg80211_bss *bss = NULL;
	unsigned long flags;
	u8 *assoc_req_buf = NULL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	struct cfg80211_rx_assoc_resp resp = {
		.uapsd_queues = -1,
	};
#endif

	if (priv) {
		if (priv->auth_flag & HOST_MLME_ASSOC_DONE) {
			priv->auth_flag = 0;
			bss = priv->assoc_bss;
			if (!bss) {
				PRINTM(MERROR,
				       "HostMlme %s:assoc_bss is null\n",
				       priv->netdev->name);
				return;
			}

			if (assoc_info->assoc_resp_len) {
				PRINTM(MCMND,
				       "HostMlme: %s assoc_resp_len=%d, frame_control=0x%x\n",
				       priv->netdev->name,
				       assoc_info->assoc_resp_len,
				       ((struct ieee80211_mgmt *)
						assoc_info->assoc_resp_buf)
					       ->frame_control);
				if (ieee80211_is_assoc_resp(
					    ((struct ieee80211_mgmt *)
						     assoc_info->assoc_resp_buf)
						    ->frame_control) ||
				    ieee80211_is_reassoc_resp(
					    ((struct ieee80211_mgmt *)
						     assoc_info->assoc_resp_buf)
						    ->frame_control)) {
					spin_lock_irqsave(&priv->connect_lock,
							  flags);
					if (le16_to_cpu(
						    ((struct ieee80211_mgmt
							      *)assoc_info
							     ->assoc_resp_buf)
							    ->u.assoc_resp
							    .status_code) !=
					    WLAN_STATUS_SUCCESS) {
						memset(priv->cfg_bssid, 0,
						       ETH_ALEN);
						woal_clear_conn_params(priv);
					} else {
						priv->cfg_disconnect = MFALSE;
					}
					spin_unlock_irqrestore(
						&priv->connect_lock, flags);
					/*Populate Assoc req buf only if len is
					 * non zero . i.e. we received assoc req
					 * buffer from fw.*/
					if (assoc_info->assoc_req_len)
						assoc_req_buf =
							assoc_info
								->assoc_req_buf;

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
					resp.links[0].bss = bss;
					resp.buf = assoc_info->assoc_resp_buf;
					resp.len = assoc_info->assoc_resp_len;
					resp.req_ies = assoc_req_buf;
					resp.req_ies_len =
						assoc_info->assoc_req_len;
					mutex_lock(&priv->wdev->mtx);
					cfg80211_rx_assoc_resp(priv->netdev,
							       &resp);
					mutex_unlock(&priv->wdev->mtx);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)|| LINUX_VERSION_CODE  == KERNEL_VERSION(4, 19, 206)
					mutex_lock(&priv->wdev->mtx);
					cfg80211_rx_assoc_resp(
						priv->netdev, bss,
						assoc_info->assoc_resp_buf,
						assoc_info->assoc_resp_len, -1,
						assoc_req_buf,
						assoc_info->assoc_req_len);
					mutex_unlock(&priv->wdev->mtx);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
					mutex_lock(&priv->wdev->mtx);
					cfg80211_rx_assoc_resp(
						priv->netdev, bss,
						assoc_info->assoc_resp_buf,
						assoc_info->assoc_resp_len, -1);
					mutex_unlock(&priv->wdev->mtx);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
					mutex_lock(&priv->wdev->mtx);
					cfg80211_rx_assoc_resp(
						priv->netdev, bss,
						assoc_info->assoc_resp_buf,
						assoc_info->assoc_resp_len);
					mutex_unlock(&priv->wdev->mtx);
#else
					cfg80211_send_rx_assoc(
						priv->netdev, bss,
						assoc_info->assoc_resp_buf,
						assoc_info->assoc_resp_len);
#endif
#endif
#endif
#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
 */

static 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;
	mlan_ds_misc_assoc_req assoc_req;
	memset(&assoc_req, 0, sizeof(mlan_ds_misc_assoc_req));
	woal_get_assoc_req(priv, &assoc_req, MOAL_IOCTL_WAIT);

	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_info.assoc_resp_buf,
				passoc_rsp->assoc_resp_buf,
				passoc_rsp->assoc_resp_len, ASSOC_RSP_BUF_SIZE);
		evt->assoc_info.assoc_resp_len =
			MIN(passoc_rsp->assoc_resp_len, ASSOC_RSP_BUF_SIZE);
		moal_memcpy_ext(priv->phandle, evt->assoc_info.assoc_req_buf,
				assoc_req.assoc_req_buf,
				assoc_req.assoc_req_len, ASSOC_RSP_BUF_SIZE);
		evt->assoc_info.assoc_req_len =
			MIN(assoc_req.assoc_req_len, ASSOC_RSP_BUF_SIZE);

		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;
	int ie_len;
	mlan_ssid_bssid *ssid_bssid = NULL;
	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();

	ssid_bssid = kmalloc(sizeof(mlan_ssid_bssid), GFP_KERNEL);
	if (!ssid_bssid) {
		LEAVE();
		return -EFAULT;
	}

	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)) {
		kfree(ssid_bssid);
		LEAVE();
		return -EBUSY;
	}

	/** cancel pending scan */
	woal_cancel_scan(priv, MOAL_IOCTL_WAIT);

	priv->cfg_connect = MTRUE;
	priv->assoc_status = 0;
	priv->auth_alg = 0xFFFF;

	memset(ssid_bssid, 0, sizeof(mlan_ssid_bssid));
	rcu_read_lock();
	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) {
		rcu_read_unlock();
		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];
	rcu_read_unlock();

	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, 0);
		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, 1);
		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);
	}
	if (req->prev_bssid) {
		moal_memcpy_ext(priv->phandle, ssid_bssid->prev_bssid,
				req->prev_bssid, ETH_ALEN,
				sizeof(ssid_bssid->prev_bssid));
	}

	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;

		woal_save_assoc_params(priv, req, ssid_bssid);

		memset(&bss_info, 0, sizeof(bss_info));
		if (MLAN_STATUS_SUCCESS !=
		    woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
			PRINTM(MERROR,
			       "woal_get_bss_info Fails to get bss info\n");
		}
		priv->channel = bss_info.bss_chan;
	} else {
		/* clear the encryption mode */
		if (MLAN_STATUS_SUCCESS !=
		    woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE,
					   MFALSE,MOAL_IOCTL_WAIT, 0)) {
			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,
					MOAL_IOCTL_WAIT)) {
			PRINTM(MERROR, "Could not clear RSN IE\n");
			ret = -EFAULT;
		}
	}

	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);
			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);
	}
	kfree(ssid_bssid);
	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 = NULL;
	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;
	struct ieee80211_channel *channel = NULL;
	t_u16 beacon_interval = 0;
	bool privacy;
	struct cfg80211_bss *pub = NULL;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	if (!sme) {
		LEAVE();
		return -EFAULT;
	}
	ssid_bssid = kmalloc(sizeof(mlan_ssid_bssid), GFP_KERNEL);
	if (!ssid_bssid) {
		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
		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)
		pub = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid,
				       ssid_len, IEEE80211_BSS_TYPE_ESS,
				       IEEE80211_PRIVACY_ANY);
#else
		pub = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid,
				       ssid_len, WLAN_CAPABILITY_ESS,
				       WLAN_CAPABILITY_ESS);
#endif
		if (pub) {
			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, pub);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
			cfg80211_put_bss(priv->wdev->wiphy, pub);
#else
			cfg80211_put_bss(pub);
#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, 0);
			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, 1);
			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, 0);
			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 CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
	if (conn_param && conn_param->prev_bssid) {
		moal_memcpy_ext(priv->phandle, ssid_bssid->prev_bssid,
				conn_param->prev_bssid, ETH_ALEN,
				sizeof(ssid_bssid->prev_bssid));
	}
#endif

	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:
	kfree(ssid_bssid);
	if (ret) {
		/* clear the encryption mode */
		if (MLAN_STATUS_SUCCESS !=
		    woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE,
					   MFALSE, wait_option, 0)) {
			PRINTM(MERROR, "Could not clear encryption \n");
			ret = -EFAULT;
		}
		if (MLAN_STATUS_SUCCESS !=
		    woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE,
					   MFALSE, wait_option, 1)) {
			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
 */
static 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 */
		if (rate->param.data_rate.tx_data_rate < 12) {
			sinfo->txrate.legacy =
				Rates[rate->param.data_rate.tx_data_rate] * 5;
		} else
			sinfo->txrate.legacy = Rates[0] * 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 */
		if (rate->param.data_rate.rx_data_rate < 12) {
			sinfo->rxrate.legacy =
				Rates[rate->param.data_rate.rx_data_rate] * 5;
		} else
			sinfo->rxrate.legacy = 0;
	}
#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 (priv->phandle->scan_pending_on_block) {
		if (priv->sinfo)
			moal_memcpy_ext(priv->phandle, sinfo, priv->sinfo,
					sizeof(struct station_info),
					sizeof(struct station_info));
		LEAVE();
		return ret;
	}
#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);
	if (priv->sinfo)
		moal_memcpy_ext(priv->phandle, priv->sinfo, sinfo,
				sizeof(struct station_info),
				sizeof(struct station_info));

done:
	LEAVE();
	return ret;
}

/********************************************************
				Global Functions
********************************************************/
#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 get channel reg_rule flags
 *
 *  @param buf              Buffer containing channel region config
 *  @param num_chan         Length of buffer
 *  @param regd             ieee80211_regdomain to be updated
 *
 *  @return                 N/A
 */
t_u32 woal_get_chan_rule_flags(mlan_ds_custom_reg_domain *custom_reg,
			       t_u8 channel)
{
	t_u16 num_chan = 0;
	t_u32 flags = 0;
	int idx;
	t_u16 chflags;

	num_chan = custom_reg->num_bg_chan;
	num_chan += custom_reg->num_a_chan;
	for (idx = 0; idx < num_chan; idx++) {
		chflags = custom_reg->cfp_tbl[idx].dynamic.flags;
		if (chflags & NXP_CHANNEL_DISABLED)
			continue;
		if (custom_reg->cfp_tbl[idx].channel == channel) {
			if (chflags & NXP_CHANNEL_PASSIVE)
				flags |= NL80211_RRF_NO_IR;
			if (chflags & NXP_CHANNEL_DFS)
				flags |= NL80211_RRF_DFS;
			if (chflags & NXP_CHANNEL_NO_OFDM)
				flags |= NL80211_RRF_NO_OFDM;
			break;
		}
	}
	return flags;
}

/**
 *  @brief This function update the beaconng flags of channel
 *
 *  @param custom_reg       pointer to mlan_ds_custom_reg_domain
 *
 *  @return                 pointer to ieee80211_regdomain
 */

static void
woal_reg_apply_beaconing_flags(struct wiphy *wiphy,
			       mlan_ds_custom_reg_domain *custom_reg)
{
	struct ieee80211_supported_band *sband;
	struct ieee80211_channel *ch;
	int band, i;
	t_u32 rule_flags = 0;

	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
		sband = wiphy->bands[band];
		if (!sband)
			continue;

		for (i = 0; i < sband->n_channels; i++) {
			ch = &sband->channels[i];

			if (ch->flags &
			    (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR))
				continue;

			rule_flags = woal_get_chan_rule_flags(custom_reg,
							      ch->hw_value);

			if (!(rule_flags & NL80211_RRF_NO_IR))
				ch->flags &= ~IEEE80211_CHAN_NO_IR;
		}
	}
}

/**
 *  @brief This function create the custom regdomain
 *
 *  @param priv       pointer to moal_private
 *  @param custom_reg       pointer to mlan_ds_custom_reg_domain
 *
 *  @return                 pointer to ieee80211_regdomain
 */
static struct ieee80211_regdomain *
create_custom_regdomain(moal_private *priv,
			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;
	}
	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 (chflags & NXP_CHANNEL_PASSIVE)
			rule->flags |= NL80211_RRF_NO_IR;
		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];
	}

	switch (custom_reg->region.dfs_region) {
	case 1:
		regd->dfs_region = NL80211_DFS_FCC;
		break;
	case 2:
		regd->dfs_region = NL80211_DFS_ETSI;
		break;
	case 3:
		regd->dfs_region = NL80211_DFS_JP;
		break;
	default:
		regd->dfs_region = NL80211_DFS_UNSET;
		break;
	}
	priv->phandle->dfs_region = regd->dfs_region;
	PRINTM(MCMND, "create_custom_regdomain: %c%c rules=%d dfs_region=%d\n",
	       regd->alpha2[0], regd->alpha2[1], valid_rules, regd->dfs_region);
	for (idx = 0; idx < (int)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);
	}
	if (!regd->n_reg_rules) {
		kfree(regd);
		regd = NULL;
	}
	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] == '\0' ||
	    misc->param.custom_reg_domain.region.country_code[1] == '\0') {
		PRINTM(MCMND, "FW country code not valid\n");
		ret = -EFAULT;
		goto done;
	}
	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(MCMND, "FW 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]);
	}
	regd = create_custom_regdomain(priv, &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;
		rtnl_lock();
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) || CFG80211_VERSION_CODE == KERNEL_VERSION(4, 19, 206)
		ret = regulatory_set_wiphy_regd_sync(wiphy, regd);
#else
		ret = regulatory_set_wiphy_regd_sync_rtnl(wiphy, regd);
#endif
		rtnl_unlock();
		kfree(regd);
		if (!ret)
			woal_reg_apply_beaconing_flags(
				wiphy, &misc->param.custom_reg_domain);
	}
done:
	if (status != MLAN_STATUS_PENDING)
		kfree(req);
	LEAVE();
	return ret;
}

/**
 *  @brief This workqueue handles create customer regulatory
 * case
 *
 *  @param work    A pointer to work_struct
 *
 *  @return        N/A
 */
void woal_regulatory_work_queue(struct work_struct *work)
{
	moal_handle *handle = container_of(work, moal_handle, regulatory_work);
	struct wiphy *wiphy = handle->wiphy;
	moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
	enum ieee80211_band band;

	if (priv && wiphy) {
		woal_update_custom_regdomain(priv, wiphy);
		band = priv->phandle->band;
		priv->phandle->band = IEEE80211_BAND_2GHZ;
		woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
		if (priv->phandle->fw_bands & BAND_A) {
			priv->phandle->band = IEEE80211_BAND_5GHZ;
			woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
		}
		priv->phandle->band = band;
	}
}
#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
	t_u8 load_power_table = MFALSE;
	mlan_fw_info fw_info;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
	char *reg_alpha2 = NULL;
#endif
	t_u8 dfs_region = NXP_DFS_UNKNOWN;

	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
	}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
	dfs_region = request->dfs_region;
#endif
	PRINTM(MCMND,
	       "cfg80211 regulatory domain callback "
	       "%c%c initiator=%d dfs_region=%d\n",
	       request->alpha2[0], request->alpha2[1], request->initiator,
	       dfs_region);

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
	if (!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED))
#endif
		handle->dfs_region = dfs_region;

	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
	}

	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 (handle->params.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
			}
			load_power_table = MTRUE;
		}
	}
	if (!handle->params.cntry_txpwr) {
		handle->country_code[0] = request->alpha2[0];
		handle->country_code[1] = request->alpha2[1];
		handle->country_code[2] = ' ';
		if (MTRUE != is_cfg80211_special_region_code(region)) {
			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");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
		reg_alpha2 = priv->phandle->params.reg_alpha2;
		if ((handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE) &&
		    !handle->params.txpwrlimit_cfg && load_power_table &&
		    reg_alpha2 && woal_is_valid_alpha2(reg_alpha2))
			queue_work(handle->evt_workqueue,
				   &handle->regulatory_work);
#endif
		break;
	case NL80211_REGDOM_SET_BY_CORE:
		PRINTM(MCMND, "Regulatory domain BY_CORE\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
		if (handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE &&
		    load_power_table && !handle->params.txpwrlimit_cfg)
			queue_work(handle->evt_workqueue,
				   &handle->regulatory_work);
#endif
		break;
	case NL80211_REGDOM_SET_BY_USER:
		PRINTM(MCMND, "Regulatory domain BY_USER\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
		if (handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE &&
		    load_power_table && !handle->params.txpwrlimit_cfg)
			queue_work(handle->evt_workqueue,
				   &handle->regulatory_work);
#endif
		break;
	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
		PRINTM(MCMND, "Regulatory domain BY_COUNTRY_IE\n");
		break;
	}
	if (priv->wdev && priv->wdev->wiphy &&
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
	    !(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
#endif
	    (request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
		band = priv->phandle->band;
		priv->phandle->band = IEEE80211_BAND_2GHZ;
		woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
		if (priv->phandle->fw_bands & BAND_A) {
			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
}

static int woal_find_wps_ie_in_probereq(const t_u8 *ie, size_t 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;
}

#ifdef UAP_CFG80211
/** scan result expired value */
#define SCAN_RESULT_EXPIRTED 1
/**
 *  @brief check if the scan result expired
 *
 *  @param priv         A pointer to moal_private
 *
 *
 *  @return             MTRUE/MFALSE;
 */
static t_u8 woal_is_uap_scan_result_expired(moal_private *priv)
{
	mlan_scan_resp scan_resp;
	wifi_timeval t;
	ENTER();

	memset(&scan_resp, 0, sizeof(scan_resp));

	if (MLAN_STATUS_SUCCESS !=
	    woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
		LEAVE();
		return MTRUE;
	}
	if (!scan_resp.num_in_scan_table) {
		LEAVE();
		return MTRUE;
	}
	woal_get_monotonic_time(&t);
	if (t.time_sec > (scan_resp.age_in_secs + SCAN_RESULT_EXPIRTED)) {
		LEAVE();
		return MTRUE;
	}
	LEAVE();
	return MFALSE;
}
#endif

/**
 *  @brief check if the scan result ageout
 *
 *  @param priv         A pointer to moal_private
 *
 *
 *  @return             MTRUE/MFALSE;
 */
t_u8 wlan_check_scan_table_ageout(moal_private *priv)
{
	mlan_scan_resp scan_resp;
	wifi_timeval t;
	ENTER();
	memset(&scan_resp, 0, sizeof(scan_resp));
	if (MLAN_STATUS_SUCCESS !=
	    woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
		LEAVE();
		return MFALSE;
	}
	woal_get_monotonic_time(&t);
#define CFG80211_SCAN_RESULT_AGEOUT 10
	if (t.time_sec >
	    (scan_resp.age_in_secs + CFG80211_SCAN_RESULT_AGEOUT)) {
		LEAVE();
		return MFALSE;
	}
	PRINTM(MCMND, "Scan: Keep Previous result\n");
	LEAVE();
	return MTRUE;
}

#ifndef NO_SILEX_CHANGED /* Flush scan */
static int woal_request_scan_flush(moal_private *priv)
{
	mlan_ioctl_req *ioctl_req = NULL;
	mlan_ds_scan *scan = NULL;
	int ret = 0;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	/* Allocate an IOCTL request buffer */
	ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
	if (ioctl_req == NULL) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}

	scan = (mlan_ds_scan *)ioctl_req->pbuf;

	/* Flush the scan table*/
	ioctl_req->req_id = MLAN_IOCTL_SCAN;
	ioctl_req->action = MLAN_ACT_SET;

	scan->sub_command = MLAN_OID_SCAN_TABLE_FLUSH;

	/* Send IOCTL request to MLAN */
	status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);

	if (status == MLAN_STATUS_FAILURE) {
		ret = MLAN_STATUS_FAILURE;
		goto done;
	}
done:
	if (status != MLAN_STATUS_PENDING)
		kfree(ioctl_req);

	LEAVE();
	return ret;
}
#endif

#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->driver_status) {
		PRINTM(MERROR,
		       "Block woal_cfg80211_scan in abnormal driver state\n");
		LEAVE();
		return -EFAULT;
	}
	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
	cancel_delayed_work(&priv->phandle->scan_timeout_work);
	priv->phandle->fake_scan_complete = priv->fake_scan_complete;
	if (priv->fake_scan_complete) {
		PRINTM(MEVENT, "fake scan complete flag is on\n");
		priv->phandle->scan_request = request;
		queue_delayed_work(priv->phandle->evt_workqueue,
				   &priv->phandle->scan_timeout_work,
				   msecs_to_jiffies(1000));
		return ret;
	}
	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);
	if (!scan_req) {
		PRINTM(MERROR, "Failed to alloc memory for scan_req\n");
		LEAVE();
		return -ENOMEM;
	}
	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

	memset(&scan_cfg, 0, sizeof(mlan_scan_cfg));
	if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) {
		PRINTM(MERROR, "Fail to get scan request IE\n");
	}
#ifdef WIFI_DIRECT_SUPPORT
	if (priv->phandle->miracast_mode)
		scan_req->scan_chan_gap = priv->phandle->scan_chan_gap;
	else {
#endif
		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;

	scan_req->scan_cfg_only = MTRUE;
	if (scan_cfg.ext_scan == 3)
		scan_req->ext_scan_type = EXT_SCAN_ENHANCE;

	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 < (int)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 =
			woal_ieee_band_to_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;
		PRINTM(MCMD_D, "cfg80211_scan: chan=%d chan->flag=0x%x\n",
		       chan->hw_value, chan->flags);
#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(
					MIN_SPECIFIC_SCAN_CHAN_TIME,
					scan_cfg.scan_time.specific_scan_time);
		}
#endif
#ifdef UAP_CFG80211
		if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
			if (!woal_is_uap_scan_result_expired(priv))
				scan_req->chan_list[i].scan_time =
					MIN_SPECIFIC_SCAN_CHAN_TIME;

			else
				scan_req->chan_list[i].scan_time =
					PASSIVE_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) {
			if (MLAN_STATUS_SUCCESS !=
			    woal_cfg80211_mgmt_frame_ie(
				    priv, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
				    MGMT_MASK_PROBE_REQ, MOAL_IOCTL_WAIT)) {
				PRINTM(MERROR,
				       "Fail to clear scan request IE\n");
				ret = -EFAULT;
				goto done;
			}
		}
	}
#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));

#ifndef NO_SILEX_CHANGED  /* Flush scan*/
	if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
		PRINTM(MIOCTL, "NL80211_SCAN_FLAG_FLUSH is set\n");
		woal_request_scan_flush(priv);
	}
#endif
	PRINTM(MCMND, "wlan:random_mac " MACSTR "\n",
	       MAC2STR(scan_req->random_mac));
	if (priv->phandle->params.keep_previous_scan)
		scan_req->keep_previous_scan =
			wlan_check_scan_table_ageout(priv);

	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);
		queue_delayed_work(
			priv->phandle->evt_workqueue,
			&priv->phandle->scan_timeout_work,
			msecs_to_jiffies(priv->phandle->scan_timeout));
	}
	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_u16 pkt_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*/
	pkt_len = woal_cpu_to_le16(packet_len);
	moal_memcpy_ext(priv->phandle,
			pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
			&pkt_len, sizeof(pkt_len), sizeof(pkt_len));

	mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
				  HEADER_SIZE + sizeof(packet_len));
	memset(mgmt, 0, MGMT_HEADER_LEN);
	mgmt->frame_control =
		woal_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_u16 pkt_len;
	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*/
	pkt_len = woal_cpu_to_le16(packet_len);
	moal_memcpy_ext(priv->phandle,
			pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
			&pkt_len, sizeof(pkt_len), sizeof(pkt_len));

	mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
				  HEADER_SIZE + sizeof(packet_len));
	memset(mgmt, 0, MGMT_HEADER_LEN);
	mgmt->frame_control =
		woal_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 = woal_cpu_to_le16(WLAN_AUTH_FT);
	mgmt->u.auth.auth_transaction = woal_cpu_to_le16(1);
	mgmt->u.auth.status_code = woal_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)
	if (woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE,
						&status, chan, 0,
						AUTH_TX_DEFAULT_WAIT_TIME)) {
		PRINTM(MERROR, "Failed remain on channel config\n");
	}
#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)
	if (woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE,
						&status, NULL, 0, 0)) {
		PRINTM(MERROR, "Failed to cancel remain on channel\n");
	}
#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)
		if (woal_cfg80211_remain_on_channel_cfg(priv, wait_option,
							MFALSE, &status, pchan,
							0, 1200)) {
			PRINTM(MERROR,
			       "Failed to configure remain on channel\n");
		}
#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)
			if (woal_cfg80211_remain_on_channel_cfg(
				    priv, wait_option, MTRUE, &status, NULL, 0,
				    0)) {
				PRINTM(MERROR,
				       "Failed to cancel remain on channel\n");
			}
#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)
		if (woal_cfg80211_remain_on_channel_cfg(
			    priv, wait_option, MTRUE, &status, NULL, 0, 0)) {
			PRINTM(MERROR, "Failed to cancel remain on channel\n");
		}
#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_ds_misc_assoc_req assoc_req;

	mlan_ssid_bssid *ssid_bssid = NULL;
	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;
	}
	ssid_bssid = kmalloc(sizeof(mlan_ssid_bssid), GFP_KERNEL);
	if (!ssid_bssid) {
		LEAVE();
		return -EFAULT;
	}
	memset(ssid_bssid, 0, sizeof(mlan_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));
			kfree(ssid_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;

			kfree(ssid_bssid);
			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));
		if (MLAN_STATUS_SUCCESS !=
		    woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
			PRINTM(MERROR, "Fail to get bss info\n");
		}
		/** 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);
			kfree(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;
		kfree(ssid_bssid);
		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;
		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;
		}
		memset(&assoc_req, 0, sizeof(mlan_ds_misc_assoc_req));
		woal_get_assoc_req(priv, &assoc_req, MOAL_IOCTL_WAIT);
	}
	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, assoc_req.assoc_req_buf,
			assoc_req.assoc_req_len, 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(ssid_bssid);
	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);
	mlan_status status = MLAN_STATUS_SUCCESS;

	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->media_connected == MFALSE) {
		PRINTM(MMSG, " Already disconnected\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13)
		if (priv->wdev->connected &&
#else
		if (priv->wdev->current_bss &&
#endif
		    (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;
	}

	if (priv->cfg_disconnect) {
		PRINTM(MERROR, "Disassociation already in progress\n");
		LEAVE();
		return 0;
	}

	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, "Fail to cancel remain on channel\n");
		}
		priv->phandle->remain_on_channel = MFALSE;
	}

	/** cancel pending scan */
	woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
	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);
	woal_clear_conn_params(priv);
	priv->channel = 0;

	LEAVE();
	return 0;
}

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 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;
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
	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
	if (priv->media_connected)
		woal_send_disconnect_to_system(priv, DEF_DEAUTH_REASON_CODE);
	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;
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
	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
	if (priv->media_connected)
		woal_send_disconnect_to_system(priv, DEF_DEAUTH_REASON_CODE);

	return ret;
}
#endif

/**
 * @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)
	woal_check_auto_tdls(wiphy, dev);
#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
 */
static 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 > (int)scan_resp.num_in_chan_stats || idx < 0) {
		ret = -EFAULT;
		goto done;
	}
	if (idx == (int)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,
#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13)
				     unsigned int link_id,
#endif
				     struct cfg80211_chan_def *chandef)
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
	chan_band_info channel;
#ifdef UAP_SUPPORT
	moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
#endif

	memset(&channel, 0x00, sizeof(channel));

#ifdef UAP_SUPPORT
	if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
		if ((handle->mon_if) &&
		    (handle->mon_if->mon_ndev == wdev->netdev)) {
			*chandef = handle->mon_if->chandef;
			return 0;
		}
		return -EFAULT;
	}
#endif

#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();
	memset(&power_cfg, 0, sizeof(power_cfg));

	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)
{
	int ret = 0;
	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);
	if (MLAN_STATUS_SUCCESS !=
	    woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT)) {
		PRINTM(MERROR, "Fail to set rssi thresold.\n");
	}
	LEAVE();
	return ret;
}
#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
		chan_cfg.bandcfg.chanBand =
			woal_ieee_band_to_radio_type(chan->band);
		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
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
	*cookie = (u64)prandom_u32() | 1;
#else
	*cookie = (u64)get_random_u32() | 1;
#endif
#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(MCMND,
	       "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d iterations=%d ie_len=%d\n",
	       priv->netdev->name, request->n_ssids, request->n_match_sets,
	       request->n_channels, request->scan_plans[0].interval,
	       request->scan_plans[0].iterations, (int)request->ie_len);
#else
	PRINTM(MCMND,
	       "%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 < (int)MIN(WLAN_USER_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 =
			woal_ieee_band_to_radio_type(chan->band);
		if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
				priv->scan_cfg.chan_list[i].scan_type=
					MLAN_SCAN_TYPE_PASSIVE;
		else if (chan->flags & IEEE80211_CHAN_RADAR)
			priv->scan_cfg.chan_list[i].scan_type=
					MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
		else
			priv->scan_cfg.chan_list[i].scan_type =
				MLAN_SCAN_TYPE_ACTIVE;
		PRINTM(MCMD_D, "cfg80211_sched_scan: chan=%d chan->flag=0x%x\n",
		       chan->hw_value, chan->flags);
		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_USER_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) {
			if (MLAN_STATUS_SUCCESS !=
			    woal_cfg80211_mgmt_frame_ie(
				    priv, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
				    MGMT_MASK_PROBE_REQ, MOAL_IOCTL_WAIT)) {
				PRINTM(MERROR, "Fail to clear sched scan IE\n");
				ret = -EFAULT;
				goto done;
			}
		}
	}

	/* 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;
	priv->scan_cfg.repeat_count = 0;

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
	/* interval in seconds */
	if (request->scan_plans[0].interval)
		priv->scan_cfg.scan_interval =
			request->scan_plans[0].interval * 1000;
	priv->scan_cfg.repeat_count = request->scan_plans[0].iterations;
	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);
		/*High scan interval in milliseconds*/
		priv->scan_cfg.high_period =
			request->scan_plans[0].interval * 1000;
		priv->scan_cfg.high_period_count =
			request->scan_plans[0].iterations;
		/*Mid scan interval in seconds*/
		priv->scan_cfg.mid_period = request->scan_plans[1].interval;
		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);
			/*low scan interval in seconds*/
			priv->scan_cfg.low_period =
				request->scan_plans[2].interval;
			priv->scan_cfg.low_period_count =
				request->scan_plans[2].iterations;
		}
	}
#else
	/* interval in miliseconds */
	if (request->interval)
		priv->scan_cfg.scan_interval = request->interval;
#endif
	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)) {
		PRINTM(MMSG, "wlan: sched scan start\n");
		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(MMSG, "wlan: 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);
#if KERNEL_VERSION(3, 2, 0) <= CFG80211_VERSION_CODE
					woal_report_sched_scan_result(
						handle->priv[i]);
#endif
					handle->priv[i]->last_event = 0;
					PRINTM(MCMND,
					       "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, 1, 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
#ifdef REASSOCIATION
					wake_up_interruptible(
						&handle->reassoc_thread.wait_q);
#endif
				}
			}
		}
	}

	memset((t_u8 *)&wakeup_reason, 0, sizeof(wakeup_reason));
	if (MLAN_STATUS_SUCCESS !=
	    woal_get_wakeup_reason(priv, &wakeup_reason)) {
		PRINTM(MERROR, "%s: get_wakeup_reason failed \n", __func__);
		goto done;
	}
#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;
	queue_work(handle->rx_workqueue, &handle->rx_work);

	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->bss_role == MLAN_BSS_ROLE_STA) &&
		       !priv->media_connected)) ||
	    ((priv->bss_role == MLAN_BSS_ROLE_UAP) && !priv->bss_started)) {
		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 TDLS operation ioctl handler
 *
 *  @param priv     A pointer to moal_private structure
 *  @param peer     A pointer to peer mac
 *  @apram action   action for TDLS
 *  @return         0 --success, otherwise fail
 */
static int woal_tdls_oper(moal_private *priv, u8 *peer, t_u8 action)
{
	mlan_ioctl_req *ioctl_req = NULL;
	mlan_ds_misc_cfg *misc = NULL;
	int ret = 0;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
	if (ioctl_req == NULL) {
		ret = -ENOMEM;
		goto done;
	}
	misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
	misc->sub_command = MLAN_OID_MISC_TDLS_OPER;
	ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
	ioctl_req->action = MLAN_ACT_SET;
	misc->param.tdls_oper.tdls_action = action;
	moal_memcpy_ext(priv->phandle, misc->param.tdls_oper.peer_mac, peer,
			ETH_ALEN, sizeof(misc->param.tdls_oper.peer_mac));
	status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
	if (status != MLAN_STATUS_SUCCESS) {
		ret = -EFAULT;
		goto done;
	}
done:
	if (status != MLAN_STATUS_PENDING)
		kfree(ioctl_req);
	LEAVE();
	return ret;
}

/**
 *  @brief TDLS operation ioctl handler
 *
 *  @param priv         A pointer to moal_private structure
 *  @param peer         A pointer to peer mac
 *  @param tdls_ies     A pointer to mlan_ds_misc_tdls_ies structure
 *  @param flags        TDLS ie flags
 *
 *  @return         0 --success, otherwise fail
 */
static int woal_tdls_get_ies(moal_private *priv, u8 *peer,
			     mlan_ds_misc_tdls_ies *tdls_ies, t_u16 flags)
{
	mlan_ioctl_req *ioctl_req = NULL;
	mlan_ds_misc_cfg *misc = NULL;
	int ret = 0;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
	if (ioctl_req == NULL) {
		ret = -ENOMEM;
		goto done;
	}
	misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
	misc->sub_command = MLAN_OID_MISC_GET_TDLS_IES;
	ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
	ioctl_req->action = MLAN_ACT_GET;
	misc->param.tdls_ies.flags = flags;
	moal_memcpy_ext(priv->phandle, misc->param.tdls_ies.peer_mac, peer,
			ETH_ALEN, sizeof(misc->param.tdls_ies.peer_mac));
	status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
	if (status != MLAN_STATUS_SUCCESS) {
		ret = -EFAULT;
		goto done;
	}
	if (tdls_ies)
		moal_memcpy_ext(priv->phandle, tdls_ies, &misc->param.tdls_ies,
				sizeof(mlan_ds_misc_tdls_ies),
				sizeof(mlan_ds_misc_tdls_ies));
done:
	if (status != MLAN_STATUS_PENDING)
		kfree(ioctl_req);
	LEAVE();
	return ret;
}

/**
 * @brief append tdls ext_capability
 *
 * @param skb                   A pointer to sk_buff structure
 *
 * @return                      N/A
 */
static void woal_tdls_add_ext_capab(moal_private *priv, struct sk_buff *skb,
				    mlan_ds_misc_tdls_ies *tdls_ies)
{
	u8 *pos = NULL;
	if (tdls_ies->ext_cap[0] == WLAN_EID_EXT_CAPABILITY) {
		pos = (void *)skb_put(skb, sizeof(IEEEtypes_ExtCap_t));
		moal_memcpy_ext(priv->phandle, pos, tdls_ies->ext_cap,
				sizeof(IEEEtypes_ExtCap_t),
				sizeof(IEEEtypes_ExtCap_t));
	} else {
		PRINTM(MERROR, "Fail to append tdls ext_capability\n");
	}
}

/**
 * @brief append supported rates
 *
 * @param priv                  A pointer to moal_private structure
 * @param skb                   A pointer to sk_buff structure
 * @param band                  AP's band
 *
 * @return                      N/A
 */
static void woal_add_supported_rates_ie(moal_private *priv, struct sk_buff *skb,
					enum ieee80211_band band)
{
	t_u8 basic_rates[] = {0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24};
	t_u8 basic_rates_5G[] = {0x0c, 0x12, 0x18, 0x24,
				 0x30, 0x48, 0x60, 0x6c};
	t_u8 *pos;
	t_u8 rate_num = 0;
	if (band == IEEE80211_BAND_2GHZ)
		rate_num = sizeof(basic_rates);
	else
		rate_num = sizeof(basic_rates_5G);

	if (skb_tailroom(skb) < rate_num + 2)
		return;

	pos = skb_put(skb, rate_num + 2);
	*pos++ = WLAN_EID_SUPP_RATES;
	*pos++ = rate_num;
	if (band == IEEE80211_BAND_2GHZ)
		moal_memcpy_ext(priv->phandle, pos, basic_rates, rate_num,
				rate_num);
	else
		moal_memcpy_ext(priv->phandle, pos, basic_rates_5G, rate_num,
				rate_num);
	return;
}

/**
 * @brief append ext_supported rates
 *
 * @param priv                  A pointer to moal_private structure
 * @param skb                   A pointer to sk_buff structure
 * @param band                  AP's band
 *
 * @return                      N/A
 */
static void woal_add_ext_supported_rates_ie(moal_private *priv,
					    struct sk_buff *skb,
					    enum ieee80211_band band)
{
	t_u8 ext_rates[] = {0x0c, 0x12, 0x18, 0x60};
	t_u8 *pos;
	t_u8 rate_num = sizeof(ext_rates);

	if (band != IEEE80211_BAND_2GHZ)
		return;

	if (skb_tailroom(skb) < rate_num + 2)
		return;

	pos = skb_put(skb, rate_num + 2);
	*pos++ = WLAN_EID_EXT_SUPP_RATES;
	*pos++ = rate_num;
	moal_memcpy_ext(priv->phandle, pos, ext_rates, rate_num, rate_num);
	return;
}

/**
 * @brief append wmm ie
 *
 * @param priv                  A pointer to moal_private structure
 * @param skb                   A pointer to sk_buff structure
 * @param wmm_type         WMM_TYPE_INFO/WMM_TYPE_PARAMETER
 * @param pQosInfo           A pointer to qos info
 *
 * @return                      N/A
 */
static void woal_add_wmm_ie(moal_private *priv, struct sk_buff *skb,
			    t_u8 wmm_type, t_u8 *pQosInfo)
{
	t_u8 wmmInfoElement[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01};
	t_u8 wmmParamElement[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
	t_u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00};
	t_u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00};
	t_u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00};
	t_u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00};
	t_u8 qosInfo = 0x0;
	t_u8 reserved = 0;
	t_u8 wmm_id = 221;
	t_u8 wmmParamIe_len = 25;
	t_u8 wmmInfoIe_len = 7;
	t_u8 len = 0;
	t_u8 *pos;

	qosInfo = (pQosInfo == NULL) ? 0xf : (*pQosInfo);
	/*wmm parameter*/
	if (wmm_type == WMM_TYPE_PARAMETER) {
		if (skb_tailroom(skb) < (wmmParamIe_len + 2))
			return;
		pos = skb_put(skb, wmmParamIe_len + 2);
		len = wmmParamIe_len;
	} else {
		if (skb_tailroom(skb) < (wmmInfoIe_len + 2))
			return;
		pos = skb_put(skb, wmmInfoIe_len + 2);
		len = wmmInfoIe_len;
	}

	*pos++ = wmm_id;
	*pos++ = len;
	/*wmm parameter*/
	if (wmm_type == WMM_TYPE_PARAMETER) {
		moal_memcpy_ext(priv->phandle, pos, wmmParamElement,
				sizeof(wmmParamElement),
				sizeof(wmmParamElement));
		pos += sizeof(wmmParamElement);
	} else {
		moal_memcpy_ext(priv->phandle, pos, wmmInfoElement,
				sizeof(wmmInfoElement), sizeof(wmmInfoElement));
		pos += sizeof(wmmInfoElement);
	}
	*pos++ = qosInfo;
	/*wmm parameter*/
	if (wmm_type == WMM_TYPE_PARAMETER) {
		*pos++ = reserved;
		moal_memcpy_ext(priv->phandle, pos, ac_be, sizeof(ac_be),
				sizeof(ac_be));
		pos += sizeof(ac_be);
		moal_memcpy_ext(priv->phandle, pos, ac_bk, sizeof(ac_bk),
				sizeof(ac_bk));
		pos += sizeof(ac_bk);
		moal_memcpy_ext(priv->phandle, pos, ac_vi, sizeof(ac_vi),
				sizeof(ac_vi));
		pos += sizeof(ac_vi);
		moal_memcpy_ext(priv->phandle, pos, ac_vo, sizeof(ac_vo),
				sizeof(ac_vo));
	}
	return;
}

/**
 * @brief update tdls peer status
 *
 * @param priv                  A pointer to moal_private structure
 * @param peer_addr             A point to peer mac address
 * @param link_status           link status
 *
 * @return                      N/A
 */
static t_void woal_updata_peer_status(moal_private *priv, t_u8 *peer_addr,
				      tdlsStatus_e link_status)
{
	struct tdls_peer *peer = NULL;
	unsigned long flags;
	if (priv && priv->enable_auto_tdls) {
		spin_lock_irqsave(&priv->tdls_lock, flags);
		list_for_each_entry (peer, &priv->tdls_list, link) {
			if (!memcmp(peer->peer_addr, peer_addr, ETH_ALEN)) {
				if ((link_status == TDLS_NOT_SETUP) &&
				    (peer->link_status ==
				     TDLS_SETUP_INPROGRESS))
					peer->num_failure++;
				else if (link_status == TDLS_SETUP_COMPLETE)
					peer->num_failure = 0;
				peer->link_status = link_status;
				break;
			}
		}
		spin_unlock_irqrestore(&priv->tdls_lock, flags);
	}
}

/**
 * @brief add tdls peer
 *
 * @param priv                  A pointer to moal_private structure
 * @param peer                  A point to peer address
 *
 * @return                      N/A
 */
static t_void woal_add_tdls_peer(moal_private *priv, t_u8 *peer)
{
	struct tdls_peer *tdls_peer = NULL;
	unsigned long flags;
	t_u8 find_peer = MFALSE;
	if (priv && priv->enable_auto_tdls) {
		spin_lock_irqsave(&priv->tdls_lock, flags);
		list_for_each_entry (tdls_peer, &priv->tdls_list, link) {
			if (!memcmp(tdls_peer->peer_addr, peer, ETH_ALEN)) {
				tdls_peer->link_status = TDLS_SETUP_INPROGRESS;
				tdls_peer->rssi_jiffies = jiffies;
				find_peer = MTRUE;
				break;
			}
		}
		if (!find_peer) {
			/* create new TDLS peer */
			tdls_peer =
				kzalloc(sizeof(struct tdls_peer), GFP_ATOMIC);
			if (tdls_peer) {
				moal_memcpy_ext(priv->phandle,
						tdls_peer->peer_addr, peer,
						ETH_ALEN,
						sizeof(tdls_peer->peer_addr));
				tdls_peer->link_status = TDLS_SETUP_INPROGRESS;
				tdls_peer->rssi_jiffies = jiffies;
				INIT_LIST_HEAD(&tdls_peer->link);
				list_add_tail(&tdls_peer->link,
					      &priv->tdls_list);
				PRINTM(MCMND,
				       "Add to TDLS list: peer=" MACSTR "\n",
				       MAC2STR(peer));
			}
		}
		spin_unlock_irqrestore(&priv->tdls_lock, flags);
	}
}

/**
 * @brief check auto tdls
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 *
 * @return                      N/A
 */
void woal_check_auto_tdls(struct wiphy *wiphy, struct net_device *dev)
{
	t_u8 bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	struct tdls_peer *tdls_peer = NULL;
	unsigned long flags;
	t_u8 tdls_discovery = MFALSE;
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);

	ENTER();
	if (priv && priv->enable_auto_tdls) {
		priv->tdls_check_tx = MFALSE;
		spin_lock_irqsave(&priv->tdls_lock, flags);
		list_for_each_entry (tdls_peer, &priv->tdls_list, link) {
			if ((jiffies - tdls_peer->rssi_jiffies) >
			    TDLS_IDLE_TIME) {
				tdls_peer->rssi = 0;
				if (tdls_peer->num_failure <
				    TDLS_MAX_FAILURE_COUNT)
					tdls_discovery = MTRUE;
			}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
			if (tdls_peer->rssi &&
			    (tdls_peer->rssi >= TDLS_RSSI_LOW_THRESHOLD)) {
				if (tdls_peer->link_status ==
				    TDLS_SETUP_COMPLETE) {
					tdls_peer->link_status = TDLS_TEAR_DOWN;
					PRINTM(MMSG,
					       "Wlan: Tear down TDLS link, peer=" MACSTR
					       " rssi=%d\n",
					       MAC2STR(tdls_peer->peer_addr),
					       -tdls_peer->rssi);
					cfg80211_tdls_oper_request(
						dev, tdls_peer->peer_addr,
						NL80211_TDLS_TEARDOWN,
						TDLS_TEARN_DOWN_REASON_UNSPECIFIC,
						GFP_ATOMIC);
				}
			} else if (tdls_peer->rssi &&
				   (tdls_peer->rssi <=
				    TDLS_RSSI_HIGH_THRESHOLD)) {
				if ((tdls_peer->link_status ==
				     TDLS_NOT_SETUP) &&
				    (tdls_peer->num_failure <
				     TDLS_MAX_FAILURE_COUNT)) {
					priv->tdls_check_tx = MTRUE;
					PRINTM(MCMND,
					       "Wlan: Find TDLS peer=" MACSTR
					       " rssi=%d\n",
					       MAC2STR(tdls_peer->peer_addr),
					       -tdls_peer->rssi);
				}
			}
#endif
		}
		spin_unlock_irqrestore(&priv->tdls_lock, flags);
	}
	if (tdls_discovery)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
		woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr,
					TDLS_DISCOVERY_REQUEST, 1, 0, 0, 0,
					NULL, 0);
#else
		woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr,
					TDLS_DISCOVERY_REQUEST, 1, 0, 0, NULL,
					0);
#endif
#else
		woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr,
					TDLS_DISCOVERY_REQUEST, 1, 0, NULL, 0);
#endif
	LEAVE();
}

/**
 * @brief woal construct tdls data frame
 *
 * @param priv                  A pointer to moal_private structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param skb                   skb buffer
 *
 * @return                      0 -- success, otherwise fail
 */
static int woal_construct_tdls_data_frame(moal_private *priv, t_u8 *peer,
					  t_u8 action_code, t_u8 dialog_token,
					  t_u16 status_code,
					  struct sk_buff *skb)
{
	struct ieee80211_tdls_data *tdata;
	t_u16 capability;
	IEEEtypes_HTCap_t *HTcap;
	IEEEtypes_HTInfo_t *HTInfo;
	IEEEtypes_2040BSSCo_t *BSSCo;
	IEEEtypes_VHTCap_t *VHTcap;
	IEEEtypes_VHTOprat_t *vht_oprat;
	IEEEtypes_AID_t *AidInfo;
	IEEEtypes_Header_t *ieee_hdr;
	t_u8 *skb_data;
	t_u8 len = 0;
	IEEEtypes_Generic_t *pSupp_chan = NULL, *pRegulatory_class = NULL;
	mlan_ds_misc_tdls_ies *tdls_ies = NULL;
	int ret = 0;
	mlan_bss_info bss_info;
	enum ieee80211_band band;
	mlan_fw_info fw_info;
	t_u16 setup_flag = 0;
	t_u16 confirm_flag = 0;

	ENTER();

	memset(&bss_info, 0, sizeof(bss_info));
	if (MLAN_STATUS_SUCCESS !=
	    woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
		PRINTM(MERROR, "Fail to get bss info\n");
		LEAVE();
		return -EFAULT;
	}
	band = woal_band_cfg_to_ieee_band(bss_info.bss_band);
	tdls_ies = kzalloc(sizeof(mlan_ds_misc_tdls_ies), GFP_KERNEL);
	if (!tdls_ies) {
		PRINTM(MERROR, "Fail to alloc memory for tdls_ies\n");
		LEAVE();
		return -ENOMEM;
	}

	capability = 0x2421;

	memset(&fw_info, 0, sizeof(mlan_fw_info));
	tdata = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
	moal_memcpy_ext(priv->phandle, tdata->da, peer, ETH_ALEN,
			sizeof(tdata->da));
	moal_memcpy_ext(priv->phandle, tdata->sa, priv->current_addr, ETH_ALEN,
			sizeof(tdata->sa));
	tdata->ether_type = cpu_to_be16(MLAN_ETHER_PKT_TYPE_TDLS_ACTION);
	tdata->payload_type = WLAN_TDLS_SNAP_RFTYPE;
	woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);

	setup_flag = TDLS_IE_FLAGS_EXTCAP | TDLS_IE_FLAGS_HTCAP |
		     TDLS_IE_FLAGS_SUPP_CS_IE;
	confirm_flag = TDLS_IE_FLAGS_EXTCAP | TDLS_IE_FLAGS_HTINFO |
		       TDLS_IE_FLAGS_QOS_INFO;
	if (fw_info.fw_bands & BAND_AAC) {
		setup_flag |= (TDLS_IE_FLAGS_VHTCAP | TDLS_IE_FLAGS_AID);
		confirm_flag |= TDLS_IE_FLAGS_VHTOPRAT;
	}
	if (fw_info.fw_bands & BAND_AAX) {
		setup_flag |= (TDLS_IE_FLAGS_VHTCAP | TDLS_IE_FLAGS_AID |
			       TDLS_IE_FLAGS_HECAP);
		confirm_flag |= (TDLS_IE_FLAGS_VHTOPRAT | TDLS_IE_FLAGS_HEOP);
	}
	if (fw_info.fw_bands & BAND_GAX) {
		setup_flag |= TDLS_IE_FLAGS_HECAP;
		confirm_flag |= TDLS_IE_FLAGS_HEOP;
	}
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
		setup_flag |= TDLS_IE_FLAGS_SETUP;
		if (woal_tdls_get_ies(priv, peer, tdls_ies, setup_flag)) {
			PRINTM(MERROR, "%s: woal_tdls_get_ies failed \n",
			       __func__);
			ret = -EFAULT;
			goto done;
		}
		tdata->category = WLAN_CATEGORY_TDLS;
		tdata->action_code = WLAN_TDLS_SETUP_REQUEST;
		skb_put(skb, sizeof(tdata->u.setup_req));
		tdata->u.setup_req.dialog_token = dialog_token;
		tdata->u.setup_req.capability = cpu_to_le16(capability);
		woal_add_supported_rates_ie(priv, skb, band);
		woal_add_ext_supported_rates_ie(priv, skb, band);
		break;
	case WLAN_TDLS_SETUP_RESPONSE:
		if (woal_tdls_get_ies(priv, peer, tdls_ies, setup_flag)) {
			PRINTM(MERROR, "%s: woal_tdls_get_ies failed \n",
			       __func__);
			ret = -EFAULT;
			goto done;
		}
		tdata->category = WLAN_CATEGORY_TDLS;
		tdata->action_code = WLAN_TDLS_SETUP_RESPONSE;

		skb_put(skb, sizeof(tdata->u.setup_resp));
		tdata->u.setup_resp.status_code = cpu_to_le16(status_code);
		tdata->u.setup_resp.dialog_token = dialog_token;
		tdata->u.setup_resp.capability = cpu_to_le16(capability);

		woal_add_supported_rates_ie(priv, skb, band);
		woal_add_ext_supported_rates_ie(priv, skb, band);
		break;
	case WLAN_TDLS_SETUP_CONFIRM:
		if (woal_tdls_get_ies(priv, peer, tdls_ies, confirm_flag)) {
			PRINTM(MERROR, "%s: woal_tdls_get_ies failed \n",
			       __func__);
			ret = -EFAULT;
			goto done;
		}
		tdata->category = WLAN_CATEGORY_TDLS;
		tdata->action_code = WLAN_TDLS_SETUP_CONFIRM;

		skb_put(skb, sizeof(tdata->u.setup_cfm));
		tdata->u.setup_cfm.status_code = cpu_to_le16(status_code);
		tdata->u.setup_cfm.dialog_token = dialog_token;

		break;
	case WLAN_TDLS_TEARDOWN:
		tdata->category = WLAN_CATEGORY_TDLS;
		tdata->action_code = WLAN_TDLS_TEARDOWN;

		skb_put(skb, sizeof(tdata->u.teardown));
		tdata->u.teardown.reason_code = cpu_to_le16(status_code);
		break;
	case WLAN_TDLS_DISCOVERY_REQUEST:
		tdata->category = WLAN_CATEGORY_TDLS;
		tdata->action_code = WLAN_TDLS_DISCOVERY_REQUEST;

		skb_put(skb, sizeof(tdata->u.discover_req));
		tdata->u.discover_req.dialog_token = dialog_token;
		break;
	default:
		ret = -EINVAL;
		goto done;
	}

	if (action_code == WLAN_TDLS_SETUP_REQUEST ||
	    action_code == WLAN_TDLS_SETUP_RESPONSE) {
		/* supported chanel ie*/
		if (tdls_ies->supp_chan[0] == SUPPORTED_CHANNELS) {
			pSupp_chan = (void *)skb_put(
				skb, sizeof(IEEEtypes_Header_t) +
					     tdls_ies->supp_chan[1]);
			memset(pSupp_chan, 0,
			       sizeof(IEEEtypes_Header_t) +
				       tdls_ies->supp_chan[1]);
			moal_memcpy_ext(priv->phandle, pSupp_chan,
					tdls_ies->supp_chan,
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->supp_chan[1],
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->supp_chan[1]);
		}
		/* supported regulatory class ie*/
		if (tdls_ies->regulatory_class[0] == REGULATORY_CLASS) {
			pRegulatory_class = (void *)skb_put(
				skb, sizeof(IEEEtypes_Header_t) +
					     tdls_ies->regulatory_class[1]);
			memset(pRegulatory_class, 0,
			       sizeof(IEEEtypes_Header_t) +
				       tdls_ies->regulatory_class[1]);
			moal_memcpy_ext(priv->phandle, pRegulatory_class,
					tdls_ies->regulatory_class,
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->regulatory_class[1],
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->regulatory_class[1]);
		}
		woal_tdls_add_ext_capab(priv, skb, tdls_ies);
	}

	/* TODO we should fill in ht_cap and htinfo with correct value */
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
	case WLAN_TDLS_SETUP_RESPONSE:
		/*HT capability*/
		if (tdls_ies->ht_cap[0] == HT_CAPABILITY) {
			HTcap = (void *)skb_put(skb, sizeof(IEEEtypes_HTCap_t));
			memset(HTcap, 0, sizeof(IEEEtypes_HTCap_t));
			moal_memcpy_ext(priv->phandle, HTcap, tdls_ies->ht_cap,
					sizeof(IEEEtypes_HTCap_t),
					sizeof(IEEEtypes_HTCap_t));
		} else {
			PRINTM(MIOCTL, "No TDLS HT capability\n");
		}

		/*20_40_bss_coexist*/
		BSSCo = (void *)skb_put(skb, sizeof(IEEEtypes_2040BSSCo_t));
		memset(BSSCo, 0, sizeof(IEEEtypes_2040BSSCo_t));
		BSSCo->ieee_hdr.element_id = BSSCO_2040;
		BSSCo->ieee_hdr.len = sizeof(IEEEtypes_2040BSSCo_t) -
				      sizeof(IEEEtypes_Header_t);
		BSSCo->bss_co_2040.bss_co_2040_value = 0x01;

		/* VHT capability */
		if (tdls_ies->vht_cap[0] == VHT_CAPABILITY) {
			VHTcap = (void *)skb_put(skb,
						 sizeof(IEEEtypes_VHTCap_t));
			memset(VHTcap, 0, sizeof(IEEEtypes_VHTCap_t));
			moal_memcpy_ext(priv->phandle, VHTcap,
					tdls_ies->vht_cap,
					sizeof(IEEEtypes_VHTCap_t),
					sizeof(IEEEtypes_VHTCap_t));
		} else {
			PRINTM(MIOCTL, "NO TDLS VHT capability\n");
		}
		/* AID info */
		if (tdls_ies->aid_info[0] == AID_INFO) {
			AidInfo = (void *)skb_put(skb, sizeof(IEEEtypes_AID_t));
			memset(AidInfo, 0, sizeof(IEEEtypes_AID_t));
			moal_memcpy_ext(priv->phandle, AidInfo,
					tdls_ies->aid_info,
					sizeof(IEEEtypes_AID_t),
					sizeof(IEEEtypes_AID_t));
		} else {
			PRINTM(MIOCTL, "No TDLS AID info\n");
		}
		/* HE capability */
		if (tdls_ies->he_cap[2] == HE_CAPABILITY) {
			ieee_hdr = (IEEEtypes_Header_t *)tdls_ies->he_cap;
			len = sizeof(IEEEtypes_Header_t) + ieee_hdr->len;
			skb_data = (void *)skb_put(skb, len);
			memset(skb_data, 0, len);
			moal_memcpy_ext(priv->phandle, skb_data,
					tdls_ies->he_cap, len, len);
		} else {
			PRINTM(MIOCTL, "NO TDLS HE Capability IE\n");
		}
		break;
	case WLAN_TDLS_SETUP_CONFIRM:
		/*HT information*/
		if (tdls_ies->ht_info[0] == HT_OPERATION) {
			HTInfo = (void *)skb_put(skb,
						 sizeof(IEEEtypes_HTInfo_t));
			memset(HTInfo, 0, sizeof(IEEEtypes_HTInfo_t));
			moal_memcpy_ext(priv->phandle, HTInfo,
					tdls_ies->ht_info,
					sizeof(IEEEtypes_HTInfo_t),
					sizeof(IEEEtypes_HTInfo_t));
		} else
			PRINTM(MIOCTL, "No TDLS HT information\n");
		/** VHT operation */
		if (tdls_ies->vht_oprat[0] == VHT_OPERATION) {
			vht_oprat = (void *)skb_put(
				skb, sizeof(IEEEtypes_VHTOprat_t));
			memset(vht_oprat, 0, sizeof(IEEEtypes_VHTOprat_t));
			moal_memcpy_ext(priv->phandle, vht_oprat,
					tdls_ies->vht_oprat,
					sizeof(IEEEtypes_VHTOprat_t),
					sizeof(IEEEtypes_VHTOprat_t));
		} else
			PRINTM(MIOCTL, "NO TDLS VHT Operation IE\n");
		/** HE operation */
		if (tdls_ies->he_op[2] == HE_OPERATION) {
			ieee_hdr = (IEEEtypes_Header_t *)tdls_ies->he_op;
			len = sizeof(IEEEtypes_Header_t) + ieee_hdr->len;
			skb_data = (void *)skb_put(skb, len);
			memset(skb_data, 0, len);
			moal_memcpy_ext(priv->phandle, skb_data,
					tdls_ies->he_op, len, len);
		} else
			PRINTM(MIOCTL, "NO TDLS HE Operation IE\n");
		break;
	default:
		break;
	}

	if (action_code == WLAN_TDLS_SETUP_REQUEST ||
	    action_code == WLAN_TDLS_SETUP_RESPONSE) {
		/*wmm info*/
		woal_add_wmm_ie(priv, skb, WMM_TYPE_INFO, NULL);
	} else if (action_code == WLAN_TDLS_SETUP_CONFIRM) {
		/*wmm parameter*/
		woal_add_wmm_ie(priv, skb, WMM_TYPE_PARAMETER,
				&tdls_ies->QosInfo);
	}

done:
	kfree(tdls_ies);
	return ret;
}

/**
 * @brief woal construct tdls action frame
 *
 * @param priv                  A pointer to moal_private structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param skb                   skb buffer
 *
 * @return                      0 -- success, otherwise fail
 */
static int woal_construct_tdls_action_frame(moal_private *priv, t_u8 *peer,
					    t_u8 action_code, t_u8 dialog_token,
					    t_u16 status_code,
					    struct sk_buff *skb)
{
	struct ieee80211_mgmt *mgmt;
	t_u8 addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	t_u16 capability;
	t_u8 *pos = NULL;
	mlan_ds_misc_tdls_ies *tdls_ies = NULL;
	mlan_bss_info bss_info;
	enum ieee80211_band band;
	IEEEtypes_Generic_t *pSupp_chan = NULL, *pRegulatory_class = NULL;

	int ret = 0;

	ENTER();

	memset(&bss_info, 0, sizeof(bss_info));
	if (MLAN_STATUS_SUCCESS !=
	    woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
		PRINTM(MERROR, "Fail to get bss info\n");
		LEAVE();
		return -EFAULT;
	}
	band = woal_band_cfg_to_ieee_band(bss_info.bss_band);

	tdls_ies = kzalloc(sizeof(mlan_ds_misc_tdls_ies), GFP_KERNEL);
	if (!tdls_ies) {
		PRINTM(MERROR, "Fail to alloc memory for tdls_ies\n");
		LEAVE();
		return -ENOMEM;
	}

	mgmt = (void *)skb_put(skb, 24);
	memset(mgmt, 0, 24);
	moal_memcpy_ext(priv->phandle, mgmt->da, peer, 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, priv->cfg_bssid, ETH_ALEN,
			sizeof(mgmt->bssid));

	mgmt->frame_control =
		cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
	/* add address 4*/
	pos = skb_put(skb, ETH_ALEN);

	capability = 0x2421;

	switch (action_code) {
	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
		if (woal_tdls_get_ies(priv, peer, tdls_ies,
				      TDLS_IE_FLAGS_EXTCAP |
					      TDLS_IE_FLAGS_SUPP_CS_IE)) {
			PRINTM(MERROR, "%s: woal_tdls_get_ies failed \n",
			       __func__);
			if (tdls_ies)
				kfree(tdls_ies);
			LEAVE();
			return -EFAULT;
		}
		skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
		mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
		mgmt->u.action.u.tdls_discover_resp.action_code =
			WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
		mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token;
		mgmt->u.action.u.tdls_discover_resp.capability =
			cpu_to_le16(capability);
		/* move back for addr4 */
		memmove(pos + ETH_ALEN, &mgmt->u.action,
			1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
		/** init address 4 */
		moal_memcpy_ext(priv->phandle, pos, addr, ETH_ALEN, ETH_ALEN);

		woal_add_supported_rates_ie(priv, skb, band);
		woal_add_ext_supported_rates_ie(priv, skb, band);
		woal_tdls_add_ext_capab(priv, skb, tdls_ies);
		/* supported chanel ie*/
		if (tdls_ies->supp_chan[0] == SUPPORTED_CHANNELS) {
			pSupp_chan = (void *)skb_put(
				skb, sizeof(IEEEtypes_Header_t) +
					     tdls_ies->supp_chan[1]);
			memset(pSupp_chan, 0,
			       sizeof(IEEEtypes_Header_t) +
				       tdls_ies->supp_chan[1]);
			moal_memcpy_ext(priv->phandle, pSupp_chan,
					tdls_ies->supp_chan,
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->supp_chan[1],
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->supp_chan[1]);
		}
		/* supported regulatory class ie*/
		if (tdls_ies->regulatory_class[0] == REGULATORY_CLASS) {
			pRegulatory_class = (void *)skb_put(
				skb, sizeof(IEEEtypes_Header_t) +
					     tdls_ies->regulatory_class[1]);
			memset(pRegulatory_class, 0,
			       sizeof(IEEEtypes_Header_t) +
				       tdls_ies->regulatory_class[1]);
			moal_memcpy_ext(priv->phandle, pRegulatory_class,
					tdls_ies->regulatory_class,
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->regulatory_class[1],
					sizeof(IEEEtypes_Header_t) +
						tdls_ies->regulatory_class[1]);
		}

		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (tdls_ies)
		kfree(tdls_ies);
	return ret;
}

/**
 * @brief woal add tdls link identifier ie
 *
 * @param skb                   skb buffer
 * @param src_addr              source address
 * @param peer                  peer address
 * @param bssid                 AP's bssid
 *
 * @return                      NA
 */
static void woal_tdls_add_link_ie(moal_private *priv, struct sk_buff *skb,
				  u8 *src_addr, u8 *peer, u8 *bssid)
{
	struct ieee80211_tdls_lnkie *lnkid;

	lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));

	lnkid->ie_type = WLAN_EID_LINK_ID;
	lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;

	moal_memcpy_ext(priv->phandle, lnkid->bssid, bssid, ETH_ALEN,
			sizeof(lnkid->bssid));
	moal_memcpy_ext(priv->phandle, lnkid->init_sta, src_addr, ETH_ALEN,
			sizeof(lnkid->init_sta));
	moal_memcpy_ext(priv->phandle, lnkid->resp_sta, peer, ETH_ALEN,
			sizeof(lnkid->resp_sta));
}

/**
 * @brief woal send tdls action frame
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param extra_ies              A pointer to extra ie buffer
 * @param extra_ies_len          etra ie len
 * @param skb                   skb buffer
 *
 * @return                      0 -- success, otherwise fail
 */
static int woal_send_tdls_action_frame(struct wiphy *wiphy,
				       struct net_device *dev, t_u8 *peer,
				       u8 action_code, t_u8 dialog_token,
				       t_u16 status_code, const t_u8 *extra_ies,
				       size_t extra_ies_len)
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
	pmlan_buffer pmbuf = NULL;
	mlan_status status = MLAN_STATUS_SUCCESS;
	struct sk_buff *skb = NULL;
	t_u8 *pos;
	t_u32 pkt_type;
	t_u32 tx_control;
	t_u16 pkt_len;
	t_u16 packet_len;
	int ret = 0;

	ENTER();

#define HEADER_SIZE 8 /* pkt_type + tx_control */

	pmbuf = woal_alloc_mlan_buffer(
		priv->phandle,
		MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + sizeof(pkt_len) +
			max(sizeof(struct ieee80211_mgmt),
			    sizeof(struct ieee80211_tdls_data)) +
			50 + /* supported rates */
			sizeof(IEEEtypes_ExtCap_t) + /* ext capab */
			extra_ies_len + sizeof(IEEEtypes_tdls_linkie));
	if (!pmbuf) {
		PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
		ret = -ENOMEM;
		goto done;
	}

	skb = (struct sk_buff *)pmbuf->pdesc;

	skb_put(skb, MLAN_MIN_DATA_HEADER_LEN);

	pos = skb_put(skb, HEADER_SIZE + sizeof(pkt_len));
	pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
	tx_control = 0;
	memset(pos, 0, HEADER_SIZE + sizeof(pkt_len));
	moal_memcpy_ext(priv->phandle, pos, &pkt_type, sizeof(pkt_type),
			sizeof(pkt_type));
	moal_memcpy_ext(priv->phandle, pos + sizeof(pkt_type), &tx_control,
			sizeof(tx_control), sizeof(tx_control));

	woal_construct_tdls_action_frame(priv, peer, action_code, dialog_token,
					 status_code, skb);

	if (extra_ies_len)
		moal_memcpy_ext(priv->phandle, skb_put(skb, extra_ies_len),
				extra_ies, extra_ies_len, extra_ies_len);

	/* the TDLS link IE is always added last */
	/* we are the responder */
	woal_tdls_add_link_ie(priv, skb, peer, priv->current_addr,
			      priv->cfg_bssid);

	/*
	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
	 * we should default to AC_VI.
	 */
	skb_set_queue_mapping(skb, WMM_AC_VI);
	skb->priority = 5;

	pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
	pmbuf->data_len = skb->len - pmbuf->data_offset;
	pmbuf->priority = skb->priority;
	pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
	pmbuf->bss_index = priv->bss_index;

	pkt_len = pmbuf->data_len - HEADER_SIZE - sizeof(pkt_len);
	packet_len = woal_cpu_to_le16(pkt_len);
	moal_memcpy_ext(priv->phandle,
			pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
			&packet_len, sizeof(packet_len), sizeof(packet_len));

	DBG_HEXDUMP(MDAT_D, "TDLS action:", pmbuf->pbuf + pmbuf->data_offset,
		    pmbuf->data_len);

	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 woal send tdls data frame
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param extra_ies              A pointer to extra ie buffer
 * @param extra_ies_len          etra ie len
 * @param skb                   skb buffer
 *
 * @return                      0 -- success, otherwise fail
 */
static int woal_send_tdls_data_frame(struct wiphy *wiphy,
				     struct net_device *dev, t_u8 *peer,
				     u8 action_code, t_u8 dialog_token,
				     t_u16 status_code, const t_u8 *extra_ies,
				     size_t extra_ies_len)
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
	pmlan_buffer pmbuf = NULL;
	mlan_status status = MLAN_STATUS_SUCCESS;
	struct sk_buff *skb = NULL;
	int ret = 0;
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
	t_u32 index = 0;
#endif

	ENTER();

	skb = dev_alloc_skb(
		priv->extra_tx_head_len + MLAN_MIN_DATA_HEADER_LEN +
		sizeof(mlan_buffer) +
		max(sizeof(struct ieee80211_mgmt),
		    sizeof(struct ieee80211_tdls_data)) +
		50 + /* supported rates */
		sizeof(IEEEtypes_ExtCap_t) + /* ext capab */
		3 + /* Qos Info */
		sizeof(IEEEtypes_WmmParameter_t) + /*wmm ie*/
		sizeof(IEEEtypes_HTCap_t) + sizeof(IEEEtypes_2040BSSCo_t) +
		sizeof(IEEEtypes_HTInfo_t) + sizeof(IEEEtypes_VHTCap_t) +
		sizeof(IEEEtypes_VHTOprat_t) + sizeof(IEEEtypes_AID_t) +
		sizeof(IEEEtypes_HECap_t) + sizeof(IEEEtypes_HeOp_t) +
		extra_ies_len + sizeof(IEEEtypes_tdls_linkie));
	if (!skb)
		return -ENOMEM;

	skb_reserve(skb, MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) +
				 priv->extra_tx_head_len);

	woal_construct_tdls_data_frame(priv, peer, action_code, dialog_token,
				       status_code, skb);

	if (extra_ies_len)
		moal_memcpy_ext(priv->phandle, skb_put(skb, extra_ies_len),
				extra_ies, extra_ies_len, extra_ies_len);

	/* the TDLS link IE is always added last */
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
	case WLAN_TDLS_SETUP_CONFIRM:
	case WLAN_TDLS_TEARDOWN:
	case WLAN_TDLS_DISCOVERY_REQUEST:
		/* we are the initiator */
		woal_tdls_add_link_ie(priv, skb, priv->current_addr, peer,
				      priv->cfg_bssid);
		break;
	case WLAN_TDLS_SETUP_RESPONSE:
		/* we are the responder */
		woal_tdls_add_link_ie(priv, skb, peer, priv->current_addr,
				      priv->cfg_bssid);
		break;
	default:
		ret = -ENOTSUPP;
		goto fail;
	}

	/*
	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
	 * we should default to AC_VI.
	 */
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
	case WLAN_TDLS_SETUP_RESPONSE:
		skb_set_queue_mapping(skb, WMM_AC_BK);
		skb->priority = 2;
		break;
	default:
		skb_set_queue_mapping(skb, WMM_AC_VI);
		skb->priority = 5;
		break;
	}

	pmbuf = (mlan_buffer *)skb->head;
	memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer));
	pmbuf->bss_index = priv->bss_index;
	pmbuf->pdesc = skb;
	pmbuf->pbuf = skb->head + sizeof(mlan_buffer);

	pmbuf->data_offset = skb->data - (skb->head + sizeof(mlan_buffer));
	pmbuf->data_len = skb->len;
	pmbuf->priority = skb->priority;
	pmbuf->buf_type = MLAN_BUF_TYPE_DATA;

	DBG_HEXDUMP(MDAT_D, "TDLS data:", pmbuf->pbuf + pmbuf->data_offset,
		    pmbuf->data_len);

#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
	index = skb_get_queue_mapping(skb);
#endif
	status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);

	switch (status) {
	case MLAN_STATUS_PENDING:
		atomic_inc(&priv->phandle->tx_pending);
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
		atomic_inc(&priv->wmm_tx_pending[index]);
#endif
		queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
		/*delay 10 ms to guarantee the teardown/confirm frame can be
		 * sent out before disalbe/enable tdls link if we don't delay
		 * and return immediately, wpa_supplicant will call
		 * disalbe/enable tdls link this may cause tdls link
		 * disabled/enabled before teardown/confirm frame sent out */
		if (action_code == WLAN_TDLS_TEARDOWN ||
		    action_code == WLAN_TDLS_SETUP_CONFIRM)
			woal_sched_timeout(10);
		break;
	case MLAN_STATUS_SUCCESS:
		dev_kfree_skb(skb);
		break;
	case MLAN_STATUS_FAILURE:
	default:
		dev_kfree_skb(skb);
		ret = -ENOTSUPP;
		break;
	}

	LEAVE();
	return ret;
fail:
	dev_kfree_skb(skb);
	return ret;
}

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
/**
 * @brief Tx TDLS packet
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param peer_capability       peer capability
 * @param initiator             initiator
 * @param extra_ies              A pointer to extra ie buffer
 * @param extra_ies_len          etra ie len
 *
 * @return                      0 -- success, otherwise fail
 */
int woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
			    const t_u8 *peer, u8 action_code, t_u8 dialog_token,
			    t_u16 status_code, t_u32 peer_capability,
			    bool initiator, const t_u8 *extra_ies,
			    size_t extra_ies_len)
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
/**
 * @brief Tx TDLS packet
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param peer_capability       peer capability
 * @param extra_ies              A pointer to extra ie buffer
 * @param extra_ies_len          etra ie len
 *
 * @return                      0 -- success, otherwise fail
 */
int woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
			    const t_u8 *peer,
#else
			    t_u8 *peer,
#endif
			    u8 action_code, t_u8 dialog_token,
			    t_u16 status_code, t_u32 peer_capability,
			    const t_u8 *extra_ies, size_t extra_ies_len)
#else
/**
 * @brief Tx TDLS packet
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param peer                  A pointer to peer mac
 * @param action_code           tdls action code
 * @param dialog_token          dialog_token
 * @param status_code           status_code
 * @param extra_ies              A pointer to extra ie buffer
 * @param extra_ies_len          etra ie len
 *
 * @return                      0 -- success, otherwise fail
 */
int woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
			    t_u8 *peer, u8 action_code, t_u8 dialog_token,
			    t_u16 status_code, const t_u8 *extra_ies,
			    size_t extra_ies_len)
#endif
#endif
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
	int ret = 0;
	mlan_bss_info bss_info;

	ENTER();

	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) {
		LEAVE();
		return -ENOTSUPP;
	}
	/* make sure we are not in uAP mode and Go mode */
	if (priv->bss_type != MLAN_BSS_TYPE_STA) {
		LEAVE();
		return -ENOTSUPP;
	}

	/* check if AP prohited TDLS */
	memset(&bss_info, 0, sizeof(bss_info));
	if (MLAN_STATUS_SUCCESS !=
	    woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
		PRINTM(MERROR, "WLAN, %s: Failed to get bss info.\n", __func__);
	}
	if (IS_EXTCAP_TDLS_PROHIBITED(bss_info.ext_cap)) {
		PRINTM(MMSG, "TDLS is prohibited by AP\n");
		LEAVE();
		return -ENOTSUPP;
	}

	switch (action_code) {
	case TDLS_SETUP_REQUEST:
		woal_add_tdls_peer(priv, (t_u8 *)peer);
		PRINTM(MMSG,
		       "wlan: Send TDLS Setup Request to " MACSTR
		       " status_code=%d\n",
		       MAC2STR(peer), status_code);
		ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
						action_code, dialog_token,
						status_code, extra_ies,
						extra_ies_len);
		break;
	case TDLS_SETUP_RESPONSE:
		PRINTM(MMSG,
		       "wlan: Send TDLS Setup Response to " MACSTR
		       " status_code=%d\n",
		       MAC2STR(peer), status_code);
		ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
						action_code, dialog_token,
						status_code, extra_ies,
						extra_ies_len);
		break;
	case TDLS_SETUP_CONFIRM:
		PRINTM(MMSG,
		       "wlan: Send TDLS Confirm to " MACSTR " status_code=%d\n",
		       MAC2STR(peer), status_code);
		ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
						action_code, dialog_token,
						status_code, extra_ies,
						extra_ies_len);
		break;
	case TDLS_TEARDOWN:
		PRINTM(MMSG, "wlan: Send TDLS Tear down to " MACSTR "\n",
		       MAC2STR(peer));
		ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
						action_code, dialog_token,
						status_code, extra_ies,
						extra_ies_len);
		break;
	case TDLS_DISCOVERY_REQUEST:
		PRINTM(MMSG,
		       "wlan: Send TDLS Discovery Request to " MACSTR "\n",
		       MAC2STR(peer));
		ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
						action_code, dialog_token,
						status_code, extra_ies,
						extra_ies_len);
		break;
	case TDLS_DISCOVERY_RESPONSE:
		PRINTM(MMSG,
		       "wlan: Send TDLS Discovery Response to " MACSTR "\n",
		       MAC2STR(peer));
		ret = woal_send_tdls_action_frame(wiphy, dev, (t_u8 *)peer,
						  action_code, dialog_token,
						  status_code, extra_ies,
						  extra_ies_len);
		break;
	default:
		break;
	}

	LEAVE();
	return ret;
}

/**
 * @brief cfg80211_tdls_oper handler
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param peer                  tdls peer mac
 * @param oper                  tdls operation code
 *
 * @return                  	0 -- success, otherwise fail
 */
int woal_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
			    const u8 *peer,
#else
			    u8 *peer,
#endif
			    enum nl80211_tdls_operation oper)
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
	t_u8 action;
	int ret = 0;
	t_u8 event_buf[32];
	int custom_len = 0;

	ENTER();

	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
		return -ENOTSUPP;

	if (!(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
		return -ENOTSUPP;
	/* make sure we are in managed mode, and associated */
	if (priv->bss_type != MLAN_BSS_TYPE_STA)
		return -ENOTSUPP;

	PRINTM(MIOCTL, "wlan: TDLS peer=" MACSTR ", oper=%d\n", MAC2STR(peer),
	       oper);
	switch (oper) {
	case NL80211_TDLS_ENABLE_LINK:
		/*Configure TDLS link first*/
		woal_tdls_oper(priv, (u8 *)peer, WLAN_TDLS_CONFIG_LINK);
		woal_updata_peer_status(priv, (t_u8 *)peer,
					TDLS_SETUP_COMPLETE);
		PRINTM(MMSG, "wlan: TDLS_ENABLE_LINK: peer=" MACSTR "\n",
		       MAC2STR(peer));
		action = WLAN_TDLS_ENABLE_LINK;
		memset(event_buf, 0, sizeof(event_buf));
		custom_len = strlen(CUS_EVT_TDLS_CONNECTED);
		moal_memcpy_ext(priv->phandle, event_buf,
				CUS_EVT_TDLS_CONNECTED, custom_len,
				sizeof(event_buf));
		moal_memcpy_ext(priv->phandle, event_buf + custom_len, peer,
				ETH_ALEN, sizeof(event_buf) - custom_len);
		woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN);
		break;
	case NL80211_TDLS_DISABLE_LINK:
		woal_updata_peer_status(priv, (t_u8 *)peer, TDLS_NOT_SETUP);
		PRINTM(MMSG, "wlan: TDLS_DISABLE_LINK: peer=" MACSTR "\n",
		       MAC2STR(peer));
		action = WLAN_TDLS_DISABLE_LINK;
		memset(event_buf, 0, sizeof(event_buf));
		custom_len = strlen(CUS_EVT_TDLS_TEARDOWN);
		moal_memcpy_ext(priv->phandle, event_buf, CUS_EVT_TDLS_TEARDOWN,
				custom_len, sizeof(event_buf));
		moal_memcpy_ext(priv->phandle, event_buf + custom_len, peer,
				ETH_ALEN, sizeof(event_buf) - custom_len);
		woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN);
		break;
	case NL80211_TDLS_TEARDOWN:
	case NL80211_TDLS_SETUP:
	case NL80211_TDLS_DISCOVERY_REQ:
		return 0;

	default:
		return -ENOTSUPP;
	}
	ret = woal_tdls_oper(priv, (u8 *)peer, action);

	LEAVE();
	return ret;
}

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
/**
 * @brief tdls channel switch
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param addr                  A pointer to peer addr
 * @param oper_class            The operating class
 * @param chandef               A pointer to cfg80211_chan_def structure
 *
 * @return                      0 -- success, otherwise fail
 */
static int woal_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
					     struct net_device *dev,
					     const u8 *addr, u8 oper_class,
					     struct cfg80211_chan_def *chandef)
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
	mlan_ioctl_req *ioctl_req = NULL;
	mlan_ds_misc_cfg *misc = NULL;
	mlan_ds_misc_tdls_config *tdls_data = NULL;
	tdls_all_config *tdls_all_cfg = NULL;
	int ret = 0;
	mlan_status status = MLAN_STATUS_SUCCESS;
	mlan_bss_info bss_info;

	ENTER();

	/* check if AP prohited TDLS channel switch */
	memset(&bss_info, 0, sizeof(bss_info));
	if (MLAN_STATUS_SUCCESS !=
	    woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
		PRINTM(MERROR, "WLAN, %s: Failed to get bss info.\n", __func__);
	}
	if (IS_EXTCAP_TDLS_CHLSWITCHPROHIB(bss_info.ext_cap)) {
		PRINTM(MMSG, "TDLS Channel Switching is prohibited by AP\n");
		LEAVE();
		return -ENOTSUPP;
	}

	ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
	if (ioctl_req == NULL) {
		ret = -ENOMEM;
		goto done;
	}

	misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
	misc->sub_command = MLAN_OID_MISC_TDLS_OPER;
	ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
	ioctl_req->action = MLAN_ACT_SET;

	tdls_data = &misc->param.tdls_config;
	tdls_data->tdls_action = WLAN_TDLS_INIT_CHAN_SWITCH;

	tdls_all_cfg = (tdls_all_config *)tdls_data->tdls_data;
	moal_memcpy_ext(priv->phandle,
			tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr, addr,
			ETH_ALEN,
			sizeof(tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr));
	tdls_all_cfg->u.tdls_chan_switch.primary_channel =
		chandef->chan->hw_value;
	tdls_all_cfg->u.tdls_chan_switch.band = chandef->chan->band;
	tdls_all_cfg->u.tdls_chan_switch.regulatory_class = oper_class;

	status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
	if (status != MLAN_STATUS_SUCCESS) {
		PRINTM(MERROR, "TDLS channel switch request failed.\n");
		ret = -EFAULT;
		goto done;
	}

done:
	if (status != MLAN_STATUS_PENDING)
		kfree(ioctl_req);
	LEAVE();
	return ret;
}

/**
 * @brief tdls cancel channel switch
 *
 * @param wiphy                 A pointer to wiphy structure
 * @param dev                   A pointer to net_device structure
 * @param addr                  A pointer to peer addr
 *
 */
void woal_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
					      struct net_device *dev,
					      const u8 *addr)
{
	moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
	mlan_ioctl_req *ioctl_req = NULL;
	mlan_ds_misc_cfg *misc = NULL;
	mlan_ds_misc_tdls_config *tdls_data = NULL;
	mlan_status status = MLAN_STATUS_SUCCESS;

	ENTER();

	if (!priv || !priv->phandle) {
		PRINTM(MERROR, "priv or handle is null\n");
		status = MLAN_STATUS_FAILURE;
		goto done;
	}

	ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
	if (ioctl_req == NULL) {
		status = MLAN_STATUS_FAILURE;
		goto done;
	}

	misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
	misc->sub_command = MLAN_OID_MISC_TDLS_CONFIG;
	ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
	ioctl_req->action = MLAN_ACT_SET;

	tdls_data = &misc->param.tdls_config;
	tdls_data->tdls_action = WLAN_TDLS_STOP_CHAN_SWITCH;
	moal_memcpy_ext(priv->phandle, tdls_data->tdls_data, addr, ETH_ALEN,
			sizeof(tdls_data->tdls_data));

	status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
	if (status != MLAN_STATUS_SUCCESS)
		goto done;

	PRINTM(MIOCTL, "Tdls channel switch stop!\n");
done:
	if (status != MLAN_STATUS_PENDING)
		kfree(ioctl_req);

	LEAVE();
}
#endif
#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
	if (!(params->sta_flags_set & MBIT(NL80211_STA_FLAG_TDLS_PEER)))
		goto done;
	/* make sure we are in connected mode */
	if ((priv->bss_type != MLAN_BSS_TYPE_STA) ||
	    (priv->media_connected == MFALSE)) {
		ret = -ENOTSUPP;
		goto done;
	}
	PRINTM(MMSG, "wlan: TDLS add peer station, address =" MACSTR "\n",
	       MAC2STR(mac));
	ret = woal_tdls_oper(priv, (u8 *)mac, WLAN_TDLS_CREATE_LINK);
done:
	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 = NULL;
	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;
	}
	assoc_rsp = kmalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_KERNEL);
	if (!assoc_rsp) {
		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, (int)ftie->ie_len, MOBILITY_DOMAIN);
	if (!md_ie) {
		PRINTM(MERROR, "No Mobility domain IE\n");
		kfree(assoc_rsp);
		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) {
		kfree(assoc_rsp);
		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");
		kfree(assoc_rsp);
		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)
#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_13)
			roam_info.links[0].bssid = priv->cfg_bssid;
#else
			roam_info.bssid = priv->cfg_bssid;
#endif
			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;
		}
	}
	kfree(assoc_rsp);
	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));
	}
	if (priv->sinfo)
		memset(priv->sinfo, 0, sizeof(struct station_info));
	else
		priv->sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
}

/**
 * @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;
	priv->assoc_bss = NULL;
	if (priv->sinfo) {
		kfree(priv->sinfo);
		priv->sinfo = NULL;
	}
	LEAVE();
}

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
/**
 * @brief Build new roaming connect ie for okc
 *
 * @param priv            A pointer to moal_private
 * @param entry           A pointer to pmksa_entry
 **/
static 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 =
			MIN(INT_MAX, 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;
}
#endif

/**
 * @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 = NULL;
	char rssi_low[10];
	int ret = 0;
	mlan_ds_misc_assoc_rsp *assoc_rsp = NULL;
	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;
	}
	ssid_bssid = kmalloc(sizeof(mlan_ssid_bssid), GFP_KERNEL);
	if (!ssid_bssid) {
		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");
		kfree(ssid_bssid);
		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)
#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_13)
		roam_info.links[0].bssid = priv->cfg_bssid;
#else
		roam_info.bssid = priv->cfg_bssid;
#endif
		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:
	kfree(ssid_bssid);
	/* config rssi low threshold again */
	priv->last_event = 0;
	priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
	snprintf(rssi_low, sizeof(rssi_low), "%d", priv->rssi_low);
	if (MLAN_STATUS_FAILURE ==
	    woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT))
		PRINTM(MERROR, "set_rssi_low_threshold fail\n");
	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 CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
	if (params->ext_capab_len)
		req_len += sizeof(MrvlIEtypesHeader_t) + params->ext_capab_len;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.supported_rates_len)
		req_len += sizeof(MrvlIEtypesHeader_t) +
			   params->link_sta_params.supported_rates_len;
#else
	if (params->supported_rates_len)
		req_len += sizeof(MrvlIEtypesHeader_t) +
			   params->supported_rates_len;
#endif
	if (params->uapsd_queues || params->max_sp)
		req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(qosinfo);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.ht_capa)
#else
	if (params->ht_capa)
#endif
		req_len += sizeof(MrvlIEtypesHeader_t) +
			   sizeof(struct ieee80211_ht_cap);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.vht_capa)
#else
	if (params->vht_capa)
#endif
		req_len += sizeof(MrvlIEtypesHeader_t) +
			   sizeof(struct ieee80211_vht_cap);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.opmode_notif_used)
		req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(u8);
#else
	if (params->opmode_notif_used)
		req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(u8);
#endif
#endif

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.he_capa_len)
		req_len += sizeof(MrvlExtIEtypesHeader_t) +
			   params->link_sta_params.he_capa_len;
#else
	if (params->he_capa_len)
		req_len += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
#endif
#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 CFG80211_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 CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.supported_rates_len) {
#else
	if (params->supported_rates_len) {
#endif
		tlv = (MrvlIEtypes_Data_t *)pos;
		tlv->header.type = SUPPORTED_RATES;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
		tlv->header.len = params->link_sta_params.supported_rates_len;
#else
		tlv->header.len = params->supported_rates_len;
#endif
		moal_memcpy_ext(priv->phandle, tlv->data,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
				params->link_sta_params.supported_rates,
				tlv->header.len,
#else
				params->supported_rates, tlv->header.len,
#endif
				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 CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.ht_capa) {
#else
	if (params->ht_capa) {
#endif
		tlv = (MrvlIEtypes_Data_t *)pos;
		tlv->header.type = HT_CAPABILITY;
		tlv->header.len = sizeof(struct ieee80211_ht_cap);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
		moal_memcpy_ext(priv->phandle, tlv->data,
				params->link_sta_params.ht_capa,
#else
		moal_memcpy_ext(priv->phandle, tlv->data, params->ht_capa,
#endif
				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 CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.vht_capa) {
#else
	if (params->vht_capa) {
#endif
		tlv = (MrvlIEtypes_Data_t *)pos;
		tlv->header.type = VHT_CAPABILITY;
		tlv->header.len = sizeof(struct ieee80211_vht_cap);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
		moal_memcpy_ext(priv->phandle, tlv->data,
				params->link_sta_params.vht_capa,
#else
		moal_memcpy_ext(priv->phandle, tlv->data, params->vht_capa,
#endif
				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 CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
	if (params->link_sta_params.opmode_notif_used) {
#else
	if (params->opmode_notif_used) {
#endif
		tlv = (MrvlIEtypes_Data_t *)pos;
		tlv->header.type = OPER_MODE_NTF;
		tlv->header.len = sizeof(u8);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
		moal_memcpy_ext(priv->phandle, tlv->data,
				&params->link_sta_params.opmode_notif,
#else
		moal_memcpy_ext(priv->phandle, tlv->data, &params->opmode_notif,
#endif
				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(6, 0, 0)
	if (params->link_sta_params.he_capa_len) {
		ext_tlv = (MrvlExtIEtypes_Data_t *)pos;
		ext_tlv->header.type = EXTENSION;
		ext_tlv->header.len =
			params->link_sta_params.he_capa_len + sizeof(u8);
		ext_tlv->header.ext_id = HE_CAPABILITY;
		moal_memcpy_ext(priv->phandle, ext_tlv->data,
				(u8 *)params->link_sta_params.he_capa,
				params->link_sta_params.he_capa_len,
				params->link_sta_params.he_capa_len);
		pos += sizeof(MrvlExtIEtypesHeader_t) +
		       params->link_sta_params.he_capa_len;
		bss->param.sta_info.tlv_len +=
			sizeof(MrvlExtIEtypesHeader_t) +
			params->link_sta_params.he_capa_len;
		tlv = (MrvlIEtypes_Data_t *)pos;
	}

#elif 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);
#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE
	if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) {
		struct station_info *sinfo = NULL;
		sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
		if (sinfo) {
			cfg80211_new_sta(dev, mac, sinfo, GFP_KERNEL);
			kfree(sinfo);
		}
	}
#endif
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[100];
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf;
	ENTER();

	memset(frame_buf, 0, sizeof(frame_buf));
	mgmt->frame_control = (__force __le16)IEEE80211_STYPE_DEAUTH;
	mgmt->duration = 0;
	mgmt->seq_ctrl = 0;
	mgmt->u.deauth.reason_code = (__force __le16)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 CFG80211_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;
	enum ieee80211_band band;

	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;
	}
	if (priv->phandle->country_code[0] && priv->phandle->country_code[1]) {
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
		if (priv->phandle->params.cntry_txpwr == CNTRY_RGPOWER_MODE &&
		    !priv->phandle->params.txpwrlimit_cfg)
			queue_work(priv->phandle->evt_workqueue,
				   &priv->phandle->regulatory_work);
#endif
		band = priv->phandle->band;
		priv->phandle->band = IEEE80211_BAND_2GHZ;
		woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
		if (priv->phandle->fw_bands & BAND_A) {
			priv->phandle->band = IEEE80211_BAND_5GHZ;
			woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
		}
		priv->phandle->band = band;
	}
	LEAVE();
	return ret;
}

/**
 * @brief Initialize the wiphy
 *
 * @param priv            A pointer to moal_private structure
 * @param wiphy 		  A pointer to structure wiphy
 * @param fw_info         A pointer to mlan_fw_info
 * @param wait_option     Wait option
 * @return                MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
static mlan_status woal_cfg80211_init_wiphy(moal_private *priv,
					    struct wiphy *wiphy,
					    mlan_fw_info *fw_info,
					    t_u8 wait_option)
{
	mlan_status ret = MLAN_STATUS_SUCCESS;
	int retry_count, rts_thr, frag_thr;
	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
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
	t_u16 enable = 0;
#endif
	int mcs_supp = 0;

	ENTER();
	/* 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;
		}
		woal_cfg80211_setup_ht_cap(
			&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, hw_dev_cap,
			cfg_11n->param.supported_mcs_set,
			fw_info->hw_mpdu_density);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
		woal_cfg80211_setup_vht_cap(
			priv, &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap);
#endif
#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,
			fw_info->hw_mpdu_density);
#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 */
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
	if (priv->phandle->params.drv_mode & DRV_MODE_DFS) {
		radio->param.ant_cfg.tx_antenna = 0x101;
		radio->param.ant_cfg.rx_antenna = 0x101;
	}
#endif
	if (IS_CARD9098(priv->phandle->card_type) ||
	    IS_CARD9097(priv->phandle->card_type) ||
	    IS_CARDIW624(priv->phandle->card_type)) {
		woal_cfg80211_notify_antcfg(priv, wiphy, radio);
	}
	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;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
	/* Enable multi-channel by default if multi-channel is supported */
	if (cfg80211_iface_comb_ap_sta.num_different_channels > 1) {
		if (priv->phandle->card_info->drcs &&
		    moal_extflg_isset(priv->phandle, EXT_CFG80211_DRCS)) {
			enable = 1;
			ret = woal_mc_policy_cfg(priv, &enable, wait_option,
						 MLAN_ACT_SET);
		}
	}
#endif

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
 */
static 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);
	wiphy->interface_modes |= MBIT(NL80211_IFTYPE_MONITOR);

#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) {
		wiphy->bands[IEEE80211_BAND_5GHZ] =
			woal_setup_wiphy_bands(IEEE80211_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)) {
		wiphy->bands[IEEE80211_BAND_2GHZ] =
			woal_setup_wiphy_bands(IEEE80211_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)
	if ((moal_extflg_isset(priv->phandle, EXT_CFG80211_DRCS) &&
	     priv->phandle->card_info->drcs) ||
	    IS_CARD9098(priv->phandle->card_type)) {
		cfg80211_iface_comb_ap_sta.num_different_channels = 2;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
		cfg80211_iface_comb_ap_sta.radar_detect_widths = 0;
#endif
	}
	/* 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 (priv->phandle->params.sched_scan) {
#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, 18, 0)
	wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
	wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
	wiphy->flags |=
		WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
	wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
#endif

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
#define WLAN_EXT_FEATURE_DFS_OFFLOAD 25
	if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) {
		if (NUM_NL80211_EXT_FEATURES > WLAN_EXT_FEATURE_DFS_OFFLOAD) {
			PRINTM(MCMND,
			       "wlan: Set NL80211_EXT_FEATURE_DFS_OFFLOAD\n");
			wiphy_ext_feature_set(wiphy,
					      WLAN_EXT_FEATURE_DFS_OFFLOAD);
		}
	}
#endif

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
	if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
		wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
#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, 11, 0)
	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA);
	wiphy_ext_feature_set(wiphy,
			      NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED);
#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

#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
	if (fw_info.fw_beacon_prot) {
		wiphy_ext_feature_set(wiphy,
				      NL80211_EXT_FEATURE_BEACON_PROTECTION);
		wiphy_ext_feature_set(
			wiphy, NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT);
	}
#endif
#ifdef UAP_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
	if (priv->phandle->params.drv_mode & DRV_MODE_DFS) {
		wiphy_ext_feature_set(wiphy,
				      NL80211_EXT_FEATURE_RADAR_BACKGROUND);
		woal_cfg80211_ops.set_radar_background =
			woal_cfg80211_set_radar_background;
	}
#endif
#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
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
	/*REGULATORY_IGNORE_STALE_KICKOFF: the regulatory core will _not_ make
	 * sure all interfaces on this wiphy reside on allowed channels. If this
	 * flag is not set, upon a regdomain change, the interfaces are given a
	 * grace period (currently 60 seconds) to disconnect or move to an
	 * allowed channel.*/
	wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
#endif

	memset(&priv->phandle->country_code, 0,
	       sizeof(priv->phandle->country_code));
	priv->phandle->dfs_region = NXP_DFS_UNKNOWN;

	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

	woal_cfg80211_init_wiphy(priv, wiphy, &fw_info, MOAL_IOCTL_WAIT);
	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) {
		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);
			queue_work(priv->phandle->evt_workqueue,
				   &priv->phandle->regulatory_work);
		}
	}
#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;
	return ret;
err_wiphy:
	if (wiphy) {
		woal_cfg80211_free_bands(wiphy);
		wiphy_free(wiphy);
	}
	LEAVE();
	return ret;
}
