// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright (c) 2021 MediaTek Inc.
 */

/*
 ** Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/os/linux
 *      /gl_init.c#11
 */

/*! \file   gl_init.c
 *    \brief  Main routines of Linux driver
 *
 *    This file contains the main routines of Linux driver for MediaTek Inc.
 *    802.11 Wireless LAN Adapters.
 */


/*******************************************************************************
 *                         C O M P I L E R   F L A G S
 *******************************************************************************
 */

/*******************************************************************************
 *                    E X T E R N A L   R E F E R E N C E S
 *******************************************************************************
 */
#include "gl_os.h"
#include "debug.h"
#include "wlan_lib.h"
#include "gl_wext.h"
#include "gl_cfg80211.h"
#include "precomp.h"
#if CFG_SUPPORT_AGPS_ASSIST
#include "gl_kal.h"
#endif
#include "gl_vendor.h"
#if CFG_THERMAL_API_SUPPORT
#include "mtk_ts_wmt.h"
#endif

#if CFG_POWER_OFF_CTRL_SUPPORT
#include <linux/reboot.h>
#endif

/*******************************************************************************
 *                              C O N S T A N T S
 *******************************************************************************
 */
/* #define MAX_IOREQ_NUM   10 */
struct semaphore g_halt_sem;
int g_u4HaltFlag;
atomic_t g_wlanProbing;
atomic_t g_wlanRemoving;

struct wireless_dev *gprWdev;
#if CFG_THERMAL_API_SUPPORT
u_int8_t g_fgIsWifiEnabled = FALSE;
static int mtk_wcn_temp_query_ctrl(void);
#endif

#if IS_ENABLED(CFG_CCN7_SAP_EASYMESH)
#define NETLINK_OSS_KERNEL 25
struct sock *nl_sk;
#endif/* IS_ENABLED(CFG_CCN7_SAP_EASYMESH) */

/*******************************************************************************
 *                             D A T A   T Y P E S
 *******************************************************************************
 */
/* Tasklet mechanism is like buttom-half in Linux. We just want to
 * send a signal to OS for interrupt defer processing. All resources
 * are NOT allowed reentry, so txPacket, ISR-DPC and ioctl must avoid preempty.
 */
struct WLANDEV_INFO {
	struct net_device *prDev;
};

/*******************************************************************************
 *                            P U B L I C   D A T A
 *******************************************************************************
 */

MODULE_AUTHOR(NIC_AUTHOR);
MODULE_DESCRIPTION(NIC_DESC);
#if KERNEL_VERSION(5, 12, 0) > LINUX_VERSION_CODE
MODULE_SUPPORTED_DEVICE(NIC_NAME);
#endif

/* MODULE_LICENSE("MTK Propietary"); */
MODULE_LICENSE("Dual BSD/GPL");

#ifdef CFG_DRIVER_INF_NAME_CHANGE
char *gprifnamesta = "";
char *gprifnamep2p = "";
char *gprifnameap = "";
module_param_named(sta, gprifnamesta, charp, 0000);
module_param_named(p2p, gprifnamep2p, charp, 0000);
module_param_named(ap, gprifnameap, charp, 0000);
#endif /* CFG_DRIVER_INF_NAME_CHANGE */

/* NIC interface name */
#define NIC_INF_NAME    "wlan%d"

#ifdef CFG_DRIVER_INF_NAME_CHANGE
/* Kernel IFNAMESIZ is 16, we use 5 in case some protocol might auto gen
 * interface name,
 */
/* in that case, the interface name might have risk of over kernel's IFNAMESIZ
 */
#define CUSTOM_IFNAMESIZ 5
#endif /* CFG_DRIVER_INF_NAME_CHANGE */

#if CFG_SUPPORT_SNIFFER
#define NIC_MONITOR_INF_NAME	"radiotap%d"
#endif

uint8_t aucDebugModule[DBG_MODULE_NUM];
uint32_t au4LogLevel[ENUM_WIFI_LOG_MODULE_NUM] = {ENUM_WIFI_LOG_LEVEL_DEFAULT};

/* 4 2007/06/26, mikewu, now we don't use this, we just fix the number of wlan
 *               device to 1
 */
static struct WLANDEV_INFO
	arWlanDevInfo[CFG_MAX_WLAN_DEVICES] = { {0} };

static uint32_t
u4WlanDevNum;	/* How many NICs coexist now */

#if CFG_SUPPORT_CAL_RESULT_BACKUP_TO_HOST
u_int8_t	g_fgIsCalDataBackuped = FALSE;
#endif

/* 20150205 added work queue for sched_scan to avoid cfg80211 stop schedule scan
 *          dead loack
 */
struct delayed_work sched_workq;

#if CFG_SUPPORT_CFG80211_AUTH
#if CFG_SUPPORT_CFG80211_QUEUE
struct delayed_work cfg80211_workq;
#endif
#endif

#define CFG_EEPRM_FILENAME    "EEPROM"
#define FILE_NAME_MAX     64

#if (CFG_EFUSE_BUFFER_MODE_DELAY_CAL == 1)
static uint8_t *apucEepromName[] = {
	(uint8_t *) CFG_EEPRM_FILENAME "_MT",
	NULL
};
#endif

/*  For DTV Ref project -> Default enable */
#if CFG_DC_USB_WOW_CALLBACK || CFG_POWER_OFF_CTRL_SUPPORT

/* Register  DC  wow callback */
int kalDcSetWow(void)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	uint32_t count = 0;
	uint8_t ret = 0;

	if (!gprWdev) {
		DBGLOG(INIT, ERROR, "gprWdev == NULL\n");
		return -ENODEV;
	}
	prGlueInfo = (struct GLUE_INFO *) wiphy_priv(gprWdev->wiphy);

	if (!prGlueInfo) {
		DBGLOG(INIT, ERROR, "prGlueInfo == NULL\n");
		return -ENODEV;
	}

	if (!prGlueInfo->prAdapter) {
		DBGLOG(INIT, ERROR, "prGlueInfo->prAdapter == NULL\n");
		return -ENODEV;
	}

	do {
#if !CFG_ENABLE_WAKE_LOCK
		/* AIS flow: disassociation if wow_en=0 */
		/* cancel scan report done event */
		aisPreSuspendFlow(prGlueInfo);

		/* It need to add flow to deactive P2P (GC/GO) link during
		 * suspend flow. Otherwise, MT7668 would fail
		 * to enter deep sleep.
		 */
		p2pProcessPreSuspendFlow(prGlueInfo->prAdapter);
#endif
		if (kalHaltTryLock())
			break;

		if (kalIsHalted()) {
			kalHaltUnlock();
			break;
		}

		if (prGlueInfo && prGlueInfo->prAdapter) {
			set_bit(SUSPEND_FLAG_FOR_WAKEUP_REASON,
				&prGlueInfo->prAdapter->ulSuspendFlag);
			set_bit(SUSPEND_FLAG_CLEAR_WHEN_RESUME,
				&prGlueInfo->prAdapter->ulSuspendFlag);
		}
	} while (0);

	if (kalIsResetting()) {
		DBGLOG(HAL, WARN, "Chip resetting, skip\n");
		return -1;
	}

	/* Stop upper layers calling the device hard_start_xmit routine. */
	netif_tx_stop_all_queues(prGlueInfo->prDevHandler);

	wlanWaitCfg80211SuspendDone(prGlueInfo);

	/* change to non-READY state to block cfg80211 ops */
	glUsbSetState(&prGlueInfo->rHifInfo, USB_STATE_PRE_SUSPEND_START);

	wlanSuspendPmHandle(prGlueInfo);
	/* wifi disconnected when DC off, fouce set suspend mode*/
	DBGLOG(REQ, INFO, "Set suspend mode 1\n");
	prGlueInfo->fgIsInSuspendMode = 1;
	wlanSetSuspendMode(prGlueInfo, 1);
	p2pSetSuspendMode(prGlueInfo, 1);

	halPreSuspendCmd(prGlueInfo->prAdapter);

	while (prGlueInfo->rHifInfo.state != USB_STATE_PRE_SUSPEND_DONE) {
		if (count > 500) {
			DBGLOG(HAL, ERROR, "pre_suspend timeout\n");
				ret = -EFAULT;
				break;
		}
		mdelay(2);
		count++;
	}

	glUsbSetState(&prGlueInfo->rHifInfo, USB_STATE_SUSPEND);
	halDisableInterrupt(prGlueInfo->prAdapter);
	halTxCancelAllSending(prGlueInfo->prAdapter);

	wlanReleaseAllTxCmdQueue(prGlueInfo->prAdapter);

	DBGLOG(INIT, EVENT, "Wow disable suspend Wifi\n");

	return ret;
}

#endif

int CFG80211_Suspend(struct wiphy *wiphy,
		     struct cfg80211_wowlan *wow)
{
	DBGLOG(INIT, INFO, "CFG80211 suspend CB\n");

	return 0;
}

int CFG80211_Resume(struct wiphy *wiphy)
{
	DBGLOG(INIT, INFO, "CFG80211 resume CB\n");

	return 0;
}

/*******************************************************************************
 *                           P R I V A T E   D A T A
 *******************************************************************************
 */

#define CHAN2G(_channel, _freq, _flags)		\
{						\
	.band               = KAL_BAND_2GHZ,	\
	.center_freq        = (_freq),		\
	.hw_value           = (_channel),	\
	.flags              = (_flags),		\
	.max_antenna_gain   = 0,		\
	.max_power          = 30,		\
}
static struct ieee80211_channel mtk_2ghz_channels[] = {
	CHAN2G(1, 2412, 0),
	CHAN2G(2, 2417, 0),
	CHAN2G(3, 2422, 0),
	CHAN2G(4, 2427, 0),
	CHAN2G(5, 2432, 0),
	CHAN2G(6, 2437, 0),
	CHAN2G(7, 2442, 0),
	CHAN2G(8, 2447, 0),
	CHAN2G(9, 2452, 0),
	CHAN2G(10, 2457, 0),
	CHAN2G(11, 2462, 0),
	CHAN2G(12, 2467, 0),
	CHAN2G(13, 2472, 0),
	CHAN2G(14, 2484, 0),
};

#define CHAN5G(_channel, _flags)					\
{									\
	.band               = KAL_BAND_5GHZ,				\
	.center_freq        =						\
		(((_channel >= 182) && (_channel <= 196)) ?		\
		(4000 + (5 * (_channel))) : (5000 + (5 * (_channel)))),	\
	.hw_value           = (_channel),				\
	.flags              = (_flags),					\
	.max_antenna_gain   = 0,					\
	.max_power          = 30,					\
}
static struct ieee80211_channel mtk_5ghz_channels[] = {
	CHAN5G(36, 0), CHAN5G(40, 0),
	CHAN5G(44, 0), CHAN5G(48, 0),
	CHAN5G(52, 0), CHAN5G(56, 0),
	CHAN5G(60, 0), CHAN5G(64, 0),
	CHAN5G(100, 0), CHAN5G(104, 0),
	CHAN5G(108, 0), CHAN5G(112, 0),
	CHAN5G(116, 0), CHAN5G(120, 0),
	CHAN5G(124, 0), CHAN5G(128, 0),
	CHAN5G(132, 0), CHAN5G(136, 0),
	CHAN5G(140, 0), CHAN5G(144, 0),
	CHAN5G(149, 0), CHAN5G(153, 0),
	CHAN5G(157, 0), CHAN5G(161, 0),
	CHAN5G(165, 0),

};

#define RATETAB_ENT(_rate, _rateid, _flags)	\
{						\
	.bitrate    = (_rate),			\
	.hw_value   = (_rateid),		\
	.flags      = (_flags),			\
}

/* for cfg80211 - rate table */
static struct ieee80211_rate mtk_rates[] = {
	RATETAB_ENT(10, 0x1000, 0),
	RATETAB_ENT(20, 0x1001, 0),
	RATETAB_ENT(55, 0x1002, 0),
	RATETAB_ENT(110, 0x1003, 0),	/* 802.11b */
	RATETAB_ENT(60, 0x2000, 0),
	RATETAB_ENT(90, 0x2001, 0),
	RATETAB_ENT(120, 0x2002, 0),
	RATETAB_ENT(180, 0x2003, 0),
	RATETAB_ENT(240, 0x2004, 0),
	RATETAB_ENT(360, 0x2005, 0),
	RATETAB_ENT(480, 0x2006, 0),
	RATETAB_ENT(540, 0x2007, 0),	/* 802.11a/g */
};

#define mtk_a_rates         (mtk_rates + 4)
#define mtk_a_rates_size    (ARRAY_SIZE(mtk_rates) - 4)
#define mtk_g_rates         (mtk_rates + 0)
#define mtk_g_rates_size    (ARRAY_SIZE(mtk_rates) - 0)

#define WLAN_MCS_INFO						\
{								\
	.rx_mask        = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0},	\
	.rx_highest     = 0,					\
	.tx_params      = IEEE80211_HT_MCS_TX_DEFINED,		\
}

#define WLAN_VHT_MCS_INFO					\
{								\
	.rx_mcs_map     = 0xFFFA,				\
	.rx_highest     = cpu_to_le16(867),			\
	.tx_mcs_map     = 0xFFFA,				\
	.tx_highest     = cpu_to_le16(867),			\
}


#define WLAN_HT_CAP						\
{								\
	.ht_supported   = true,					\
	.cap            = IEEE80211_HT_CAP_SUP_WIDTH_20_40	\
			| IEEE80211_HT_CAP_SM_PS		\
			| IEEE80211_HT_CAP_GRN_FLD		\
			| IEEE80211_HT_CAP_SGI_20		\
			| IEEE80211_HT_CAP_SGI_40,		\
	.ampdu_factor   = IEEE80211_HT_MAX_AMPDU_64K,		\
	.ampdu_density  = IEEE80211_HT_MPDU_DENSITY_NONE,	\
	.mcs            = WLAN_MCS_INFO,			\
}

#define WLAN_VHT_CAP							\
{									\
	.vht_supported  = true,						\
	.cap            = IEEE80211_VHT_CAP_RXLDPC			\
			| IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK	\
			| IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454	\
			| IEEE80211_VHT_CAP_RXLDPC			\
			| IEEE80211_VHT_CAP_SHORT_GI_80			\
			| IEEE80211_VHT_CAP_TXSTBC			\
			| IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE	\
			| IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE,	\
	.vht_mcs        = WLAN_VHT_MCS_INFO,				\
}

/* public for both Legacy Wi-Fi / P2P access */
struct ieee80211_supported_band mtk_band_2ghz = {
	.band = KAL_BAND_2GHZ,
	.channels = mtk_2ghz_channels,
	.n_channels = ARRAY_SIZE(mtk_2ghz_channels),
	.bitrates = mtk_g_rates,
	.n_bitrates = mtk_g_rates_size,
	.ht_cap = WLAN_HT_CAP,
};

/* public for both Legacy Wi-Fi / P2P access */
struct ieee80211_supported_band mtk_band_5ghz = {
	.band = KAL_BAND_5GHZ,
	.channels = mtk_5ghz_channels,
	.n_channels = ARRAY_SIZE(mtk_5ghz_channels),
	.bitrates = mtk_a_rates,
	.n_bitrates = mtk_a_rates_size,
	.ht_cap = WLAN_HT_CAP,
	.vht_cap = WLAN_VHT_CAP,
};

const uint32_t mtk_cipher_suites[] = {
	/* keep WEP first, it may be removed below */
	WLAN_CIPHER_SUITE_WEP40,
	WLAN_CIPHER_SUITE_WEP104,
	WLAN_CIPHER_SUITE_TKIP,
	WLAN_CIPHER_SUITE_CCMP,

	/* keep last -- depends on hw flags! */
	WLAN_CIPHER_SUITE_AES_CMAC,
	WLAN_CIPHER_SUITE_NO_GROUP_ADDR
};

#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
const uint32_t mtk_akm_suites[] = {
#if CFG_SUPPORT_802_11R
	WLAN_AKM_SUITE_FT_8021X,
	WLAN_AKM_SUITE_FT_PSK,
#endif
#if CFG_SUPPORT_802_11W
	WLAN_AKM_SUITE_8021X_SHA256,
	WLAN_AKM_SUITE_PSK_SHA256,
#endif
#if CFG_SUPPORT_OWE
	WLAN_AKM_SUITE_OWE,
#endif
#if CFG_SUPPORT_DPP
	WLAN_AKM_SUITE_DPP,
#endif
#if CFG_SUPPORT_CFG80211_AUTH
	WLAN_AKM_SUITE_SAE,
#endif
	WLAN_AKM_SUITE_8021X,
	WLAN_AKM_SUITE_PSK,
	WLAN_AKM_SUITE_8021X_SUITE_B
};
#endif

#if (CFG_ENABLE_UNIFY_WIPHY == 0)
static struct cfg80211_ops mtk_wlan_ops = {
	.suspend = mtk_cfg80211_suspend,
	.resume = mtk_cfg80211_resume,
	.change_virtual_intf = mtk_cfg80211_change_iface,
	.add_key = mtk_cfg80211_add_key,
	.get_key = mtk_cfg80211_get_key,
	.del_key = mtk_cfg80211_del_key,
	.set_default_key = mtk_cfg80211_set_default_key,
	.get_station = mtk_cfg80211_get_station,
#if CFG_SUPPORT_TDLS
	.change_station = mtk_cfg80211_change_station,
	.add_station = mtk_cfg80211_add_station,
	.del_station = mtk_cfg80211_del_station,
#endif
	.scan = mtk_cfg80211_scan,
#if KERNEL_VERSION(4, 5, 0) <= CFG80211_VERSION_CODE
	.abort_scan = mtk_cfg80211_abort_scan,
#endif
#if !CFG_SUPPORT_CFG80211_AUTH
	.connect = mtk_cfg80211_connect,
	.disconnect = mtk_cfg80211_disconnect,
#endif
#if CFG_SUPPORT_CFG80211_AUTH
	.deauth = mtk_cfg80211_deauth,
#endif
	.join_ibss = mtk_cfg80211_join_ibss,
	.leave_ibss = mtk_cfg80211_leave_ibss,
	.set_power_mgmt = mtk_cfg80211_set_power_mgmt,
	.set_pmksa = mtk_cfg80211_set_pmksa,
	.del_pmksa = mtk_cfg80211_del_pmksa,
	.flush_pmksa = mtk_cfg80211_flush_pmksa,
#if CONFIG_SUPPORT_GTK_REKEY
	.set_rekey_data = mtk_cfg80211_set_rekey_data,
#endif
#if CFG_SUPPORT_CFG80211_AUTH
	.auth = mtk_cfg80211_auth,
#endif
	.assoc = mtk_cfg80211_assoc,

	/* Action Frame TX/RX */
	.remain_on_channel = mtk_cfg80211_remain_on_channel,
	.cancel_remain_on_channel = mtk_cfg80211_cancel_remain_on_channel,
	.mgmt_tx = mtk_cfg80211_mgmt_tx,
	/* .mgmt_tx_cancel_wait        = mtk_cfg80211_mgmt_tx_cancel_wait, */
#if KERNEL_VERSION(5, 8, 0) <= CFG80211_VERSION_CODE
	.update_mgmt_frame_registrations = mtk_cfg_mgmt_frame_update,
#else
	.mgmt_frame_register = mtk_cfg80211_mgmt_frame_register,
#endif

#ifdef CONFIG_NL80211_TESTMODE
	.testmode_cmd = mtk_cfg80211_testmode_cmd,
#endif
#if CFG_SUPPORT_SCHED_SCAN
	.sched_scan_start = mtk_cfg80211_sched_scan_start,
	.sched_scan_stop = mtk_cfg80211_sched_scan_stop,
#endif /* CFG_SUPPORT_SCHED_SCAN */
#if CFG_SUPPORT_TDLS
	.tdls_oper = mtk_cfg80211_tdls_oper,
	.tdls_mgmt = mtk_cfg80211_tdls_mgmt,
#endif
	.update_ft_ies = mtk_cfg80211_update_ft_ies,
};
#else /* CFG_ENABLE_UNIFY_WIPHY */
static struct cfg80211_ops mtk_cfg_ops = {
	.add_virtual_intf = mtk_cfg_add_iface,
	.del_virtual_intf = mtk_cfg_del_iface,
	.change_virtual_intf = mtk_cfg_change_iface,
	.add_key = mtk_cfg_add_key,
	.get_key = mtk_cfg_get_key,
	.del_key = mtk_cfg_del_key,
	.set_default_mgmt_key = mtk_cfg_set_default_mgmt_key,
	.set_default_key = mtk_cfg_set_default_key,
	.get_station = mtk_cfg_get_station,
#if CFG_SUPPORT_TDLS
	.change_station = mtk_cfg_change_station,
	.add_station = mtk_cfg_add_station,
	.tdls_oper = mtk_cfg_tdls_oper,
	.tdls_mgmt = mtk_cfg_tdls_mgmt,
#endif
	.del_station = mtk_cfg_del_station,	/* AP/P2P use this function */
	.scan = mtk_cfg_scan,
#if KERNEL_VERSION(4, 5, 0) <= CFG80211_VERSION_CODE
	.abort_scan = mtk_cfg_abort_scan,
#endif
#if CFG_SUPPORT_SCHED_SCAN
	.sched_scan_start = mtk_cfg_sched_scan_start,
	.sched_scan_stop = mtk_cfg_sched_scan_stop,
#endif /* CFG_SUPPORT_SCHED_SCAN */
#if !CFG_SUPPORT_CFG80211_AUTH
	.connect = mtk_cfg80211_connect,
	.disconnect = mtk_cfg80211_disconnect,
#endif
#if CFG_SUPPORT_CFG80211_AUTH
	.deauth = mtk_cfg_deauth,
#endif
	.join_ibss = mtk_cfg_join_ibss,
	.leave_ibss = mtk_cfg_leave_ibss,
	.set_power_mgmt = mtk_cfg_set_power_mgmt,
	.set_pmksa = mtk_cfg_set_pmksa,
	.del_pmksa = mtk_cfg_del_pmksa,
	.flush_pmksa = mtk_cfg_flush_pmksa,
#if CONFIG_SUPPORT_GTK_REKEY
	.set_rekey_data = mtk_cfg_set_rekey_data,
#endif
	.suspend = mtk_cfg_suspend,
	.resume = mtk_cfg_resume,
#if CFG_SUPPORT_CFG80211_AUTH
	.auth = mtk_cfg_auth,
#endif
	.assoc = mtk_cfg_assoc,

	/* Action Frame TX/RX */
	.remain_on_channel = mtk_cfg_remain_on_channel,
	.cancel_remain_on_channel = mtk_cfg_cancel_remain_on_channel,
	.mgmt_tx = mtk_cfg_mgmt_tx,
	/* .mgmt_tx_cancel_wait        = mtk_cfg80211_mgmt_tx_cancel_wait, */
#if KERNEL_VERSION(5, 8, 0) <= CFG80211_VERSION_CODE
	.update_mgmt_frame_registrations = mtk_cfg_mgmt_frame_update,
#else
	.mgmt_frame_register = mtk_cfg_mgmt_frame_register,
#endif
#ifdef CONFIG_NL80211_TESTMODE
	.testmode_cmd = mtk_cfg_testmode_cmd,
#endif

#if (CFG_SUPPORT_DFS_MASTER == 1)
	.start_radar_detection = mtk_cfg_start_radar_detection,
#if KERNEL_VERSION(3, 13, 0) <= CFG80211_VERSION_CODE
	.channel_switch = mtk_cfg_channel_switch,
#endif
#endif

#if (CFG_ENABLE_WIFI_DIRECT_CFG_80211 != 0)
	.change_bss = mtk_cfg_change_bss,
	.mgmt_tx_cancel_wait = mtk_cfg_mgmt_tx_cancel_wait,
	.deauth = mtk_cfg_deauth,
	.disassoc = mtk_cfg_disassoc,
	.start_ap = mtk_cfg_start_ap,
	.change_beacon = mtk_cfg_change_beacon,
	.stop_ap = mtk_cfg_stop_ap,
	.set_wiphy_params = mtk_cfg_set_wiphy_params,
	.set_bitrate_mask = mtk_cfg_set_bitrate_mask,
	.set_tx_power = mtk_cfg_set_txpower,
	.get_tx_power = mtk_cfg_get_txpower,
#endif
	.update_ft_ies = mtk_cfg80211_update_ft_ies,
};
#endif	/* CFG_ENABLE_UNIFY_WIPHY */

#if KERNEL_VERSION(3, 18, 0) <= CFG80211_VERSION_CODE

static const struct wiphy_vendor_command
	mtk_wlan_vendor_ops[] = {
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_GET_CHANNEL_LIST
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_channel_list,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_attribute,
		.maxattr = WIFI_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_SET_COUNTRY_CODE
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_set_country_code,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_attribute,
		.maxattr = WIFI_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV
			| WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_set_scan_mac_oui,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_attribute,
		.maxattr = WIFI_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	{
		{
			.vendor_id = OUI_QCA,
			.subcmd = QCA_NL80211_VENDOR_SUBCMD_SETBAND
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_set_band,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nal_parse_wifi_setband,
		.maxattr = QCA_WLAN_VENDOR_ATTR_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	{
		{
			.vendor_id = OUI_QCA,
			.subcmd = WIFI_SUBCMD_SET_ROAMING
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_set_roaming_policy,
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
	},
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_GET_ROAMING_CAPABILITIES
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_roaming_capabilities,
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
	},
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_CONFIG_ROAMING
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_config_roaming,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_attribute,
		.maxattr = WIFI_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_ENABLE_ROAMING
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
			 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_enable_roaming,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_attribute,
		.maxattr = WIFI_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	/* RTT */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = RTT_SUBCMD_GETCAPABILITY
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
		WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_rtt_capabilities,
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
	},
	/* Link Layer Statistics */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = LSTATS_SUBCMD_GET_INFO
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
		WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_llstats_get_info,
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
	},
	/* RSSI Monitoring */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_SET_RSSI_MONITOR
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
		WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_set_rssi_monitoring,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_rssi_monitor,
		.maxattr = WIFI_ATTRIBUTE_RSSI_MONITOR_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	/* Packet Keep Alive */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_OFFLOAD_START_MKEEP_ALIVE
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
		WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_packet_keep_alive_start,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_offloading_policy,
		.maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_OFFLOAD_STOP_MKEEP_ALIVE
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
		WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_packet_keep_alive_stop,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_offloading_policy,
		.maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	/* Get Driver Version or Firmware Version */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = LOGGER_GET_VER
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
				WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_version,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_get_version_policy,
		.maxattr = LOGGER_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
	/* Get Supported Feature Set */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_GET_FEATURE_SET
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
				WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_supported_feature_set,
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
	},
	/* Set Tx Power Scenario */
	{
		{
			.vendor_id = GOOGLE_OUI,
			.subcmd = WIFI_SUBCMD_SELECT_TX_POWER_SCENARIO
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
				WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_set_tx_power_scenario,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_parse_wifi_attribute,
		.maxattr = WIFI_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
#if CFG_SUPPORT_P2P_PREFERRED_FREQ_LIST
	/* P2P get preferred freq list */
	{
		{
			.vendor_id = OUI_QCA,
			.subcmd = NL80211_VENDOR_SUBCMD_GET_PREFER_FREQ_LIST
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV
				| WIPHY_VENDOR_CMD_NEED_NETDEV
				| WIPHY_VENDOR_CMD_NEED_RUNNING,
		.doit = mtk_cfg80211_vendor_get_preferred_freq_list,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_get_preferred_freq_list_policy,
		.maxattr = WIFI_VENDOR_ATTR_PREFERRED_FREQ_LIST_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
#endif /* CFG_SUPPORT_P2P_PREFERRED_FREQ_LIST */
#if CFG_AUTO_CHANNEL_SEL_SUPPORT
	{
		{
			.vendor_id = OUI_QCA,
			.subcmd = NL80211_VENDOR_SUBCMD_ACS
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV
				| WIPHY_VENDOR_CMD_NEED_NETDEV
				| WIPHY_VENDOR_CMD_NEED_RUNNING,
		.doit = mtk_cfg80211_vendor_acs,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_get_acs_policy,
		.maxattr = WIFI_VENDOR_ATTR_ACS_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
#endif
	{
		{
			.vendor_id = OUI_QCA,
			.subcmd = NL80211_VENDOR_SUBCMD_GET_FEATURES
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV
				| WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_features,
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
	},

	{
		{
			.vendor_id = OUI_MTK,
			.subcmd =  NL80211_VENDOR_SUBCMD_GET_APF_CAPABILITIES
		},
		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
				 WIPHY_VENDOR_CMD_NEED_NETDEV,
		.doit = mtk_cfg80211_vendor_get_apf_capabilities,
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		.policy = nla_get_apf_policy,
		.maxattr = APF_ATTRIBUTE_MAX
#else
		VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA)
#endif
	},
#if (CFG_SUPPORT_APF == 1)
	{
		{
			.vendor_id = OUI_MTK,
			.subcmd = NL80211_VENDOR_SUBCMD_SET_PACKET_FILTER
		},
			.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
				 WIPHY_VENDOR_CMD_NEED_NETDEV,
			.doit = mtk_cfg80211_vendor_set_packet_filter
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
			,
			.policy = nla_get_apf_policy,
			.maxattr = APF_ATTRIBUTE_MAX
#endif
	},
	{
		{
			.vendor_id = OUI_MTK,
			.subcmd = NL80211_VENDOR_SUBCMD_READ_PACKET_FILTER
		},
			.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
				WIPHY_VENDOR_CMD_NEED_NETDEV,
			.doit = mtk_cfg80211_vendor_read_packet_filter
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
		,
		.policy = nla_get_apf_policy,
		.maxattr = APF_ATTRIBUTE_MAX
#endif
	},
#endif /* CFG_SUPPORT_APF */
};

static const struct nl80211_vendor_cmd_info
	mtk_wlan_vendor_events[] = {
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = GSCAN_EVENT_HOTLIST_RESULTS_FOUND
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = GSCAN_EVENT_SCAN_RESULTS_AVAILABLE
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = GSCAN_EVENT_FULL_SCAN_RESULTS
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = RTT_EVENT_COMPLETE
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = GSCAN_EVENT_COMPLETE_SCAN
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = GSCAN_EVENT_HOTLIST_RESULTS_LOST
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = WIFI_EVENT_RSSI_MONITOR
	},
	{
		.vendor_id = GOOGLE_OUI,
		.subcmd = WIFI_EVENT_MAGIC_PACKET_RECEIVED
	},
	{
		.vendor_id = OUI_QCA,
		.subcmd = NL80211_VENDOR_SUBCMD_ACS
	},
#if CFG_SUPPORT_MTK_SCAN_EVENT
	{
		.vendor_id = OUI_MTK,
		.subcmd = MTK_NL80211_SCAN_EVENT
	},
#endif
};
#endif

/* There isn't a lot of sense in it, but you can transmit anything you like */
static const struct ieee80211_txrx_stypes
	mtk_cfg80211_ais_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
	[NL80211_IFTYPE_ADHOC] = {
		.tx = 0xffff,
		.rx = BIT(IEEE80211_STYPE_ACTION >> 4)
	},
	[NL80211_IFTYPE_STATION] = {
		.tx = 0xffff,
		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
		      BIT(IEEE80211_STYPE_BEACON >> 4)
	},
	[NL80211_IFTYPE_AP] = {
		.tx = 0xffff,
		.rx = BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
			BIT(IEEE80211_STYPE_ACTION >> 4)
#if (CFG_SUPPORT_SOFTAP_WPA3 == 1)
			| BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
			BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
			BIT(IEEE80211_STYPE_DISASSOC >> 4) |
			BIT(IEEE80211_STYPE_AUTH >> 4) |
			BIT(IEEE80211_STYPE_DEAUTH >> 4)
#endif
	},
	[NL80211_IFTYPE_AP_VLAN] = {
		/* copy AP */
		.tx = 0xffff,
		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
		BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
		BIT(IEEE80211_STYPE_DISASSOC >> 4) |
		BIT(IEEE80211_STYPE_AUTH >> 4) |
		BIT(IEEE80211_STYPE_DEAUTH >> 4) |
		BIT(IEEE80211_STYPE_ACTION >> 4)
	},
	[NL80211_IFTYPE_P2P_CLIENT] = {
		.tx = 0xffff,
		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
	},
	[NL80211_IFTYPE_P2P_GO] = {
		.tx = 0xffff,
		.rx = BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
		      BIT(IEEE80211_STYPE_ACTION >> 4)
	}
};

#ifdef CONFIG_PM
static const struct wiphy_wowlan_support mtk_wlan_wowlan_support = {
	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT |
		 WIPHY_WOWLAN_ANY,
};
#endif

#if CFG_SUPPORT_CFG80211_AUTH
/* NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES & NL80211_FEATURE_QUIET
 * support in linux kernet version => 3.18
 */
#if KERNEL_VERSION(3, 18, 0) > CFG80211_VERSION_CODE
#define NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES BIT(19)
#define NL80211_FEATURE_QUIET BIT(21)
#define NL80211_FEATURE_TX_POWER_INSERTION BIT(22)
#endif
#endif

/*******************************************************************************
 *                                 M A C R O S
 *******************************************************************************
 */

/*******************************************************************************
 *                   F U N C T I O N   D E C L A R A T I O N S
 *******************************************************************************
 */

/*******************************************************************************
 *                              F U N C T I O N S
 *******************************************************************************
 */

#if 0
/*----------------------------------------------------------------------------*/
/*!
 * \brief Override the implementation of select queue
 *
 * \param[in] dev Pointer to struct net_device
 * \param[in] skb Pointer to struct skb_buff
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
unsigned int _cfg80211_classify8021d(struct sk_buff *skb)
{
	unsigned int dscp = 0;

	/* skb->priority values from 256->263 are magic values
	 * directly indicate a specific 802.1d priority.  This is
	 * to allow 802.1d priority to be passed directly in from
	 * tags
	 */

	if (skb->priority >= 256 && skb->priority <= 263)
		return skb->priority - 256;
	switch (skb->protocol) {
	case htons(ETH_P_IP):
		dscp = ip_hdr(skb)->tos & 0xfc;
		break;
	}
	return dscp >> 5;
}
#endif

#if KERNEL_VERSION(5, 2, 0) <= LINUX_VERSION_CODE
u16 wlanSelectQueue(struct net_device *dev,
		    struct sk_buff *skb,
		    struct net_device *sb_dev)
{
	return mtk_wlan_ndev_select_queue(skb);
}
#elif KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE
u16 wlanSelectQueue(struct net_device *dev,
		    struct sk_buff *skb,
		    struct net_device *sb_dev, select_queue_fallback_t fallback)
{
	return mtk_wlan_ndev_select_queue(skb);
}
#elif KERNEL_VERSION(3, 14, 0) <= LINUX_VERSION_CODE
u16 wlanSelectQueue(struct net_device *dev,
		    struct sk_buff *skb,
		    void *accel_priv, select_queue_fallback_t fallback)
{
	return mtk_wlan_ndev_select_queue(skb);
}
#elif KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE
u16 wlanSelectQueue(struct net_device *dev,
		    struct sk_buff *skb,
		    void *accel_priv)
{
	return mtk_wlan_ndev_select_queue(skb);
}
#else
u16 wlanSelectQueue(struct net_device *dev,
		    struct sk_buff *skb)
{
	return mtk_wlan_ndev_select_queue(skb);
}
#endif

/*----------------------------------------------------------------------------*/
/*!
 * \brief Load NVRAM data and translate it into REG_INFO_T
 *
 * \param[in]  prGlueInfo Pointer to struct GLUE_INFO_T
 * \param[out] prRegInfo  Pointer to struct REG_INFO_T
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
static void glLoadNvram(IN struct GLUE_INFO *prGlueInfo,
			OUT struct REG_INFO *prRegInfo)
{
	ASSERT(prGlueInfo);
	ASSERT(prRegInfo);
	DBGLOG(INIT, INFO, "glLoadNvram start\n");
	if ((!prGlueInfo) || (!prRegInfo))
		return;

	/* load full NVRAM */
	prGlueInfo->fgNvramAvailable = FALSE;
	if (kalCfgDataRead(prGlueInfo, 0,
			   sizeof(prRegInfo->aucNvram),
			   (uint16_t *)prRegInfo->aucNvram) == TRUE) {

		struct WIFI_CFG_PARAM_STRUCT *prNvramSettings;

		if (sizeof(struct WIFI_CFG_PARAM_STRUCT) >
		    sizeof(prRegInfo->aucNvram)) {
			DBGLOG(INIT, ERROR,
			       "Size WIFI_CFG_PARAM_STRUCT %zu > size aucNvram %zu\n"
			       , sizeof(struct WIFI_CFG_PARAM_STRUCT),
			       sizeof(prRegInfo->aucNvram));
			return;
		}
#if CFG_SUPPORT_NVRAM_5G
		if (sizeof(struct NEW_EFUSE_MAPPING2NVRAM) >
		    sizeof(prRegInfo->aucEFUSE)) {
			DBGLOG(INIT, ERROR,
			       "Size NEW_EFUSE_MAPPING2NVRAM %zu >size aucEFUSE %zu\n"
			       , sizeof(struct NEW_EFUSE_MAPPING2NVRAM),
			       sizeof(prRegInfo->aucEFUSE));
			return;
		}
#endif

		prRegInfo->prNvramSettings =
			(struct WIFI_CFG_PARAM_STRUCT *)&prRegInfo->aucNvram;
		prNvramSettings = prRegInfo->prNvramSettings;

#if CFG_SUPPORT_NVRAM_5G
		/* load EFUSE overriding part */
		kalMemCopy(prRegInfo->aucEFUSE,
			   prNvramSettings->EfuseMapping.aucEFUSE,
			   sizeof(prRegInfo->aucEFUSE));
		prRegInfo->prOldEfuseMapping =
			(struct NEW_EFUSE_MAPPING2NVRAM *)&prRegInfo->aucEFUSE;
#else
		/* load EFUSE overriding part */
		kalMemCopy(prRegInfo->aucEFUSE, prNvramSettings->aucEFUSE,
			   sizeof(prRegInfo->aucEFUSE));
#endif

		/* load MAC Address */
		kalMemCopy(prRegInfo->aucMacAddr,
			   prNvramSettings->aucMacAddress,
			   PARAM_MAC_ADDR_LEN * sizeof(uint8_t));

		/* load country code */
		/* cast to wide characters */
		prRegInfo->au2CountryCode[0] =
			(uint16_t) prNvramSettings->aucCountryCode[0];
		prRegInfo->au2CountryCode[1] =
			(uint16_t) prNvramSettings->aucCountryCode[1];

		/* load default normal TX power */
		kalMemCopy(&prRegInfo->rTxPwr, &prNvramSettings->rTxPwr,
			   sizeof(struct TX_PWR_PARAM));

		/* load feature flags */
		prRegInfo->ucTxPwrValid = prNvramSettings->ucTxPwrValid;

		prRegInfo->ucSupport5GBand =
			prNvramSettings->ucSupport5GBand;

		prRegInfo->uc2G4BwFixed20M =
			prNvramSettings->uc2G4BwFixed20M;

		prRegInfo->uc5GBwFixed20M = prNvramSettings->uc5GBwFixed20M;

		prRegInfo->ucEnable5GBand = prNvramSettings->ucEnable5GBand;

		prRegInfo->ucRxDiversity = prNvramSettings->ucRxDiversity;

		prRegInfo->ucRssiPathCompasationUsed =
			prNvramSettings->fgRssiCompensationVaildbit;

		prRegInfo->ucGpsDesense = prNvramSettings->ucGpsDesense;

		/* load band edge tx power control */
		prRegInfo->fg2G4BandEdgePwrUsed =
			prNvramSettings->fg2G4BandEdgePwrUsed;

		if (prRegInfo->prNvramSettings->fg2G4BandEdgePwrUsed) {
			prRegInfo->cBandEdgeMaxPwrCCK =
				prNvramSettings->cBandEdgeMaxPwrCCK;

			prRegInfo->cBandEdgeMaxPwrOFDM20 =
				prNvramSettings->cBandEdgeMaxPwrOFDM20;

			prRegInfo->cBandEdgeMaxPwrOFDM40 =
				prNvramSettings->cBandEdgeMaxPwrOFDM40;
		}

		/* load regulation subbands */
		prRegInfo->eRegChannelListMap =
					(enum ENUM_REG_CH_MAP)
					prNvramSettings->ucRegChannelListMap;
		prRegInfo->ucRegChannelListIndex =
			prNvramSettings->ucRegChannelListIndex;

		if (prRegInfo->eRegChannelListMap ==
		    REG_CH_MAP_CUSTOMIZED) {
			kalMemCopy(prRegInfo->rDomainInfo.rSubBand,
				   prNvramSettings->aucRegSubbandInfo,
				   MAX_SUBBAND_NUM * sizeof(uint8_t));
		}

		/* load rssiPathCompensation */
		kalMemCopy(&prRegInfo->rRssiPathCompasation,
			   &prNvramSettings->rRssiPathCompensation,
			   sizeof(struct RSSI_PATH_COMPASATION));

		prGlueInfo->fgNvramAvailable = TRUE;
		DBGLOG(INIT, INFO, "glLoadNvram end\n");
	} else {
		DBGLOG(INIT, INFO, "glLoadNvram fail\n");
	}

}

/*----------------------------------------------------------------------------*/
/*!
 * \brief Release prDev from wlandev_array and free tasklet object related to
 *	  it.
 *
 * \param[in] prDev  Pointer to struct net_device
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
static void wlanClearDevIdx(struct net_device *prDev)
{
	int i;

	ASSERT(prDev);

	for (i = 0; i < CFG_MAX_WLAN_DEVICES; i++) {
		if (arWlanDevInfo[i].prDev == prDev) {
			arWlanDevInfo[i].prDev = NULL;
			u4WlanDevNum--;
		}
	}

}				/* end of wlanClearDevIdx() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief Allocate an unique interface index, net_device::ifindex member for
 *	  this wlan device. Store the net_device in wlandev_array, and
 *	  initialize tasklet object related to it.
 *
 * \param[in] prDev  Pointer to struct net_device
 *
 * \retval >= 0      The device number.
 * \retval -1        Fail to get index.
 */
/*----------------------------------------------------------------------------*/
static int wlanGetDevIdx(struct net_device *prDev)
{
	int i;

	ASSERT(prDev);

	for (i = 0; i < CFG_MAX_WLAN_DEVICES; i++) {
		if (arWlanDevInfo[i].prDev == (struct net_device *)NULL) {
			/* Reserve 2 bytes space to store one digit of
			 * device number and NULL terminator.
			 */
			arWlanDevInfo[i].prDev = prDev;
			u4WlanDevNum++;
			return i;
		}
	}

	return -1;
}				/* end of wlanGetDevIdx() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A method of struct net_device, a primary SOCKET interface to configure
 *        the interface lively. Handle an ioctl call on one of our devices.
 *        Everything Linux ioctl specific is done here. Then we pass the
 *	  contents of the ifr->data to the request message handler.
 *
 * \param[in] prDev      Linux kernel netdevice
 *
 * \param[in] prIFReq    Our private ioctl request structure, typed for the
 *			 generic
 *                       struct ifreq so we can use ptr to function
 *
 * \param[in] cmd        Command ID
 *
 * \retval WLAN_STATUS_SUCCESS The IOCTL command is executed successfully.
 * \retval OTHER The execution of IOCTL command is failed.
 */
/*----------------------------------------------------------------------------*/
int wlanDoIOCTL(struct net_device *prDev,
		struct ifreq *prIfReq, int i4Cmd)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	int ret = 0;

	/* Verify input parameters for the following functions */
	ASSERT(prDev && prIfReq);
	if (!prDev || !prIfReq) {
		DBGLOG(INIT, ERROR, "Invalid input data\n");
		return -EINVAL;
	}

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
	if (!prGlueInfo) {
		DBGLOG(INIT, ERROR, "prGlueInfo is NULL\n");
		return -EFAULT;
	}

	if (prGlueInfo->u4ReadyFlag == 0) {
		DBGLOG(INIT, ERROR, "Adapter is not ready\n");
		return -EINVAL;
	}

#if defined(_HIF_USB)
	if (prGlueInfo->rHifInfo.state == USB_STATE_SUSPEND) {
		DBGLOG(INIT, ERROR, "usb is suspend\n");
		return -EINVAL;
	}
#endif

	if ((i4Cmd >= SIOCIWFIRST) && (i4Cmd < SIOCIWFIRSTPRIV)) {
		/* 0x8B00 ~ 0x8BDF, wireless extension region */
		ret = wext_support_ioctl(prDev, prIfReq, i4Cmd);
	} else if ((i4Cmd >= SIOCIWFIRSTPRIV)
		   && (i4Cmd < SIOCIWLASTPRIV)) {
		/* 0x8BE0 ~ 0x8BFF, private ioctl region */
		ret = priv_support_ioctl(prDev, prIfReq, i4Cmd);
	} else if (i4Cmd == SIOCDEVPRIVATE + 1) {
#ifdef CFG_ANDROID_AOSP_PRIV_CMD
		ret = android_private_support_driver_cmd(prDev, prIfReq, i4Cmd);
#else
		ret = priv_support_driver_cmd(prDev, prIfReq, i4Cmd);
#endif /* CFG_ANDROID_AOSP_PRIV_CMD */
	}
#if (CFG_SUPPORT_MDNS_OFFLOAD && CFG_SUPPORT_MDNS_OFFLOAD_TV)
	else if (i4Cmd == SIOCDEVPRIVATE + 2) {
		/* For mDNS offload template. */
		ret = priv_support_mdns_offload(prDev, prIfReq, i4Cmd);
	}
#endif
	else {
		DBGLOG(INIT, WARN, "Unexpected ioctl command: 0x%04x\n",
		       i4Cmd);
		ret = -EOPNOTSUPP;
	}

	return ret;
}				/* end of wlanDoIOCTL() */

#if KERNEL_VERSION(5, 15, 0) <= CFG80211_VERSION_CODE
int wlanDoPrivIOCTL(struct net_device *prDev, struct ifreq *prIfReq,
		void __user *prData, int i4Cmd)
{
	return wlanDoIOCTL(prDev, prIfReq, i4Cmd);
}
#endif

/*----------------------------------------------------------------------------*/
/*!
 * \brief Export wlan GLUE_INFO_T pointer to p2p module
 *
 * \param[in]  prGlueInfo Pointer to struct GLUE_INFO_T
 *
 * \return TRUE: get GlueInfo pointer successfully
 *            FALSE: wlan is not started yet
 */
/*---------------------------------------------------------------------------*/
struct GLUE_INFO *wlanGetGlueInfo(void)
{
	struct net_device *prDev = NULL;
	struct GLUE_INFO *prGlueInfo = NULL;

	if (u4WlanDevNum == 0)
		return NULL;

	prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev;
	if (prDev == NULL)
		return NULL;

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));

	return prGlueInfo;
}

/*----------------------------------------------------------------------------*/
/*!
 * \brief This function is to set multicast list and set rx mode.
 *
 * \param[in] prDev  Pointer to struct net_device
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/

static struct delayed_work workq;
struct net_device *gPrDev;

static void wlanSetMulticastList(struct net_device *prDev)
{
	/* Allow to receive all multicast for WOW */
	DBGLOG(INIT, TRACE, "wlanSetMulticastList\n");
	prDev->flags |= (IFF_MULTICAST | IFF_ALLMULTI);
	gPrDev = prDev;
	schedule_delayed_work(&workq, 0);
}

/* FIXME: Since we cannot sleep in the wlanSetMulticastList, we arrange
 * another workqueue for sleeping. We don't want to block
 * main_thread, so we can't let tx_thread to do this
 */

static void wlanSetMulticastListWorkQueue(
	struct work_struct *work)
{

	struct GLUE_INFO *prGlueInfo = NULL;
	uint32_t u4PacketFilter = 0;
	uint32_t u4SetInfoLen;
	struct net_device *prDev = gPrDev;

	down(&g_halt_sem);
	if (g_u4HaltFlag) {
		up(&g_halt_sem);
		return;
	}

	prGlueInfo = (prDev != NULL) ? *((struct GLUE_INFO **)
					 netdev_priv(prDev)) : NULL;
	ASSERT(prDev);
	ASSERT(prGlueInfo);
	if (!prDev || !prGlueInfo) {
		DBGLOG(INIT, WARN,
		       "abnormal dev or skb: prDev(0x%p), prGlueInfo(0x%p)\n",
		       prDev, prGlueInfo);
		up(&g_halt_sem);
		return;
	}

	DBGLOG(INIT, INFO,
	       "wlanSetMulticastListWorkQueue prDev->flags:0x%x\n",
	       prDev->flags);

	if (prDev->flags & IFF_PROMISC)
		u4PacketFilter |= PARAM_PACKET_FILTER_PROMISCUOUS;

	if (prDev->flags & IFF_BROADCAST)
		u4PacketFilter |= PARAM_PACKET_FILTER_BROADCAST;

	if (prDev->flags & IFF_MULTICAST) {
		if ((prDev->flags & IFF_ALLMULTI)
		    || (netdev_mc_count(prDev) > MAX_NUM_GROUP_ADDR))
			u4PacketFilter |= PARAM_PACKET_FILTER_ALL_MULTICAST;
		else
			u4PacketFilter |= PARAM_PACKET_FILTER_MULTICAST;
	}

	up(&g_halt_sem);

	if (kalIoctl(prGlueInfo,
		     wlanoidSetCurrentPacketFilter,
		     &u4PacketFilter,
		     sizeof(u4PacketFilter), FALSE, FALSE, TRUE,
		     &u4SetInfoLen) != WLAN_STATUS_SUCCESS) {
		return;
	}

	if (u4PacketFilter & PARAM_PACKET_FILTER_MULTICAST) {
		/* Prepare multicast address list */
		struct netdev_hw_addr *ha;
		uint8_t *prMCAddrList = NULL;
		uint32_t i = 0;

		down(&g_halt_sem);
		if (g_u4HaltFlag) {
			up(&g_halt_sem);
			return;
		}

		prMCAddrList = kalMemAlloc(MAX_NUM_GROUP_ADDR * ETH_ALEN,
					   VIR_MEM_TYPE);
		if (!prMCAddrList) {
			DBGLOG(INIT, WARN, "prMCAddrList memory alloc fail!\n");
			return;
		}

		netdev_for_each_mc_addr(ha, prDev) {
			if (i < MAX_NUM_GROUP_ADDR) {
				kalMemCopy((prMCAddrList + i * ETH_ALEN),
					   GET_ADDR(ha), ETH_ALEN);
				i++;
			}
		}

		up(&g_halt_sem);

		kalIoctl(prGlueInfo,
			 wlanoidSetMulticastList, prMCAddrList, (i * ETH_ALEN),
			 FALSE, FALSE, TRUE, &u4SetInfoLen);

		kalMemFree(prMCAddrList, VIR_MEM_TYPE,
			   MAX_NUM_GROUP_ADDR * ETH_ALEN);
	}

}				/* end of wlanSetMulticastList() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief    To indicate scheduled scan has been stopped
 *
 * \param[in]
 *           prGlueInfo
 *
 * \return
 *           None
 */
/*----------------------------------------------------------------------------*/
void wlanSchedScanStoppedWorkQueue(struct work_struct *work)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	struct net_device *prDev = gPrDev;

	DBGLOG(SCN, INFO, "wlanSchedScanStoppedWorkQueue\n");
	prGlueInfo = (prDev != NULL) ? *((struct GLUE_INFO **)
					 netdev_priv(prDev)) : NULL;
	if (!prGlueInfo) {
		DBGLOG(SCN, INFO, "prGlueInfo == NULL unexpected\n");
		return;
	}

	/* 2. indication to cfg80211 */
	/* 20150205 change cfg80211_sched_scan_stopped to work queue due to
	 * sched_scan_mtx dead lock issue
	 */
#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE
	cfg80211_sched_scan_stopped(priv_to_wiphy(prGlueInfo), 0);
#else
	cfg80211_sched_scan_stopped(priv_to_wiphy(prGlueInfo));
#endif
	DBGLOG(SCN, INFO,
	       "cfg80211_sched_scan_stopped event send done WorkQueue thread return from wlanSchedScanStoppedWorkQueue\n");
	return;

}

/* FIXME: Since we cannot sleep in the wlanSetMulticastList, we arrange
 * another workqueue for sleeping. We don't want to block
 * main_thread, so we can't let tx_thread to do this
 */

void p2pSetMulticastListWorkQueueWrapper(struct GLUE_INFO
		*prGlueInfo)
{

	ASSERT(prGlueInfo);

	if (!prGlueInfo) {
		DBGLOG(INIT, WARN,
		       "abnormal dev or skb: prGlueInfo(0x%p)\n", prGlueInfo);
		return;
	}
#if CFG_ENABLE_WIFI_DIRECT
	if (prGlueInfo->prAdapter->fgIsP2PRegistered)
		mtk_p2p_wext_set_Multicastlist(prGlueInfo);
#endif

} /* end of p2pSetMulticastListWorkQueueWrapper() */

/*----------------------------------------------------------------------------*/
/*
 * \brief This function is TX entry point of NET DEVICE.
 *
 * \param[in] prSkb  Pointer of the sk_buff to be sent
 * \param[in] prDev  Pointer to struct net_device
 *
 * \retval NETDEV_TX_OK - on success.
 * \retval NETDEV_TX_BUSY - on failure, packet will be discarded by upper layer.
 */
/*----------------------------------------------------------------------------*/
netdev_tx_t wlanHardStartXmit(struct sk_buff *prSkb,
		      struct net_device *prDev)
{
	struct NETDEV_PRIVATE_GLUE_INFO *prNetDevPrivate =
		(struct NETDEV_PRIVATE_GLUE_INFO *) NULL;
	struct GLUE_INFO *prGlueInfo = *((struct GLUE_INFO **)
					 netdev_priv(prDev));
	uint8_t ucBssIndex;

	ASSERT(prSkb);
	ASSERT(prDev);
	ASSERT(prGlueInfo);

	prNetDevPrivate = (struct NETDEV_PRIVATE_GLUE_INFO *)
			  netdev_priv(prDev);
	ASSERT(prNetDevPrivate->prGlueInfo == prGlueInfo);
	ucBssIndex = prNetDevPrivate->ucBssIdx;

#if CFG_SUPPORT_PASSPOINT
	if (prGlueInfo->fgIsDad) {
		/* kalPrint("[Passpoint R2] Due to ipv4_dad...TX is forbidden\n"
		 *         );
		 */
		dev_kfree_skb(prSkb);
		return NETDEV_TX_OK;
	}
	if (prGlueInfo->fgIs6Dad) {
		/* kalPrint("[Passpoint R2] Due to ipv6_dad...TX is forbidden\n"
		 *          );
		 */
		dev_kfree_skb(prSkb);
		return NETDEV_TX_OK;
	}
#endif /* CFG_SUPPORT_PASSPOINT */

#if CFG_CHIP_RESET_SUPPORT
	if (!wlanIsDriverReady(prGlueInfo)) {
		DBGLOG(INIT, WARN,
			"u4ReadyFlag:%u, kalIsResetting():%d, dropping the packet\n",
			prGlueInfo->u4ReadyFlag, kalIsResetting());

		dev_kfree_skb(prSkb);
		return NETDEV_TX_OK;
	}
#endif


	kalResetPacket(prGlueInfo, (void *) prSkb);

	STATS_TX_TIME_ARRIVE(prSkb);
#if (CFG_SUPPORT_TX_TSO_SW == 1)
	if (kalSwTsoXmit(&prSkb, prDev, prGlueInfo, ucBssIndex)
		== WLAN_STATUS_SUCCESS)
		return NETDEV_TX_OK;
#endif

	if (kalHardStartXmit(prSkb, prDev, prGlueInfo,
			     ucBssIndex) == WLAN_STATUS_SUCCESS) {
		/* Successfully enqueue to Tx queue */
		/* Successfully enqueue to Tx queue */
		if (netif_carrier_ok(prDev))
			kalPerMonStart(prGlueInfo);
	}

	/* For Linux, we'll always return OK FLAG, because we'll free this skb
	 * by ourself
	 */
	return NETDEV_TX_OK;
}				/* end of wlanHardStartXmit() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A method of struct net_device, to get the network interface
 *        statistical information.
 *
 * Whenever an application needs to get statistics for the interface, this
 * method is called. This happens, for example, when ifconfig or netstat -i
 * is run.
 *
 * \param[in] prDev      Pointer to struct net_device.
 *
 * \return net_device_stats buffer pointer.
 */
/*----------------------------------------------------------------------------*/
struct net_device_stats *wlanGetStats(IN struct net_device
				      *prDev)
{
	return (struct net_device_stats *)kalGetStats(prDev);
}				/* end of wlanGetStats() */

void wlanDebugInit(void)
{
	/* Set the initial debug level of each module */
#if DBG
	/* enable all */
	wlanSetDriverDbgLevel(DBG_ALL_MODULE_IDX, DBG_CLASS_MASK);
#else
#ifdef CFG_DEFAULT_DBG_LEVEL
	wlanSetDriverDbgLevel(DBG_ALL_MODULE_IDX,
			      CFG_DEFAULT_DBG_LEVEL);
#else
	wlanSetDriverDbgLevel(DBG_ALL_MODULE_IDX,
			      DBG_LOG_LEVEL_DEFAULT);
#endif
#endif /* DBG */

	LOG_FUNC("Reset ALL DBG module log level to DEFAULT!");

}

/*----------------------------------------------------------------------------*/
/*!
 * \brief A function for prDev->init
 *
 * \param[in] prDev      Pointer to struct net_device.
 *
 * \retval 0         The execution of wlanInit succeeds.
 * \retval -ENXIO    No such device.
 */
/*----------------------------------------------------------------------------*/
static int wlanInit(struct net_device *prDev)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	struct wireless_dev *prWdev = NULL;

	if (!prDev)
		return -ENXIO;

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
	prWdev = prDev->ieee80211_ptr;
	INIT_DELAYED_WORK(&workq, wlanSetMulticastListWorkQueue);

	/* 20150205 work queue for sched_scan */
	INIT_DELAYED_WORK(&sched_workq,
			  wlanSchedScanStoppedWorkQueue);

#if IS_ENABLED(CFG_RX_NAPI_SUPPORT)
#if IS_ENABLED(CFG_GRO_SUPPORT)
	/* Register GRO function to kernel */
	prWdev->netdev->features |= NETIF_F_GRO;
	prWdev->netdev->hw_features |= NETIF_F_GRO;
#endif /* CFG_GRO_SUPPORT */
#if KERNEL_VERSION(6, 1, 0) <= CFG80211_VERSION_CODE
	netif_napi_add(prWdev->netdev, &prGlueInfo->rNapi,
		kalRxNapiPoll);
#else
	netif_napi_add(prWdev->netdev, &prGlueInfo->rNapi,
		kalRxNapiPoll, NAPI_POLL_WEIGHT);
#endif
	skb_queue_head_init(&prGlueInfo->rRxNapiSkbQ);
	if (prGlueInfo->prAdapter->rWifiVar.ucRxNapiEnable)
		kalRxNapiSetEnable(prGlueInfo, TRUE);
#endif /* CFG_RX_NAPI_SUPPORT */

	/* 20161024 init wow port setting */
#if CFG_WOW_SUPPORT
	kalWowInit(prGlueInfo);
#endif

	return 0;		/* success */
}				/* end of wlanInit() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A function for prDev->uninit
 *
 * \param[in] prDev      Pointer to struct net_device.
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
static void wlanUninit(struct net_device *prDev)
{
}				/* end of wlanUninit() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A method of struct net_device, to set the randomized mac address
 *
 * This method is called before Wifi Framework requests a new conenction with
 * enabled feature "Connected Random Mac".
 *
 * \param[in] ndev	Pointer to struct net_device.
 * \param[in] addr	Randomized Mac address passed from WIFI framework.
 *
 * \return int.
 */
/*----------------------------------------------------------------------------*/
static int wlanSetMacAddress(struct net_device *ndev, void *addr)
{
	struct ADAPTER *prAdapter = NULL;
	struct GLUE_INFO *prGlueInfo = NULL;
	struct sockaddr *sa = NULL;

	/**********************************************************************
	 * Check if kernel passes valid data to us                            *
	 **********************************************************************
	 */
	if (!ndev || !addr) {
		DBGLOG(INIT, ERROR, "Set macaddr with ndev(%d) and addr(%d)\n",
		       (ndev == NULL) ? 0 : 1, (addr == NULL) ? 0 : 1);
		return WLAN_STATUS_INVALID_DATA;
	}

	/**********************************************************************
	 * 1. Change OwnMacAddr which will be updated to FW through           *
	 *    rlmActivateNetwork later.                                       *
	 * 2. Change dev_addr stored in kernel to notify framework that the   *
	 *    mac addr has been changed and what the new value is.            *
	 **********************************************************************
	 */
	sa = (struct sockaddr *)addr;
	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(ndev));
	prAdapter = prGlueInfo->prAdapter;

	COPY_MAC_ADDR(prAdapter->prAisBssInfo->aucOwnMacAddr, sa->sa_data);
	kal_eth_hw_addr_set(prGlueInfo->prDevHandler, sa->sa_data);
	DBGLOG(INIT, INFO, "Set connect random macaddr to " MACSTR ".\n",
	       MAC2STR(prAdapter->prAisBssInfo->aucOwnMacAddr));

	return WLAN_STATUS_SUCCESS;
}				/* end of wlanSetMacAddr() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A function for prDev->open
 *
 * \param[in] prDev      Pointer to struct net_device.
 *
 * \retval 0     The execution of wlanOpen succeeds.
 * \retval < 0   The execution of wlanOpen failed.
 */
/*----------------------------------------------------------------------------*/
static int wlanOpen(struct net_device *prDev)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	ASSERT(prDev);

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
#if IS_ENABLED(CFG_RX_NAPI_SUPPORT)
	napi_enable(&prGlueInfo->rNapi);
#endif /* CFG_RX_NAPI_SUPPORT */

	netif_tx_start_all_queues(prDev);

	return 0;		/* success */
}				/* end of wlanOpen() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A function for prDev->stop
 *
 * \param[in] prDev      Pointer to struct net_device.
 *
 * \retval 0     The execution of wlanStop succeeds.
 * \retval < 0   The execution of wlanStop failed.
 */
/*----------------------------------------------------------------------------*/
static int wlanStop(struct net_device *prDev)
{
	struct GLUE_INFO *prGlueInfo = NULL;

	GLUE_SPIN_LOCK_DECLARATION();

	ASSERT(prDev);

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));

	/* CFG80211 down */
	GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
	if (prGlueInfo->prScanRequest) {
		kalCfg80211ScanDone(prGlueInfo->prScanRequest, TRUE);
		prGlueInfo->prScanRequest = NULL;
	}
	GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);

#if CFG_AUTO_CHANNEL_SEL_SUPPORT
	/* zero clear old acs information */
	kalMemZero(&(prGlueInfo->prAdapter->rWifiVar.rChnLoadInfo),
		sizeof(prGlueInfo->prAdapter->rWifiVar.rChnLoadInfo));
	wlanInitChnLoadInfoChannelList(prGlueInfo->prAdapter);
#endif

	netif_tx_stop_all_queues(prDev);

#if IS_ENABLED(CFG_RX_NAPI_SUPPORT)
	DBGLOG(RX, INFO, "RX NAPI disable ongoing %s\n", prDev->name);
	napi_synchronize(&prGlueInfo->rNapi);
	napi_disable(&prGlueInfo->rNapi);
	if (skb_queue_len(&prGlueInfo->rRxNapiSkbQ)) {

		struct sk_buff *skb;

		DBGLOG(INIT, WARN, "NAPI Remain pkts %d\n",
			skb_queue_len(&prGlueInfo->rRxNapiSkbQ));

		while ((skb = skb_dequeue(&prGlueInfo->rRxNapiSkbQ)) != NULL)
			kfree_skb(skb);
	}
	DBGLOG(RX, INFO, "RX NAPI disabled %s\n", prDev->name);
#endif /* CFG_RX_NAPI_SUPPORT */

	return 0;		/* success */
}				/* end of wlanStop() */

#if CFG_SUPPORT_SNIFFER
static int wlanMonOpen(struct net_device *prDev)
{
	ASSERT(prDev);

	netif_tx_start_all_queues(prDev);

	return 0;		/* success */
}

static int wlanMonStop(struct net_device *prDev)
{
	ASSERT(prDev);

	netif_tx_stop_all_queues(prDev);

	return 0;		/* success */
}

static const struct net_device_ops wlan_mon_netdev_ops = {
	.ndo_open = wlanMonOpen,
	.ndo_stop = wlanMonStop,
};

void wlanMonWorkHandler(struct work_struct *work)
{
	struct GLUE_INFO *prGlueInfo;

	prGlueInfo = container_of(work, struct GLUE_INFO, monWork);

	if (prGlueInfo->fgIsEnableMon) {
		if (prGlueInfo->prMonDevHandler)
			return;
#if KERNEL_VERSION(3, 18, 0) <= LINUX_VERSION_CODE
		prGlueInfo->prMonDevHandler =
			alloc_netdev_mq(sizeof(struct NETDEV_PRIVATE_GLUE_INFO),
					NIC_MONITOR_INF_NAME,
					NET_NAME_PREDICTABLE, ether_setup,
					CFG_MAX_TXQ_NUM);
#else
		prGlueInfo->prMonDevHandler =
			alloc_netdev_mq(sizeof(struct NETDEV_PRIVATE_GLUE_INFO),
					NIC_MONITOR_INF_NAME,
					ether_setup, CFG_MAX_TXQ_NUM);
#endif
		if (prGlueInfo->prMonDevHandler == NULL) {
			DBGLOG(INIT, ERROR,
			       "wlanMonWorkHandler: Allocated prMonDevHandler context FAIL.\n");
			return;
		}

		((struct NETDEV_PRIVATE_GLUE_INFO *) netdev_priv(
			 prGlueInfo->prMonDevHandler))->prGlueInfo = prGlueInfo;
		prGlueInfo->prMonDevHandler->type =
			ARPHRD_IEEE80211_RADIOTAP;
		prGlueInfo->prMonDevHandler->netdev_ops =
			&wlan_mon_netdev_ops;
		netif_carrier_off(prGlueInfo->prMonDevHandler);
		netif_tx_stop_all_queues(prGlueInfo->prMonDevHandler);
		kalResetStats(prGlueInfo->prMonDevHandler);

		if (register_netdev(prGlueInfo->prMonDevHandler) < 0) {
			DBGLOG(INIT, ERROR,
			       "wlanMonWorkHandler: Registered prMonDevHandler context FAIL.\n");
			free_netdev(prGlueInfo->prMonDevHandler);
			prGlueInfo->prMonDevHandler = NULL;
		}
		DBGLOG(INIT, INFO,
		       "wlanMonWorkHandler: Registered prMonDevHandler context DONE.\n");
	} else {
		if (prGlueInfo->prMonDevHandler) {
			unregister_netdev(prGlueInfo->prMonDevHandler);
			prGlueInfo->prMonDevHandler = NULL;
			DBGLOG(INIT, INFO,
			       "wlanMonWorkHandler: unRegistered prMonDevHandler context DONE.\n");
		}
	}
}
#endif

/*----------------------------------------------------------------------------*/
/*!
 * \brief Update Channel table for cfg80211 for Wi-Fi Direct based on current
 *        country code
 *
 * \param[in] prGlueInfo      Pointer to glue info
 *
 * \return   none
 */
/*----------------------------------------------------------------------------*/
void wlanUpdateChannelTable(struct GLUE_INFO *prGlueInfo)
{
	uint8_t i, j;
	uint8_t ucNumOfChannel;
	struct RF_CHANNEL_INFO aucChannelList[ARRAY_SIZE(
			mtk_2ghz_channels) + ARRAY_SIZE(mtk_5ghz_channels)];

	/* 1. Disable all channels */
	for (i = 0; i < ARRAY_SIZE(mtk_2ghz_channels); i++) {
		mtk_2ghz_channels[i].flags |= IEEE80211_CHAN_DISABLED;
		mtk_2ghz_channels[i].orig_flags |= IEEE80211_CHAN_DISABLED;
	}

	for (i = 0; i < ARRAY_SIZE(mtk_5ghz_channels); i++) {
		mtk_5ghz_channels[i].flags |= IEEE80211_CHAN_DISABLED;
		mtk_5ghz_channels[i].orig_flags |= IEEE80211_CHAN_DISABLED;
	}

	/* 2. Get current domain channel list */
	rlmDomainGetChnlList(prGlueInfo->prAdapter,
			     BAND_NULL, FALSE,
			     ARRAY_SIZE(mtk_2ghz_channels) + ARRAY_SIZE(
				     mtk_5ghz_channels),
			     &ucNumOfChannel, aucChannelList);

	/* 3. Enable specific channel based on domain channel list */
	for (i = 0; i < ucNumOfChannel; i++) {
		switch (aucChannelList[i].eBand) {
		case BAND_2G4:
			for (j = 0; j < ARRAY_SIZE(mtk_2ghz_channels); j++) {
				if (mtk_2ghz_channels[j].hw_value ==
				    aucChannelList[i].ucChannelNum) {
					mtk_2ghz_channels[j].flags &=
						~IEEE80211_CHAN_DISABLED;
					mtk_2ghz_channels[j].orig_flags &=
						~IEEE80211_CHAN_DISABLED;
					break;
				}
			}
			break;

		case BAND_5G:
			for (j = 0; j < ARRAY_SIZE(mtk_5ghz_channels); j++) {
				if (mtk_5ghz_channels[j].hw_value ==
				    aucChannelList[i].ucChannelNum) {
					mtk_5ghz_channels[j].flags &=
						~IEEE80211_CHAN_DISABLED;
					mtk_5ghz_channels[j].orig_flags &=
						~IEEE80211_CHAN_DISABLED;
					break;
				}
			}
			break;

		default:
			DBGLOG(INIT, WARN, "Unknown band %d\n",
			       aucChannelList[i].eBand);
			break;
		}
	}

}

/*----------------------------------------------------------------------------*/
/*!
 * \brief Register the device to the kernel and return the index.
 *
 * \param[in] prDev      Pointer to struct net_device.
 *
 * \retval 0     The execution of wlanNetRegister succeeds.
 * \retval < 0   The execution of wlanNetRegister failed.
 */
/*----------------------------------------------------------------------------*/
static int32_t wlanNetRegister(struct wireless_dev *prWdev)
{
	struct GLUE_INFO *prGlueInfo;
	int32_t i4DevIdx = -1;
	struct NETDEV_PRIVATE_GLUE_INFO *prNetDevPrivate =
		(struct NETDEV_PRIVATE_GLUE_INFO *) NULL;
	struct ADAPTER *prAdapter = NULL;

	ASSERT(prWdev);

	do {
		if (!prWdev)
			break;

		prGlueInfo = (struct GLUE_INFO *) wiphy_priv(prWdev->wiphy);
		prAdapter = prGlueInfo->prAdapter;
		i4DevIdx = wlanGetDevIdx(prWdev->netdev);
		if (i4DevIdx < 0) {
			DBGLOG(INIT, ERROR, "net_device number exceeds!\n");
			break;
		}

		if (prAdapter && prAdapter->rWifiVar.ucWow)
			kalInitDevWakeup(prGlueInfo->prAdapter,
				wiphy_dev(prWdev->wiphy));

#if (CFG_SUPPORT_TX_TSO_SW == 1)
		prWdev->netdev->features |=
			NETIF_F_TSO |
			NETIF_F_TSO6 |
			NETIF_F_GSO |
			NETIF_F_SG;
#endif

		if (register_netdev(prWdev->netdev) < 0) {
			DBGLOG(INIT, ERROR, "Register net_device failed\n");
			wlanClearDevIdx(prWdev->netdev);
			i4DevIdx = -1;
		}

#if 1
		prNetDevPrivate = (struct NETDEV_PRIVATE_GLUE_INFO *)
				  netdev_priv(prGlueInfo->prDevHandler);
		ASSERT(prNetDevPrivate->prGlueInfo == prGlueInfo);
		prNetDevPrivate->ucBssIdx =
			prGlueInfo->prAdapter->prAisBssInfo->ucBssIndex;
#if CFG_ENABLE_UNIFY_WIPHY
		prNetDevPrivate->ucIsP2p = FALSE;
#endif
		wlanBindBssIdxToNetInterface(prGlueInfo,
			     prGlueInfo->prAdapter->prAisBssInfo->ucBssIndex,
			     (void *) prWdev->netdev);
#else
		wlanBindBssIdxToNetInterface(prGlueInfo,
			     prGlueInfo->prAdapter->prAisBssInfo->ucBssIndex,
			     (void *) prWdev->netdev);
		/* wlanBindNetInterface(prGlueInfo, NET_DEV_WLAN_IDX,
		 *                      (PVOID)prWdev->netdev);
		 */
#endif
		if (i4DevIdx != -1)
			prGlueInfo->fgIsRegistered = TRUE;
	} while (FALSE);

	return i4DevIdx;	/* success */
}				/* end of wlanNetRegister() */


/*----------------------------------------------------------------------------*/
/*!
 * \brief Unregister the device from the kernel
 *
 * \param[in] prWdev      Pointer to struct net_device.
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
static void wlanNetUnregister(struct wireless_dev *prWdev)
{
	struct GLUE_INFO *prGlueInfo;

	if (!prWdev) {
		DBGLOG(INIT, ERROR, "The device context is NULL\n");
		return;
	}

	prGlueInfo = (struct GLUE_INFO *) wiphy_priv(prWdev->wiphy);

#if IS_ENABLED(CFG_RX_NAPI_SUPPORT)
	kalRxNapiSetEnable(prGlueInfo, FALSE);
	netif_napi_del(&prGlueInfo->rNapi);
#endif /* CFG_RX_NAPI_SUPPORT */

	if (prWdev->netdev &&
	     prWdev->netdev->reg_state == NETREG_REGISTERED) {
		wlanClearDevIdx(prWdev->netdev);
		unregister_netdev(prWdev->netdev);
	}
	prGlueInfo->fgIsRegistered = FALSE;

#if CFG_SUPPORT_SNIFFER
	if (prGlueInfo->prMonDevHandler) {
		unregister_netdev(prGlueInfo->prMonDevHandler);
		/* FIXME: Why not free_netdev()? */
		prGlueInfo->prMonDevHandler = NULL;
	}
	prGlueInfo->fgIsEnableMon = FALSE;
#endif

}				/* end of wlanNetUnregister() */

static const struct net_device_ops wlan_netdev_ops = {
	.ndo_open = wlanOpen,
	.ndo_stop = wlanStop,
	.ndo_set_rx_mode = wlanSetMulticastList,
	.ndo_get_stats = wlanGetStats,
	.ndo_do_ioctl = wlanDoIOCTL,
#if KERNEL_VERSION(5, 15, 0) <= CFG80211_VERSION_CODE
	.ndo_siocdevprivate = wlanDoPrivIOCTL,
#endif
	.ndo_start_xmit = wlanHardStartXmit,
	.ndo_init = wlanInit,
	.ndo_uninit = wlanUninit,
	.ndo_select_queue = wlanSelectQueue,
	.ndo_set_mac_address = wlanSetMacAddress,
};

#if CFG_ENABLE_UNIFY_WIPHY
const struct net_device_ops *wlanGetNdevOps(void)
{
	return &wlan_netdev_ops;
}
#endif

static void wlanCreateWirelessDevice(void)
{
	struct wiphy *prWiphy = NULL;
	struct wireless_dev *prWdev = NULL;
	unsigned int u4SupportSchedScanFlag = 0;

	/* 4 <1.1> Create wireless_dev */
	prWdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
	if (!prWdev) {
		DBGLOG(INIT, ERROR,
		       "Allocating memory to wireless_dev context failed\n");
		return;
	}
	/* 4 <1.2> Create wiphy */
#if CFG_ENABLE_UNIFY_WIPHY
	prWiphy = wiphy_new(&mtk_cfg_ops, sizeof(struct GLUE_INFO));
#else
	prWiphy = wiphy_new(&mtk_wlan_ops,
			    sizeof(struct GLUE_INFO));
#endif

	if (!prWiphy) {
		DBGLOG(INIT, ERROR,
		       "Allocating memory to wiphy device failed\n");
		goto free_wdev;
	}

	/* 4 <1.3> configure wireless_dev & wiphy */
	prWdev->iftype = NL80211_IFTYPE_STATION;
	prWiphy->iface_combinations = p_mtk_iface_combinations_sta;
	prWiphy->n_iface_combinations =
		mtk_iface_combinations_sta_num;
	prWiphy->max_scan_ssids = SCN_SSID_MAX_NUM +
				  1; /* include one wildcard ssid */
	prWiphy->max_scan_ie_len = 512;
#if CFG_SUPPORT_SCHED_SCAN
	prWiphy->max_sched_scan_ssids     =
		CFG_SCAN_HIDDEN_SSID_MAX_NUM;
	prWiphy->max_match_sets           =
		CFG_SCAN_SSID_MATCH_MAX_NUM;
	prWiphy->max_sched_scan_ie_len    = CFG_CFG80211_IE_BUF_LEN;
#if (KERNEL_VERSION(4, 12, 0) < LINUX_VERSION_CODE)
	prWiphy->max_sched_scan_reqs = 1;
#else
	u4SupportSchedScanFlag            =
		WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
#endif
#endif /* CFG_SUPPORT_SCHED_SCAN */
	prWiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
				   BIT(NL80211_IFTYPE_ADHOC);
	prWiphy->bands[KAL_BAND_2GHZ] = &mtk_band_2ghz;
	/* always assign 5Ghz bands here, if the chip is not support 5Ghz,
	 *  bands[KAL_BAND_5GHZ] will be assign to NULL
	 */
	prWiphy->bands[KAL_BAND_5GHZ] = &mtk_band_5ghz;
	prWiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
	prWiphy->cipher_suites = (const u32 *)mtk_cipher_suites;
	prWiphy->n_cipher_suites = ARRAY_SIZE(mtk_cipher_suites);
#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
	prWiphy->akm_suites = (const u32 *)mtk_akm_suites;
	prWiphy->n_akm_suites = ARRAY_SIZE(mtk_akm_suites);
#endif
	prWiphy->flags = WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL
			| u4SupportSchedScanFlag;

#if CFG_SUPPORT_802_11R && CFG_SUPPORT_CFG80211_AUTH
	prWiphy->features |= NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES;
	prWiphy->features |= NL80211_FEATURE_QUIET;
	prWiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
#endif /* CFG_SUPPORT_ROAMING */

#if (CFG_SUPPORT_ROAMING == 1)
	prWiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
#endif /* CFG_SUPPORT_ROAMING */

#if KERNEL_VERSION(3, 14, 0) > CFG80211_VERSION_CODE
	prWiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#else
	prWiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
#if (CFG_SUPPORT_DFS_MASTER == 1)
	prWiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
#if KERNEL_VERSION(3, 16, 0) <= CFG80211_VERSION_CODE
	prWiphy->max_num_csa_counters = 2;
#endif
#endif /* CFG_SUPPORT_DFS_MASTER */
#endif
#if (CFG_SUPPORT_SAE == 1)
	prWiphy->features |= NL80211_FEATURE_SAE;
#endif /* CFG_SUPPORT_DFS_MASTER */

#if (CFG_SUPPORT_DFS_OFFLOAD == 1)
	wiphy_ext_feature_set(prWiphy,
		NL80211_EXT_FEATURE_DFS_OFFLOAD);
#endif

	cfg80211_regd_set_wiphy(prWiphy);

#if (CFG_SUPPORT_TDLS == 1)
	TDLSEX_WIPHY_FLAGS_INIT(prWiphy->flags);
#endif /* CFG_SUPPORT_TDLS */
	prWiphy->max_remain_on_channel_duration = 5000;
	prWiphy->mgmt_stypes = mtk_cfg80211_ais_default_mgmt_stypes;

#if (CFG_SUPPORT_SCAN_RANDOM_MAC && \
	(KERNEL_VERSION(3, 19, 0) <= CFG80211_VERSION_CODE))
	prWiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
	prWiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
#endif

#if KERNEL_VERSION(3, 18, 0) <= CFG80211_VERSION_CODE
	prWiphy->vendor_commands = mtk_wlan_vendor_ops;
	prWiphy->n_vendor_commands = sizeof(mtk_wlan_vendor_ops) /
				     sizeof(struct wiphy_vendor_command);
	prWiphy->vendor_events = mtk_wlan_vendor_events;
	prWiphy->n_vendor_events = ARRAY_SIZE(
					   mtk_wlan_vendor_events);
#endif
	/* 4 <1.4> wowlan support */
#ifdef CONFIG_PM
#if KERNEL_VERSION(3, 11, 0) <= CFG80211_VERSION_CODE
	prWiphy->wowlan = &mtk_wlan_wowlan_support;
#else
	kalMemCopy(&prWiphy->wowlan, &mtk_wlan_wowlan_support,
		   sizeof(struct wiphy_wowlan_support));
#endif
#endif

#ifdef CONFIG_CFG80211_WEXT
	/* 4 <1.5> Use wireless extension to replace IOCTL */

#if CFG_ENABLE_UNIFY_WIPHY
	prWiphy->wext = NULL;
#else
	prWiphy->wext = &wext_handler_def;
#endif
#endif
	/* initialize semaphore for halt control */
	sema_init(&g_halt_sem, 1);

#if CFG_ENABLE_UNIFY_WIPHY
	prWiphy->iface_combinations = p_mtk_iface_combinations_p2p;
	prWiphy->n_iface_combinations =
		mtk_iface_combinations_p2p_num;

	prWiphy->interface_modes |= BIT(NL80211_IFTYPE_AP) |
				    BIT(NL80211_IFTYPE_P2P_CLIENT) |
				    BIT(NL80211_IFTYPE_P2P_GO) |
				    BIT(NL80211_IFTYPE_STATION);
	prWiphy->software_iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
	prWiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
			  WIPHY_FLAG_HAVE_AP_SME;
	prWiphy->ap_sme_capa = 1;
#endif
	if (wiphy_register(prWiphy) < 0) {
		DBGLOG(INIT, ERROR, "wiphy_register error\n");
		goto free_wiphy;
	}
	prWdev->wiphy = prWiphy;
	gprWdev = prWdev;
	DBGLOG(INIT, INFO, "Create wireless device success\n");
	return;

free_wiphy:
	wiphy_free(prWiphy);
free_wdev:
	kfree(prWdev);
}

/*----------------------------------------------------------------------------*/
/*!
 * \brief Destroy all wdev (including the P2P device), and unregister wiphy
 *
 * \param (none)
 *
 * \return (none)
 *
 */
/*----------------------------------------------------------------------------*/
static void wlanDestroyAllWdev(void)
{
#if CFG_ENABLE_UNIFY_WIPHY
	/* There is only one wiphy, avoid that double free the wiphy */
	struct wiphy *wiphy = NULL;
#endif
#if CFG_ENABLE_WIFI_DIRECT
	int i = 0;
#endif

#if CFG_ENABLE_WIFI_DIRECT
	/* free P2P wdev */
	for (i = 0; i < KAL_P2P_NUM; i++) {
		if (gprP2pRoleWdev[i] == NULL)
			continue;
#if CFG_ENABLE_UNIFY_WIPHY
		if (gprP2pRoleWdev[i] == gprWdev) {
			/* This is AIS/AP Interface */
			gprP2pRoleWdev[i] = NULL;
			continue;
		}
#endif
		/* Do wiphy_unregister here. Take care the case that the
		 * gprP2pRoleWdev[i] is created by the cfg80211 add iface ops,
		 * And the base P2P dev is in the gprP2pWdev.
		 * Expect that new created gprP2pRoleWdev[i] is freed in
		 * unregister_netdev/mtk_vif_destructor. And gprP2pRoleWdev[i]
		 * is reset as gprP2pWdev in mtk_vif_destructor.
		 */
		if (gprP2pRoleWdev[i] == gprP2pWdev)
			gprP2pWdev = NULL;

#if CFG_ENABLE_UNIFY_WIPHY
		wiphy = gprP2pRoleWdev[i]->wiphy;
#else
		set_wiphy_dev(gprP2pRoleWdev[i]->wiphy, NULL);
		wiphy_unregister(gprP2pRoleWdev[i]->wiphy);
		wiphy_free(gprP2pRoleWdev[i]->wiphy);
#endif

		kfree(gprP2pRoleWdev[i]);
		gprP2pRoleWdev[i] = NULL;
	}

	if (gprP2pWdev != NULL) {
		/* This case is that gprP2pWdev isn't equal to gprP2pRoleWdev[0]
		 * . The gprP2pRoleWdev[0] is created in the p2p cfg80211 add
		 * iface ops. The two wdev use the same wiphy. Don't double
		 * free the same wiphy.
		 * This part isn't expect occur. Because p2pNetUnregister should
		 * unregister_netdev the new created wdev, and gprP2pRoleWdev[0]
		 * is reset as gprP2pWdev.
		 */
#if CFG_ENABLE_UNIFY_WIPHY
		wiphy = gprP2pWdev->wiphy;
#endif

		kfree(gprP2pWdev);
		gprP2pWdev = NULL;
	}
#endif	/* CFG_ENABLE_WIFI_DIRECT */

	/* free AIS wdev */
	if (gprWdev) {
#if CFG_ENABLE_UNIFY_WIPHY
		wiphy = gprWdev->wiphy;
#else
		/* trunk doesn't do set_wiphy_dev, but trunk-ce1 does. */
		/* set_wiphy_dev(gprWdev->wiphy, NULL); */
		wiphy_unregister(gprWdev->wiphy);
		wiphy_free(gprWdev->wiphy);
#endif
		kfree(gprWdev);
		gprWdev = NULL;
	}

#if CFG_ENABLE_UNIFY_WIPHY
	/* unregister & free wiphy */
	if (wiphy) {
		/* set_wiphy_dev(wiphy, NULL): set the wiphy->dev->parent = NULL
		 * The trunk-ce1 does this, but the trunk seems not.
		 */
		/* set_wiphy_dev(wiphy, NULL); */
		wiphy_unregister(wiphy);
		wiphy_free(wiphy);
	}
#endif
}

void wlanWakeLockInit(struct GLUE_INFO *prGlueInfo)
{
#ifdef CONFIG_ANDROID
	KAL_WAKE_LOCK_INIT(NULL, prGlueInfo->prIntrWakeLock,
			   "WLAN interrupt");
	KAL_WAKE_LOCK_INIT(NULL, prGlueInfo->prTimeoutWakeLock,
			   "WLAN timeout");
#endif
}

void wlanWakeLockUninit(struct GLUE_INFO *prGlueInfo)
{
#if CFG_ENABLE_WAKE_LOCK
	if (KAL_WAKE_LOCK_ACTIVE(NULL, prGlueInfo->prIntrWakeLock))
		KAL_WAKE_UNLOCK(NULL, prGlueInfo->prIntrWakeLock);
	KAL_WAKE_LOCK_DESTROY(NULL, prGlueInfo->prIntrWakeLock);

	if (KAL_WAKE_LOCK_ACTIVE(NULL,
				 prGlueInfo->prTimeoutWakeLock))
		KAL_WAKE_UNLOCK(NULL, prGlueInfo->prTimeoutWakeLock);
	KAL_WAKE_LOCK_DESTROY(NULL, prGlueInfo->prTimeoutWakeLock);
#endif
}

/*----------------------------------------------------------------------------*/
/*!
 * \brief A method for creating Linux NET4 struct net_device object and the
 *        private data(prGlueInfo and prAdapter). Setup the IO address to the
 *        HIF.
 *        Assign the function pointer to the net_device object
 *
 * \param[in] pvData     Memory address for the device
 *
 * \retval Not null      The wireless_dev object.
 * \retval NULL          Fail to create wireless_dev object
 */
/*----------------------------------------------------------------------------*/
static struct lock_class_key rSpinKey[SPIN_LOCK_NUM];
static struct wireless_dev *wlanNetCreate(void *pvData,
		void *pvDriverData)
{
	struct wireless_dev *prWdev = gprWdev;
	struct GLUE_INFO *prGlueInfo = NULL;
	struct ADAPTER *prAdapter = NULL;
	uint32_t i;
	struct device *prDev;
	struct NETDEV_PRIVATE_GLUE_INFO *prNetDevPrivate =
		(struct NETDEV_PRIVATE_GLUE_INFO *) NULL;
	struct mt66xx_chip_info *prChipInfo;
#if CFG_ENABLE_UNIFY_WIPHY
	struct wiphy *prWiphy = NULL;
#endif

	uint8_t *prInfName = NULL;

	if (prWdev == NULL) {
		DBGLOG(INIT, ERROR,
		       "No wireless dev exist, abort power on\n");
		return NULL;
	}

#if CFG_ENABLE_UNIFY_WIPHY
	/* The gprWdev is created at initWlan() and isn't reset when the
	 * disconnection occur. That cause some issue.
	 */
	prWiphy = prWdev->wiphy;
	memset(prWdev, 0, sizeof(struct wireless_dev));
	prWdev->wiphy = prWiphy;
	prWdev->iftype = NL80211_IFTYPE_STATION;
#if (CFG_SUPPORT_SINGLE_SKU == 1)
	/* XXX: ref from cfg80211_regd_set_wiphy().
	 * The error case: Sometimes after unplug/plug usb, the wlan0 STA can't
	 * scan correctly (FW doesn't do scan). The usb_probe message:
	 * "mtk_reg_notify:(RLM ERROR) Invalid REG state happened. state = 0x6".
	 */
	if (rlmDomainGetCtrlState() == REGD_STATE_INVALID)
		rlmDomainResetCtrlInfo(TRUE);
#endif
#endif	/* CFG_ENABLE_UNIFY_WIPHY */

	/* 4 <1.3> co-relate wiphy & prDev */
	glGetDev(pvData, &prDev);
	if (!prDev) {
		DBGLOG(INIT, ERROR, "unable to get struct dev for wlan\n");
		return NULL;
	}
	/* Some kernel API (ex: cfg80211_get_drvinfo) will use wiphy_dev().
	 * Without set_wiphy_dev(prWdev->wiphy, prDev), those API will crash.
	 */
	set_wiphy_dev(prWdev->wiphy, prDev);

	/* 4 <2> Create Glue structure */
	prGlueInfo = (struct GLUE_INFO *) wiphy_priv(prWdev->wiphy);
	kalMemZero(prGlueInfo, sizeof(struct GLUE_INFO));

	/* 4 <2.1> Create Adapter structure */
	prAdapter = (struct ADAPTER *) wlanAdapterCreate(
			    prGlueInfo);

	if (!prAdapter) {
		DBGLOG(INIT, ERROR,
		       "Allocating memory to adapter failed\n");
		glClearHifInfo(prGlueInfo);
		goto netcreate_err;
	}

	prChipInfo = ((struct mt66xx_hif_driver_data *)
		      pvDriverData)->chip_info;
	prAdapter->chip_info = prChipInfo;
	prGlueInfo->prAdapter = prAdapter;

	/* 4 <3> Initialize Glue structure */
	/* 4 <3.1> Create net device */

#ifdef CFG_DRIVER_INF_NAME_CHANGE

	if (kalStrLen(gprifnamesta) > 0) {
		if (kalStrStr(gprifnamesta, "%d"))
			prInfName = gprifnamesta;
		else
			prInfName = kalStrCat(gprifnamesta, "%d");
		DBGLOG(INIT, WARN, "Station ifname customized, use %s\n",
		       prInfName);
	} else
#endif /* CFG_DRIVER_INF_NAME_CHANGE */
		prInfName = NIC_INF_NAME;

#if KERNEL_VERSION(3, 18, 0) <= LINUX_VERSION_CODE
	prGlueInfo->prDevHandler =
		alloc_netdev_mq(sizeof(struct NETDEV_PRIVATE_GLUE_INFO),
			prInfName, NET_NAME_PREDICTABLE, ether_setup,
			CFG_MAX_TXQ_NUM);
#else
	prGlueInfo->prDevHandler =
		alloc_netdev_mq(sizeof(struct NETDEV_PRIVATE_GLUE_INFO),
				prInfName,
				ether_setup, CFG_MAX_TXQ_NUM);
#endif

	if (!prGlueInfo->prDevHandler) {
		DBGLOG(INIT, ERROR,
		       "Allocating memory to net_device context failed\n");
		goto netcreate_err;
	}
	DBGLOG(INIT, INFO, "net_device prDev(0x%p) allocated\n",
	       prGlueInfo->prDevHandler);

	/* Device can help us to save at most 3000 packets, after we stopped
	** queue
	*/
	prGlueInfo->prDevHandler->tx_queue_len = 3000;

	/* 4 <3.1.1> Initialize net device varaiables */
#if 1
	prNetDevPrivate = (struct NETDEV_PRIVATE_GLUE_INFO *)
			  netdev_priv(prGlueInfo->prDevHandler);
	prNetDevPrivate->prGlueInfo = prGlueInfo;
#else
	*((struct GLUE_INFO **) netdev_priv(
		  prGlueInfo->prDevHandler)) = prGlueInfo;
#endif
	prGlueInfo->prDevHandler->needed_headroom +=
	NIC_TX_HEAD_ROOM;
/*
		NIC_TX_DESC_AND_PADDING_LENGTH +
		prChipInfo->txd_append_size;
*/
	prGlueInfo->prDevHandler->netdev_ops = &wlan_netdev_ops;
#ifdef CONFIG_WIRELESS_EXT
	prGlueInfo->prDevHandler->wireless_handlers =
		&wext_handler_def;
#endif
	netif_carrier_off(prGlueInfo->prDevHandler);
	netif_tx_stop_all_queues(prGlueInfo->prDevHandler);
	kalResetStats(prGlueInfo->prDevHandler);

#if CFG_SUPPORT_SNIFFER
	INIT_WORK(&(prGlueInfo->monWork), wlanMonWorkHandler);
#endif

	/* 4 <3.1.2> co-relate with wiphy bi-directionally */
	prGlueInfo->prDevHandler->ieee80211_ptr = prWdev;

	prWdev->netdev = prGlueInfo->prDevHandler;

	/* 4 <3.1.3> co-relate net device & prDev */
	SET_NETDEV_DEV(prGlueInfo->prDevHandler,
		       wiphy_dev(prWdev->wiphy));

	/* 4 <3.1.4> set device to glue */
	prGlueInfo->prDev = prDev;

	/* 4 <3.2> Initialize glue variables */
	prGlueInfo->eParamMediaStateIndicated =
		PARAM_MEDIA_STATE_DISCONNECTED;
	prGlueInfo->ePowerState = ParamDeviceStateD0;
	prGlueInfo->fgIsRegistered = FALSE;
	prGlueInfo->prScanRequest = NULL;
	prGlueInfo->prSchedScanRequest = NULL;


#if CFG_SUPPORT_PASSPOINT
	/* Init DAD */
	prGlueInfo->fgIsDad = FALSE;
	prGlueInfo->fgIs6Dad = FALSE;
	kalMemZero(prGlueInfo->aucDADipv4, 4);
	kalMemZero(prGlueInfo->aucDADipv6, 16);
#endif /* CFG_SUPPORT_PASSPOINT */

	init_completion(&prGlueInfo->rScanComp);
	init_completion(&prGlueInfo->rHaltComp);
	init_completion(&prGlueInfo->rPendComp);

#if CFG_SUPPORT_MULTITHREAD
	init_completion(&prGlueInfo->rHifHaltComp);
	init_completion(&prGlueInfo->rRxHaltComp);
#if CFG_SDIO_RX_DE_AGG_IN_THREAD
	init_completion(&prGlueInfo->rxDeAggHaltComp);
#endif
#endif

#if CFG_SUPPORT_CSI
	/* init CSI wait queue  */
	init_waitqueue_head(&(prGlueInfo->prAdapter->rCSIInfo.waitq));
#endif

#if CFG_SUPPORT_NCHO
	init_completion(&prGlueInfo->rAisChGrntComp);
#endif

	/* initialize timer for OID timeout checker */
	kalOsTimerInitialize(prGlueInfo, kalTimeoutHandler);

	for (i = 0; i < SPIN_LOCK_NUM; i++) {
		spin_lock_init(&prGlueInfo->rSpinLock[i]);
		lockdep_set_class(&prGlueInfo->rSpinLock[i], &rSpinKey[i]);
	}

	for (i = 0; i < MUTEX_NUM; i++) {
		mutex_init(&prGlueInfo->arMutex[i]);
		lockdep_set_subclass(&prGlueInfo->arMutex[i], i);
	}

	/* initialize semaphore for ioctl */
	sema_init(&prGlueInfo->ioctl_sem, 1);

#if CFG_SUPPORT_SDIO_READ_WRITE_PATTERN
	/* initialize SDIO read-write pattern control */
	prGlueInfo->fgEnSdioTestPattern = FALSE;
	prGlueInfo->fgIsSdioTestInitialized = FALSE;
#endif

	/* 4 <8> Init Queues */
	init_waitqueue_head(&prGlueInfo->waitq);
	QUEUE_INITIALIZE(&prGlueInfo->rCmdQueue);
	prGlueInfo->i4TxPendingCmdNum = 0;
	QUEUE_INITIALIZE(&prGlueInfo->rTxQueue);
	glSetHifInfo(prGlueInfo, (unsigned long) pvData);

	/* Init wakelock */
	wlanWakeLockInit(prGlueInfo);

	/* main thread is created in this function */
#if CFG_SUPPORT_MULTITHREAD
	init_waitqueue_head(&prGlueInfo->waitq_rx);
	init_waitqueue_head(&prGlueInfo->waitq_hif);

#if CFG_SDIO_RX_DE_AGG_IN_THREAD
	init_waitqueue_head(&prGlueInfo->waitq_rxDeAgg);
#endif

	prGlueInfo->u4TxThreadPid = 0xffffffff;
	prGlueInfo->u4RxThreadPid = 0xffffffff;
	prGlueInfo->u4HifThreadPid = 0xffffffff;
#endif

	return prWdev;

netcreate_err:
	if (prAdapter != NULL) {
		wlanAdapterDestroy(prAdapter);
		prAdapter = NULL;
	}

	if (prGlueInfo->prDevHandler != NULL) {
		free_netdev(prGlueInfo->prDevHandler);
		prGlueInfo->prDevHandler = NULL;
	}

	return NULL;
}				/* end of wlanNetCreate() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief Destroying the struct net_device object and the private data.
 *
 * \param[in] prWdev      Pointer to struct wireless_dev.
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
static void wlanNetDestroy(struct wireless_dev *prWdev)
{
	struct GLUE_INFO *prGlueInfo = NULL;

	ASSERT(prWdev);

	if (!prWdev) {
		DBGLOG(INIT, ERROR, "The device context is NULL\n");
		return;
	}

	/* prGlueInfo is allocated with net_device */
	prGlueInfo = (struct GLUE_INFO *) wiphy_priv(prWdev->wiphy);
	ASSERT(prGlueInfo);

	/* prWdev: base AIS dev
	 * Because the interface dev (ex: usb_device) would be free
	 * after un-plug event. Should set the wiphy->dev->parent which
	 * pointer to the interface dev to NULL. Otherwise, the corresponding
	 * system operation (poweroff, suspend) might reference it.
	 * set_wiphy_dev(wiphy, NULL): set the wiphy->dev->parent = NULL
	 * The set_wiphy_dev(prWdev->wiphy, prDev) is done in wlanNetCreate.
	 * But that is after wiphy_register, and will cause exception in
	 * wiphy_unregister(), if do not set_wiphy_dev(wiphy, NULL).
	 */
	set_wiphy_dev(prWdev->wiphy, NULL);

	/* destroy kal OS timer */
	kalCancelTimer(prGlueInfo);

	glClearHifInfo(prGlueInfo);

	wlanAdapterDestroy(prGlueInfo->prAdapter);
	prGlueInfo->prAdapter = NULL;

	/* Free net_device and private data, which are allocated by
	 * alloc_netdev().
	 */
	free_netdev(prWdev->netdev);

	/* gPrDev is assigned by prGlueInfo->prDevHandler,
	 * set NULL to this global variable.
	 */
	gPrDev = NULL;

}				/* end of wlanNetDestroy() */

void wlanSetSuspendMode(struct GLUE_INFO *prGlueInfo,
			u_int8_t fgEnable)
{
	struct net_device *prDev = NULL;
#if CFG_SUPPORT_DROP_MC_PACKET
	uint32_t u4PacketFilter = 0;
	uint32_t u4SetInfoLen = 0;
#endif

	if (!prGlueInfo)
		return;

	prDev = prGlueInfo->prDevHandler;
	if (!prDev)
		return;

	if (prGlueInfo->fgIsStaInSuspendMode == fgEnable)
		return;
	prGlueInfo->fgIsStaInSuspendMode = fgEnable;

#if CFG_SUPPORT_DROP_MC_PACKET
	/* new filter should not include p2p mask */
#if CFG_ENABLE_WIFI_DIRECT_CFG_80211
	u4PacketFilter = prGlueInfo->prAdapter->u4OsPacketFilter &
			 (~PARAM_PACKET_FILTER_P2P_MASK);
#endif
	if (kalIoctl(prGlueInfo,
		     wlanoidSetCurrentPacketFilter,
		     &u4PacketFilter,
		     sizeof(u4PacketFilter), FALSE, FALSE, TRUE,
		     &u4SetInfoLen) != WLAN_STATUS_SUCCESS)
		DBGLOG(INIT, ERROR, "set packet filter failed.\n");
#endif
	kalSetNetAddressFromInterface(prGlueInfo, prDev, fgEnable);
	wlanNotifyFwSuspend(prGlueInfo, prDev, fgEnable);
}

#if CFG_ENABLE_EARLY_SUSPEND
static struct early_suspend wlan_early_suspend_desc = {
	.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN,
};

static void wlan_early_suspend(struct early_suspend *h)
{
	struct net_device *prDev = NULL;
	struct GLUE_INFO *prGlueInfo = NULL;

	/* 4 <1> Sanity Check */
	if ((u4WlanDevNum == 0)
	    && (u4WlanDevNum > CFG_MAX_WLAN_DEVICES)) {
		DBGLOG(INIT, ERROR,
		       "wlanLateResume u4WlanDevNum==0 invalid!!\n");
		return;
	}

	prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev;
	if (!prDev)
		return;

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
	if (!prGlueInfo)
		return;

	DBGLOG(INIT, INFO, "********<%s>********\n", __func__);

	if (prGlueInfo->fgIsInSuspendMode == TRUE) {
		DBGLOG(INIT, INFO, "%s: Already in suspend mode, SKIP!\n",
		       __func__);
		return;
	}

	prGlueInfo->fgIsInSuspendMode = TRUE;

	wlanSetSuspendMode(prGlueInfo, TRUE);
	p2pSetSuspendMode(prGlueInfo, TRUE);
}

static void wlan_late_resume(struct early_suspend *h)
{
	struct net_device *prDev = NULL;
	struct GLUE_INFO *prGlueInfo = NULL;

	/* 4 <1> Sanity Check */
	if ((u4WlanDevNum == 0)
	    && (u4WlanDevNum > CFG_MAX_WLAN_DEVICES)) {
		DBGLOG(INIT, ERROR,
		       "wlanLateResume u4WlanDevNum==0 invalid!!\n");
		return;
	}

	prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev;
	if (!prDev)
		return;

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
	if (!prGlueInfo)
		return;

	DBGLOG(INIT, INFO, "********<%s>********\n", __func__);

	if (prGlueInfo->fgIsInSuspendMode == FALSE) {
		DBGLOG(INIT, INFO, "%s: Not in suspend mode, SKIP!\n",
		       __func__);
		return;
	}

	prGlueInfo->fgIsInSuspendMode = FALSE;

	/* 4 <2> Set suspend mode for each network */
	wlanSetSuspendMode(prGlueInfo, FALSE);
	p2pSetSuspendMode(prGlueInfo, FALSE);
}
#endif

#if (CFG_MTK_ANDROID_WMT || WLAN_INCLUDE_PROC)

int set_p2p_mode_handler(struct net_device *netdev,
			 struct PARAM_CUSTOM_P2P_SET_STRUCT p2pmode)
{
	struct GLUE_INFO *prGlueInfo = *((struct GLUE_INFO **)
					 netdev_priv(netdev));
	struct PARAM_CUSTOM_P2P_SET_STRUCT rSetP2P;
	uint32_t rWlanStatus = WLAN_STATUS_SUCCESS;
	uint32_t u4BufLen = 0;

	if (!prGlueInfo)
		return -1;

#if (CFG_MTK_ANDROID_WMT)
	if (prGlueInfo->u4ReadyFlag == 0) {
		DBGLOG(INIT, ERROR, "adapter is not ready\n");
		return -1;
	}
#endif /*CFG_MTK_ANDROID_WMT*/

	rSetP2P.u4Enable = p2pmode.u4Enable;
	rSetP2P.u4Mode = p2pmode.u4Mode;

	if ((!rSetP2P.u4Enable) && (kalIsResetting() == FALSE))
		p2pNetUnregister(prGlueInfo, FALSE);

	rWlanStatus = kalIoctl(prGlueInfo, wlanoidSetP2pMode,
			(void *) &rSetP2P,
			sizeof(struct PARAM_CUSTOM_P2P_SET_STRUCT),
			FALSE, FALSE, TRUE, &u4BufLen);

	DBGLOG(INIT, INFO, "set_p2p_mode_handler ret = 0x%08x\n",
	       (uint32_t) rWlanStatus);


	/* Need to check fgIsP2PRegistered, in case of whole chip reset.
	 * in this case, kalIOCTL return success always,
	 * and prGlueInfo->prP2PInfo[0] may be NULL
	 */
	if ((rSetP2P.u4Enable)
	    && (prGlueInfo->prAdapter->fgIsP2PRegistered)
	    && (kalIsResetting() == FALSE))
		p2pNetRegister(prGlueInfo, FALSE);

	return 0;
}

#endif

/*----------------------------------------------------------------------------*/
/*!
 * \brief get config from wifi.cfg
 *
 * \param[in] prAdapter
 *
 * \retval VOID
 */
/*----------------------------------------------------------------------------*/
void wlanGetConfig(struct ADAPTER *prAdapter)
{
	uint8_t *pucConfigBuf;
	uint32_t u4ConfigReadLen;
#if CFG_SUPPORT_MULTI_DONGLE
#define WIFI_CFG_FN	"wifi_mt7663.cfg"
#else
#define WIFI_CFG_FN	"wifi.cfg"
#endif
	wlanCfgInit(prAdapter, NULL, 0, 0);
	pucConfigBuf = (uint8_t *) kalMemAlloc(
			       WLAN_CFG_FILE_BUF_SIZE, VIR_MEM_TYPE);
	kalMemZero(pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE);
	u4ConfigReadLen = 0;
	if (pucConfigBuf) {
#ifdef CFG_FILE_NAME
		if (kalRequestFirmware(CFG_FILE_NAME, pucConfigBuf,
			   WLAN_CFG_FILE_BUF_SIZE, &u4ConfigReadLen,
			   prAdapter->prGlueInfo->prDev) == 0) {
		} else
#endif
		if (kalRequestFirmware(WIFI_CFG_FN, pucConfigBuf,
			   WLAN_CFG_FILE_BUF_SIZE, &u4ConfigReadLen,
			   prAdapter->prGlueInfo->prDev) == 0) {
			/* ToDo:: Nothing */
		} else if (kalReadToFile("/data/misc/wifi/wifi.cfg",
			   pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE,
			   &u4ConfigReadLen) == 0) {
			/* ToDo:: Nothing */
		} else if (kalReadToFile("/storage/sdcard0/wifi.cfg",
			   pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE,
			   &u4ConfigReadLen) == 0) {
			/* ToDo:: Nothing */
		}

		if (pucConfigBuf[0] != '\0' && u4ConfigReadLen > 0)
			wlanCfgInit(prAdapter,
				pucConfigBuf, u4ConfigReadLen, 0);

		kalMemFree(pucConfigBuf, VIR_MEM_TYPE,
			   WLAN_CFG_FILE_BUF_SIZE);
	}			/* pucConfigBuf */
}


/*----------------------------------------------------------------------------*/
/*!
 * \brief this function send buffer bin EEPROB_MTxxxx.bin to FW.
 *
 * \param[in] prAdapter
 *
 * \retval WLAN_STATUS_SUCCESS Success
 * \retval WLAN_STATUS_FAILURE Failed
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanDownloadBufferBin(struct ADAPTER *prAdapter)
{
	struct GLUE_INFO *prGlueInfo = NULL;
#if (CFG_FW_Report_Efuse_Address)
	uint16_t u2InitAddr = prAdapter->u4EfuseStartAddress;
#else
	uint16_t u2InitAddr = EFUSE_CONTENT_BUFFER_START;
#endif
	uint32_t u4BufLen = 0;
	uint32_t rStatus = WLAN_STATUS_SUCCESS;
	struct PARAM_CUSTOM_EFUSE_BUFFER_MODE *prSetEfuseBufModeInfo
			= NULL;
	uint32_t u4ContentLen;
	uint8_t *pucConfigBuf = NULL;
	struct mt66xx_chip_info *prChipInfo;
	uint32_t chip_id;
	uint8_t aucEeprom[32];
	uint32_t retWlanStat = WLAN_STATUS_FAILURE;

#if CFG_EFUSE_AUTO_MODE_SUPPORT
	uint32_t u4Efuse_addr = 0;
	struct PARAM_CUSTOM_ACCESS_EFUSE *prAccessEfuseInfo
			= NULL;
#endif

	if (prAdapter->fgIsSupportPowerOnSendBufferModeCMD ==
	    TRUE) {
		DBGLOG(INIT, INFO, "Start Efuse Buffer Mode ..\n");
		DBGLOG(INIT, INFO, "ucEfuseBUfferModeCal is %x\n",
		       prAdapter->rWifiVar.ucEfuseBufferModeCal);

		prChipInfo = prAdapter->chip_info;
		chip_id = prChipInfo->chip_id;
		prGlueInfo = prAdapter->prGlueInfo;

		if (prGlueInfo == NULL || prGlueInfo->prDev == NULL)
			goto label_exit;

		/* allocate memory for buffer mode info */
		prSetEfuseBufModeInfo =
			(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE *)
			kalMemAlloc(sizeof(
				    struct PARAM_CUSTOM_EFUSE_BUFFER_MODE),
				    VIR_MEM_TYPE);
		if (prSetEfuseBufModeInfo == NULL)
			goto label_exit;
		kalMemZero(prSetEfuseBufModeInfo,
			   sizeof(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE));

#if CFG_EFUSE_AUTO_MODE_SUPPORT
		/* allocate memory for Access Efuse Info */
		prAccessEfuseInfo =
			(struct PARAM_CUSTOM_ACCESS_EFUSE *)
			kalMemAlloc(sizeof(
				    struct PARAM_CUSTOM_ACCESS_EFUSE),
				    VIR_MEM_TYPE);
		if (prAccessEfuseInfo == NULL)
			goto label_exit;
		kalMemZero(prAccessEfuseInfo,
			   sizeof(struct PARAM_CUSTOM_ACCESS_EFUSE));

		if (prAdapter->rWifiVar.ucEfuseBufferModeCal == LOAD_AUTO) {
			prAccessEfuseInfo->u4Address = (u4Efuse_addr /
				EFUSE_BLOCK_SIZE) * EFUSE_BLOCK_SIZE;
			rStatus = kalIoctl(prGlueInfo,
				wlanoidQueryProcessAccessEfuseRead,
				prAccessEfuseInfo,
				sizeof(struct PARAM_CUSTOM_ACCESS_EFUSE),
				TRUE, TRUE, TRUE, &u4BufLen);
			if (prGlueInfo->prAdapter->aucEepromVaule[1]
				== (chip_id>>8)) {
				prAdapter->rWifiVar.ucEfuseBufferModeCal
					= LOAD_EFUSE;
				DBGLOG(INIT, STATE,
					"[EFUSE AUTO] EFUSE Mode\n");
			} else {
				prAdapter->rWifiVar.ucEfuseBufferModeCal
					= LOAD_EEPROM_BIN;
				DBGLOG(INIT, STATE,
					"[EFUSE AUTO] Buffer Mode\n");
			}
		}
#endif

		if (prAdapter->rWifiVar.ucEfuseBufferModeCal
			== LOAD_EEPROM_BIN) {
			/* Buffer mode */
			/* Only in buffer mode need to access bin file */
			/* 1 <1> Load bin file*/
			pucConfigBuf = (uint8_t *) kalMemAlloc(
					MAX_EEPROM_BUFFER_SIZE, VIR_MEM_TYPE);
			if (pucConfigBuf == NULL)
				goto label_exit;

			kalMemZero(pucConfigBuf, MAX_EEPROM_BUFFER_SIZE);

			/* 1 <2> Construct EEPROM binary name */
			kalMemZero(aucEeprom, sizeof(aucEeprom));

			snprintf(aucEeprom, 32, "%s%x.bin",
				 apucEepromName[0], chip_id);

			/* 1 <3> Request buffer bin */
			if (kalRequestFirmware(aucEeprom, pucConfigBuf,
			    MAX_EEPROM_BUFFER_SIZE, &u4ContentLen,
			    prGlueInfo->prDev) == 0) {
				DBGLOG(INIT, INFO, "request file done\n");
			} else {
				DBGLOG(INIT, INFO, "can't find file\n");
				goto label_exit;
			}

			/* 1 <4> Send CMD with bin file content */
			prGlueInfo = prAdapter->prGlueInfo;

			/* Update contents in local table */
			kalMemCopy(uacEEPROMImage, pucConfigBuf,
				   MAX_EEPROM_BUFFER_SIZE);

			/* copy to the command buffer */
#if (CFG_FW_Report_Efuse_Address)
			u4ContentLen = (prAdapter->u4EfuseEndAddress) -
				       (prAdapter->u4EfuseStartAddress) + 1;
#else
			u4ContentLen = EFUSE_CONTENT_BUFFER_SIZE;
#endif
			if (u4ContentLen > MAX_EEPROM_BUFFER_SIZE)
				goto label_exit;
			kalMemCopy(prSetEfuseBufModeInfo->aBinContent,
				   &pucConfigBuf[u2InitAddr], u4ContentLen);

			prSetEfuseBufModeInfo->ucSourceMode = 1;
		} else {
			/* eFuse mode */
			/* Only need to tell FW the content from, contents are
			 * directly from efuse
			 */
			prSetEfuseBufModeInfo->ucSourceMode = 0;
		}
		prSetEfuseBufModeInfo->ucCmdType = 0x1 |
				   (prAdapter->rWifiVar.ucCalTimingCtrl << 4);
		prSetEfuseBufModeInfo->ucCount   =
			0xFF; /* ucCmdType 1 don't care the ucCount */

		rStatus = kalIoctl(prGlueInfo, wlanoidSetEfusBufferMode,
				(void *)prSetEfuseBufModeInfo,
				sizeof(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE),
				FALSE, TRUE, TRUE, &u4BufLen);
	}

	retWlanStat = WLAN_STATUS_SUCCESS;

label_exit:

	/* free memory */
	if (prSetEfuseBufModeInfo != NULL)
		kalMemFree(prSetEfuseBufModeInfo, VIR_MEM_TYPE,
			   sizeof(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE));

	if (pucConfigBuf != NULL)
		kalMemFree(pucConfigBuf, VIR_MEM_TYPE,
			   MAX_EEPROM_BUFFER_SIZE);

#if CFG_EFUSE_AUTO_MODE_SUPPORT
	if (prAccessEfuseInfo != NULL)
		kalMemFree(prAccessEfuseInfo, VIR_MEM_TYPE,
			sizeof(struct PARAM_CUSTOM_ACCESS_EFUSE));
#endif

	return retWlanStat;
}

/*----------------------------------------------------------------------------*/
/*!
 * \brief this function send buffer bin EEPROB_MTxxxx.bin to FW.
 *
 * \param[in] prAdapter
 *
 * \retval WLAN_STATUS_SUCCESS Success
 * \retval WLAN_STATUS_FAILURE Failed
 */
/*----------------------------------------------------------------------------*/
uint32_t wlanConnacDownloadBufferBin(struct ADAPTER
				     *prAdapter)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	uint32_t u4BufLen = 0;
	uint32_t rStatus = WLAN_STATUS_SUCCESS;
	struct PARAM_CUSTOM_EFUSE_BUFFER_MODE_CONNAC_T
		*prSetEfuseBufModeInfo = NULL;
	uint32_t u4ContentLen;
	uint8_t *pucConfigBuf = NULL;
	struct mt66xx_chip_info *prChipInfo;
	uint32_t chip_id;
	uint8_t aucEeprom[32];
	uint32_t retWlanStat = WLAN_STATUS_FAILURE;
	struct CAP_BUFFER_MODE_INFO_T *prBufferModeInfo = NULL;
	int ret = 0;

#if CFG_EFUSE_AUTO_MODE_SUPPORT
	uint32_t u4Efuse_addr = 0;
	struct PARAM_CUSTOM_ACCESS_EFUSE *prAccessEfuseInfo
			= NULL;
#endif

	if (prAdapter->fgIsSupportPowerOnSendBufferModeCMD == FALSE)
		return WLAN_STATUS_SUCCESS;

	DBGLOG(INIT, INFO, "Start Efuse Buffer Mode ..\n");
	DBGLOG(INIT, INFO, "ucEfuseBUfferModeCal is %x\n",
	       prAdapter->rWifiVar.ucEfuseBufferModeCal);

	prChipInfo = prAdapter->chip_info;
	chip_id = prChipInfo->chip_id;
	prGlueInfo = prAdapter->prGlueInfo;
	prBufferModeInfo = &prAdapter->rBufferModeInfo;

	if (prGlueInfo == NULL || prGlueInfo->prDev == NULL)
		goto label_exit;

	/* allocate memory for buffer mode info */
	prSetEfuseBufModeInfo =
		(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE_CONNAC_T *)
		kalMemAlloc(sizeof(
			    struct PARAM_CUSTOM_EFUSE_BUFFER_MODE_CONNAC_T),
			    VIR_MEM_TYPE);
	if (prSetEfuseBufModeInfo == NULL)
		goto label_exit;
	kalMemZero(prSetEfuseBufModeInfo,
		   sizeof(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE_CONNAC_T));

#if CFG_EFUSE_AUTO_MODE_SUPPORT
	/* allocate memory for Access Efuse Info */
	prAccessEfuseInfo =
		(struct PARAM_CUSTOM_ACCESS_EFUSE *)
			kalMemAlloc(sizeof(
				struct PARAM_CUSTOM_ACCESS_EFUSE),
					VIR_MEM_TYPE);
	if (prAccessEfuseInfo == NULL)
		goto label_exit;
	kalMemZero(prAccessEfuseInfo,
		sizeof(struct PARAM_CUSTOM_ACCESS_EFUSE));

	if (prAdapter->rWifiVar.ucEfuseBufferModeCal == LOAD_AUTO) {
		prAccessEfuseInfo->u4Address = (u4Efuse_addr /
			EFUSE_BLOCK_SIZE) * EFUSE_BLOCK_SIZE;
		rStatus = kalIoctl(prGlueInfo,
			wlanoidQueryProcessAccessEfuseRead,
			prAccessEfuseInfo,
			sizeof(struct PARAM_CUSTOM_ACCESS_EFUSE),
			TRUE, TRUE, TRUE, &u4BufLen);
		if (prGlueInfo->prAdapter->aucEepromVaule[1]
			== (chip_id>>8)) {
			prAdapter->rWifiVar.ucEfuseBufferModeCal
				= LOAD_EFUSE;
			DBGLOG(INIT, STATE,
				"[EFUSE AUTO] EFUSE Mode\n");
		} else {
			prAdapter->rWifiVar.ucEfuseBufferModeCal
				= LOAD_EEPROM_BIN;
			DBGLOG(INIT, STATE,
				"[EFUSE AUTO] Buffer Mode\n");
		}
	}
#endif

	if (prAdapter->rWifiVar.ucEfuseBufferModeCal == LOAD_EEPROM_BIN) {
		/* Buffer mode */
		/* Only in buffer mode need to access bin file */
		/* 1 <1> Load bin file*/
		pucConfigBuf = (uint8_t *) kalMemAlloc(
				       MAX_EEPROM_BUFFER_SIZE, VIR_MEM_TYPE);
		if (pucConfigBuf == NULL)
			goto label_exit;

		kalMemZero(pucConfigBuf, MAX_EEPROM_BUFFER_SIZE);

		/* 1 <2> Construct EEPROM binary name */
		kalMemZero(aucEeprom, sizeof(aucEeprom));

		ret = kalSnprintf(aucEeprom, sizeof(aucEeprom), "%s%x.bin",
			 apucEepromName[0], chip_id);
		if (ret == 0 || ret >= sizeof(aucEeprom)) {
			DBGLOG(INIT, ERROR,
				"[%u] kalSnprintf failed, ret: %d\n",
				__LINE__, ret);
			goto label_exit;
		}

		/* 1 <3> Request buffer bin */
		if (kalRequestFirmware(aucEeprom, pucConfigBuf,
		    MAX_EEPROM_BUFFER_SIZE, &u4ContentLen, prGlueInfo->prDev)
		    == 0) {
			DBGLOG(INIT, INFO, "request file done\n");
		} else {
			DBGLOG(INIT, INFO, "can't find file\n");
			goto label_exit;
		}
		DBGLOG(INIT, INFO, "u4ContentLen = %d\n", u4ContentLen);

		if (u4ContentLen > MAX_EEPROM_BUFFER_SIZE) {
			DBGLOG(INIT, ERROR, "u4ContentLen: %d > %d\n",
			       u4ContentLen, MAX_EEPROM_BUFFER_SIZE);
			goto label_exit;
		}

		/* Update contents in local table */
		kalMemCopy(uacEEPROMImage, pucConfigBuf,
			   u4ContentLen);

		/* 1 <4> Send CMD with bin file content */
		if (prBufferModeInfo->ucVersion != 0x02) {
			DBGLOG(INIT, ERROR, "Not support version\n");
			goto label_exit;
		}

		if (prBufferModeInfo->ucFormatSupportBitmap &
		    BIT(CONTENT_FORMAT_WHOLE_CONTENT)) {
			if (u4ContentLen > prBufferModeInfo->u2EfuseTotalSize) {
				DBGLOG(INIT, ERROR, "u4ContentLen: %d > %d\n",
				       u4ContentLen,
				       prBufferModeInfo->u2EfuseTotalSize);
				goto label_exit;
			}

			kalMemCopy(prSetEfuseBufModeInfo->aBinContent,
				   pucConfigBuf, u4ContentLen);
		} else if (prBufferModeInfo->ucFormatSupportBitmap &
			   BIT(CONTENT_FORMAT_MULTIPLE_SECTIONS)) {
			/* TODO */
			DBGLOG(INIT, ERROR, "Not support yet!\n");
			goto label_exit;
		} else {
			DBGLOG(INIT, ERROR,
			       "Not support legacy BIN_CONTENT mode\n");
			goto label_exit;
		}

		prSetEfuseBufModeInfo->ucSourceMode = SOURCE_MODE_BUFFER_MODE;
#if CFG_RCPI_COMPENSATION
		wlanLoadBufferbinRxFELoss(prAdapter);
#endif
#if CFG_SUPPORT_HW_1T2R
		wlanLoadBufferbin1T2R(prAdapter);
#endif
	} else {
		/* eFuse mode */
		/* Only need to tell FW the content from, contents are directly
		 * from efuse
		 */
		prSetEfuseBufModeInfo->ucSourceMode = SOURCE_MODE_EFUSE;
		u4ContentLen = 0;
#if CFG_RCPI_COMPENSATION
		wlanLoadEfuseRxFELoss(prAdapter);
#endif
#if CFG_SUPPORT_HW_1T2R
		wlanLoadEfuse1T2R(prAdapter);
#endif
	}
	prSetEfuseBufModeInfo->ucContentFormat = CONTENT_FORMAT_WHOLE_CONTENT |
			(prAdapter->rWifiVar.ucCalTimingCtrl <<
			 CMD_TYPE_CAL_TIME_REDUCTION_SHFT);
	prSetEfuseBufModeInfo->u2Count = u4ContentLen;

	rStatus = kalIoctl(prGlueInfo, wlanoidConnacSetEfusBufferMode,
		(void *)prSetEfuseBufModeInfo,
		OFFSET_OF(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE_CONNAC_T,
		aBinContent) + u4ContentLen,
		FALSE, TRUE, TRUE, &u4BufLen);

	retWlanStat = WLAN_STATUS_SUCCESS;

label_exit:

	/* free memory */
	if (prSetEfuseBufModeInfo != NULL)
		kalMemFree(prSetEfuseBufModeInfo, VIR_MEM_TYPE,
			sizeof(struct PARAM_CUSTOM_EFUSE_BUFFER_MODE_CONNAC_T));

	if (pucConfigBuf != NULL)
		kalMemFree(pucConfigBuf, VIR_MEM_TYPE,
			   MAX_EEPROM_BUFFER_SIZE);

#if CFG_EFUSE_AUTO_MODE_SUPPORT
	if (prAccessEfuseInfo != NULL)
		kalMemFree(prAccessEfuseInfo, VIR_MEM_TYPE,
			sizeof(struct PARAM_CUSTOM_ACCESS_EFUSE));
#endif

	return retWlanStat;
}

#if (CFG_MTK_CONNSYS_DEDICATED_LOG_PATH == 1)

#define FW_LOG_CMD_ON_OFF        0
#define FW_LOG_CMD_SET_LEVEL     1
static uint32_t u4LogOnOffCache = -1;

static void consys_log_event_notification(int cmd, int value)
{
	struct GLUE_INFO *prGlueInfo = NULL;
	struct ADAPTER *prAdapter = NULL;
	struct net_device *prDev = gPrDev;
	uint32_t rStatus = WLAN_STATUS_FAILURE;
	struct CMD_HEADER rCmdV1Header;
	struct CMD_FORMAT_V1 rCmd_v1;

	DBGLOG(INIT, INFO, "gPrDev=%p, cmd=%d, value=%d\n",
		gPrDev, cmd, value);

	if (kalIsHalted()) { /* power-off */
		u4LogOnOffCache = value;
		DBGLOG(INIT, INFO,
			"Power off return, u4LogOnOffCache=%d\n",
				u4LogOnOffCache);
		return;
	}

	prGlueInfo = (prDev != NULL) ?
		*((struct GLUE_INFO **) netdev_priv(prDev)) : NULL;
	DBGLOG(INIT, TRACE, "prGlueInfo=%p\n", prGlueInfo);
	if (!prGlueInfo) {
		u4LogOnOffCache = value;
		DBGLOG(INIT, INFO,
			"prGlueInfo == NULL return, u4LogOnOffCache=%d\n",
				u4LogOnOffCache);
		return;
	}
	prAdapter = prGlueInfo->prAdapter;
	DBGLOG(INIT, TRACE, "prAdapter=%p\n", prAdapter);
	if (!prAdapter) {
		u4LogOnOffCache = value;
		DBGLOG(INIT, INFO,
			"prAdapter == NULL return, u4LogOnOffCache=%d\n",
				u4LogOnOffCache);
		return;
	}

	if (cmd == FW_LOG_CMD_ON_OFF) {

		/*EvtDrvnLogEn 0/1*/
		uint8_t onoff[1] = {'0'};

		DBGLOG(INIT, TRACE, "FW_LOG_CMD_ON_OFF\n");

		rCmdV1Header.cmdType = CMD_TYPE_SET;
		rCmdV1Header.cmdVersion = CMD_VER_1;
		rCmdV1Header.cmdBufferLen = 0;
		rCmdV1Header.itemNum = 0;

		kalMemSet(rCmdV1Header.buffer, 0, MAX_CMD_BUFFER_LENGTH);
		kalMemSet(&rCmd_v1, 0, sizeof(struct CMD_FORMAT_V1));

		rCmd_v1.itemType = ITEM_TYPE_STR;

		/*send string format to firmware */
		rCmd_v1.itemStringLength = kalStrLen("EnableDbgLog");
		kalMemZero(rCmd_v1.itemString, MAX_CMD_NAME_MAX_LENGTH);
		kalMemCopy(rCmd_v1.itemString, "EnableDbgLog",
			rCmd_v1.itemStringLength);

		if (value == 1) /* other cases, send 'OFF=0' */
			onoff[0] = '1';
		rCmd_v1.itemValueLength = 1;
		kalMemZero(rCmd_v1.itemValue, MAX_CMD_VALUE_MAX_LENGTH);
		kalMemCopy(rCmd_v1.itemValue, &onoff, 1);

		DBGLOG(INIT, INFO, "Send key word (%s) WITH (%s) to firmware\n",
				rCmd_v1.itemString, rCmd_v1.itemValue);

		kalMemCopy(((struct CMD_FORMAT_V1 *)rCmdV1Header.buffer),
				&rCmd_v1,  sizeof(struct CMD_FORMAT_V1));

		rCmdV1Header.cmdBufferLen += sizeof(struct CMD_FORMAT_V1);
		rCmdV1Header.itemNum = 1;

		rStatus = wlanSendSetQueryCmd(
				prAdapter, /* prAdapter */
				CMD_ID_GET_SET_CUSTOMER_CFG, /* 0x70 */
				TRUE,  /* fgSetQuery */
				FALSE, /* fgNeedResp */
				FALSE, /* fgIsOid */
				NULL,  /* pfCmdDoneHandler*/
				NULL,  /* pfCmdTimeoutHandler */
				sizeof(struct CMD_HEADER),
				(uint8_t *)&rCmdV1Header, /* pucInfoBuffer */
				NULL,  /* pvSetQueryBuffer */
				0      /* u4SetQueryBufferLen */
			);

		if (rStatus == WLAN_STATUS_FAILURE)
			DBGLOG(INIT, INFO,
				"[Fail]kalIoctl wifiSefCFG fail 0x%x\n",
					rStatus);

		/* keep in cache */
		u4LogOnOffCache = value;
	} else if (cmd == FW_LOG_CMD_SET_LEVEL) {
		/*ENG_LOAD_OFFSET 1*/
		/*USERDEBUG_LOAD_OFFSET 2 */
		/*USER_LOAD_OFFSET 3 */
		DBGLOG(INIT, INFO, "FW_LOG_CMD_SET_LEVEL\n");
	} else {
		DBGLOG(INIT, INFO, "command can not parse\n");
	}
}
#endif

u_int8_t wlanIsProbing(void)
{
	return atomic_read(&g_wlanProbing) ? TRUE : FALSE;
}

u_int8_t wlanIsRemoving(void)
{
	return atomic_read(&g_wlanRemoving) ? TRUE : FALSE;
}

/*----------------------------------------------------------------------------*/
/*!
 * \brief Wlan probe function. This function probes and initializes the device.
 *
 * \param[in] pvData     data passed by bus driver init function
 *                           _HIF_EHPI: NULL
 *                           _HIF_SDIO: sdio bus driver handle
 *
 * \retval 0 Success
 * \retval negative value Failed
 */
/*----------------------------------------------------------------------------*/
static int32_t wlanProbe(void *pvData, void *pvDriverData)
{
	struct wireless_dev *prWdev = NULL;
	enum ENUM_PROBE_FAIL_REASON {
		BUS_INIT_FAIL,
		NET_CREATE_FAIL,
		BUS_SET_IRQ_FAIL,
		ADAPTER_START_FAIL,
		MAIN_TREAD_CREATE_FAIL,
		NON_MAIN_TREAD_CREATE_FAIL,
		NET_REGISTER_FAIL,
		PROC_INIT_FAIL,
		FAIL_MET_INIT_PROCFS,
		FAIL_REASON_NUM
	} eFailReason;
	struct WLANDEV_INFO *prWlandevInfo = NULL;
	int32_t i4DevIdx = 0;
	struct GLUE_INFO *prGlueInfo = NULL;
	struct ADAPTER *prAdapter = NULL;
	int32_t i4Status = 0;
	u_int8_t bRet = FALSE;
	u_int8_t i = 0;
	struct REG_INFO *prRegInfo;
	struct mt66xx_chip_info *prChipInfo;
#if CFG_SUPPORT_REPLAY_DETECTION
	u_int8_t ucRpyDetectOffload;
#endif

#if CFG_THERMAL_API_SUPPORT
	struct wcn_platform_bridge pbridge;
#endif

#if (MTK_WCN_HIF_SDIO && CFG_WMT_WIFI_PATH_SUPPORT)
	int32_t i4RetVal = 0;
#endif
	int32_t u4LogLevel = ENUM_WIFI_LOG_LEVEL_DEFAULT;

#if 0
	uint8_t *pucConfigBuf = NULL, pucCfgBuf = NULL;
	uint32_t u4ConfigReadLen = 0;
#endif

	if (atomic_read(&g_wlanProbing))
		return 0;
	atomic_set(&g_wlanProbing, 1);

	eFailReason = FAIL_REASON_NUM;
#if CFG_CHIP_RESET_SUPPORT
	fgIsResetting = FALSE;
#if CFG_CHIP_RESET_KO_SUPPORT
	send_reset_event(RESET_MODULE_TYPE_WIFI, RFSM_EVENT_START_PROBE);
#endif
#endif

	do {
		/* 4 <1> Initialize the IO port of the interface */
		/*  GeorgeKuo: pData has different meaning for _HIF_XXX:
		 * _HIF_EHPI: pointer to memory base variable, which will be
		 *      initialized by glBusInit().
		 * _HIF_SDIO: bus driver handle
		 */

		DBGLOG(INIT, STATE, "enter wlanProbe\n");

		bRet = glBusInit(pvData);

#if (CFG_SUPPORT_TRACE_TC4 == 1)
		wlanDebugTC4Init();
#endif
		/* Cannot get IO address from interface */
		if (bRet == FALSE) {
			DBGLOG(INIT, ERROR, "wlanProbe: glBusInit() fail\n");
			i4Status = -EIO;
			eFailReason = BUS_INIT_FAIL;
			break;
		}
		/* 4 <2> Create network device, Adapter, KalInfo,
		 *       prDevHandler(netdev)
		 */
		prWdev = wlanNetCreate(pvData, pvDriverData);
		if (prWdev == NULL) {
			DBGLOG(INIT, ERROR,
			       "wlanProbe: No memory for dev and its private\n");
			i4Status = -ENOMEM;
			eFailReason = NET_CREATE_FAIL;
			break;
		}
		/* 4 <2.5> Set the ioaddr to HIF Info */
		prGlueInfo = (struct GLUE_INFO *) wiphy_priv(prWdev->wiphy);
		if (prGlueInfo == NULL) {
			DBGLOG(INIT, ERROR,
				"wlanProbe: get wiphy_priv() fail\n");
			i4Status = -EFAULT;
			break;
		}

		gPrDev = prGlueInfo->prDevHandler;

		/* 4 <4> Setup IRQ */
		prWlandevInfo = &arWlanDevInfo[i4DevIdx];

		i4Status = glBusSetIrq(prWdev->netdev, NULL, prGlueInfo);

		if (i4Status != WLAN_STATUS_SUCCESS) {
			DBGLOG(INIT, ERROR, "wlanProbe: Set IRQ error\n");
			eFailReason = BUS_SET_IRQ_FAIL;
			break;
		}

		prGlueInfo->i4DevIdx = i4DevIdx;

		prAdapter = prGlueInfo->prAdapter;

		prGlueInfo->u4ReadyFlag = 0;

#if CFG_TCP_IP_CHKSUM_OFFLOAD
		prAdapter->fgIsSupportCsumOffload = FALSE;
		prAdapter->u4CSUMFlags = CSUM_OFFLOAD_EN_ALL;
#endif

#if CFG_SUPPORT_CFG_FILE
		wlanGetConfig(prAdapter);
#endif

		/* Init Chip Capability */
		prChipInfo = prAdapter->chip_info;
		if (prChipInfo->asicCapInit)
			prChipInfo->asicCapInit(prAdapter);


		/* Default support 2.4/5G MIMO */
		prAdapter->rWifiFemCfg.u2WifiPath = (
				WLAN_FLAG_2G4_WF0 | WLAN_FLAG_5G_WF0 |
				WLAN_FLAG_2G4_WF1 | WLAN_FLAG_5G_WF1);

#if (MTK_WCN_HIF_SDIO && CFG_WMT_WIFI_PATH_SUPPORT)
		i4RetVal = mtk_wcn_wmt_wifi_fem_cfg_report((
					void *)&prAdapter->rWifiFemCfg);
		if (i4RetVal)
			DBGLOG(INIT, ERROR, "Get WifiPath from WMT drv fail\n");
		else
			DBGLOG(INIT, INFO,
			       "Get WifiPath from WMT drv success, WifiPath=0x%x\n",
			       prAdapter->rWifiFemCfg.u2WifiPath);
#endif
		/* 4 <5> Start Device */
		prRegInfo = &prGlueInfo->rRegInfo;

		/* P_REG_INFO_T prRegInfo = (P_REG_INFO_T) kmalloc(
		 *				sizeof(REG_INFO_T), GFP_KERNEL);
		 */
		kalMemSet(prRegInfo, 0, sizeof(struct REG_INFO));

		/* Trigger the action of switching Pwr state to drv_own */
		prAdapter->fgIsFwOwn = TRUE;

		/* Load NVRAM content to REG_INFO_T */
		glLoadNvram(prGlueInfo, prRegInfo);

		/* kalMemCopy(&prGlueInfo->rRegInfo, prRegInfo,
		 *            sizeof(REG_INFO_T));
		 */

		prRegInfo->u4PowerMode = CFG_INIT_POWER_SAVE_PROF;
#if 0
		prRegInfo->fgEnArpFilter = TRUE;
#endif
		/* The Init value of u4WpaVersion/u4AuthAlg shall be
		 * DISABLE/OPEN, not zero!
		 */
		/* The Init value of u4CipherGroup/u4CipherPairwise shall be
		 * NONE, not zero!
		 */
		prGlueInfo->rWpaInfo.u4WpaVersion =
			IW_AUTH_WPA_VERSION_DISABLED;
		prGlueInfo->rWpaInfo.u4AuthAlg = IW_AUTH_ALG_OPEN_SYSTEM;
		prGlueInfo->rWpaInfo.u4CipherGroup = IW_AUTH_CIPHER_NONE;
		prGlueInfo->rWpaInfo.u4CipherPairwise = IW_AUTH_CIPHER_NONE;

		tasklet_init(&prGlueInfo->rRxTask, halRxTasklet,
			     (unsigned long)prGlueInfo);
		tasklet_init(&prGlueInfo->rTxCompleteTask,
			     halTxCompleteTasklet, (unsigned long)prGlueInfo);

		if (wlanAdapterStart(prAdapter,
				     prRegInfo) != WLAN_STATUS_SUCCESS)
			i4Status = -EIO;

		if (HAL_IS_TX_DIRECT(prAdapter)) {
			if (!prAdapter->fgTxDirectInited) {
				skb_queue_head_init(
						&prAdapter->rTxDirectSkbQueue);

#if KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE
				timer_setup(&prAdapter->rTxDirectSkbTimer,
					nicTxDirectTimerCheckSkbQ, 0);

				timer_setup(&prAdapter->rTxDirectHifTimer,
					nicTxDirectTimerCheckHifQ, 0);
#else
				init_timer(&prAdapter->rTxDirectSkbTimer);
				prAdapter->rTxDirectSkbTimer.data =
						(unsigned long)prGlueInfo;
				prAdapter->rTxDirectSkbTimer.function =
						nicTxDirectTimerCheckSkbQ;

				init_timer(&prAdapter->rTxDirectHifTimer);
				prAdapter->rTxDirectHifTimer.data =
						(unsigned long)prGlueInfo;
				prAdapter->rTxDirectHifTimer.function =
					nicTxDirectTimerCheckHifQ;
#endif
				prAdapter->fgTxDirectInited = TRUE;
			}
		}

		/* kfree(prRegInfo); */

		if (i4Status < 0) {
			eFailReason = ADAPTER_START_FAIL;
			break;
		}

#if (CFG_SUPPORT_TX_TSO_SW == 1)
		skb_queue_head_init(&prAdapter->rTsoQueue);
#endif

		INIT_WORK(&prGlueInfo->rTxMsduFreeWork, kalFreeTxMsduWorker);

		INIT_DELAYED_WORK(&prGlueInfo->rRxPktDeAggWork,
				halDeAggRxPktWorker);

#if CFG_SUPPORT_CFG80211_AUTH
#if CFG_SUPPORT_CFG80211_QUEUE
		INIT_DELAYED_WORK(&cfg80211_workq,
				wlanSchedCfg80211WorkQueue);
#endif
#endif

		prGlueInfo->main_thread = kthread_run(main_thread,
				prGlueInfo->prDevHandler, "main_thread");
		if (IS_ERR(prGlueInfo->main_thread)) {
			DBGLOG(INIT, ERROR,
			       "fail to create and wake main_thread\n");
			i4Status = -1;
			eFailReason = MAIN_TREAD_CREATE_FAIL;
			break;
		}

#if CFG_SUPPORT_MULTITHREAD
		prGlueInfo->hif_thread = kthread_run(hif_thread,
				prGlueInfo->prDevHandler, "hif_thread");
		if (IS_ERR(prGlueInfo->hif_thread)) {
			DBGLOG(INIT, ERROR,
			       "fail to create and wake hif_thread\n");
			i4Status = -1;
			eFailReason = NON_MAIN_TREAD_CREATE_FAIL;
			break;
		}
		prGlueInfo->rx_thread = kthread_run(rx_thread,
				prGlueInfo->prDevHandler, "rx_thread");
		if (IS_ERR(prGlueInfo->rx_thread)) {
			DBGLOG(INIT, ERROR,
			       "fail to create and wake rx_thread\n");
			i4Status = -1;
			eFailReason = NON_MAIN_TREAD_CREATE_FAIL;
			break;
		}
		HAL_AGG_THREAD(prGlueInfo->prAdapter);
#if CFG_SDIO_RX_DE_AGG_IN_THREAD
		prGlueInfo->rx_DeAgg_thread = kthread_run(sdio_rx_DeAgg_thread,
					prGlueInfo->prDevHandler,
					"sdio_rx_DeAgg_thread");
		if (IS_ERR(prGlueInfo->rx_DeAgg_thread)) {
			DBGLOG(INIT, ERROR,
			       "fail to create and wake rx_DeAgg_thread\n");
			i4Status = -1;
			eFailReason = NON_MAIN_TREAD_CREATE_FAIL;
			break;
		}
#endif
#endif

		g_u4HaltFlag = 0;

#if CFG_SUPPORT_BUFFER_MODE
#if (CFG_EFUSE_BUFFER_MODE_DELAY_CAL == 1)
		if (prChipInfo->downloadBufferBin) {
			if (prChipInfo->downloadBufferBin(prAdapter) !=
			    WLAN_STATUS_SUCCESS){
				DBGLOG(INIT, ERROR,
					"wlanProbe: downloadBufferBin fail\n");
				i4Status = -1;
				goto WLAN_PROBE_RETURN;
			}
		}
#endif
#endif

#if CFG_SUPPORT_DBDC
		/* Update DBDC default setting */
		cnmInitDbdcSetting(prAdapter);
#endif /*CFG_SUPPORT_DBDC*/

		/* send regulatory information to firmware */
		rlmDomainSendInfoToFirmware(prAdapter);

		/* set MAC address */
		{
			uint32_t rStatus = WLAN_STATUS_FAILURE;
			struct sockaddr MacAddr;
			uint32_t u4SetInfoLen = 0;

			rStatus = kalIoctl(prGlueInfo, wlanoidQueryCurrentAddr,
					   &MacAddr.sa_data, PARAM_MAC_ADDR_LEN,
					   TRUE, TRUE, TRUE, &u4SetInfoLen);

			if (rStatus != WLAN_STATUS_SUCCESS) {
				DBGLOG(INIT, WARN, "set MAC addr fail 0x%x\n",
								rStatus);
			} else {
				kal_eth_hw_addr_set(
					prGlueInfo->prDevHandler,
					MacAddr.sa_data);
				kalMemCopy(prGlueInfo->prDevHandler->perm_addr,
					   prGlueInfo->prDevHandler->dev_addr,
					   ETH_ALEN);

#if CFG_SHOW_MACADDR_SOURCE
				DBGLOG(INIT, INFO, "MAC address: " MACSTR,
				MAC2STR(prAdapter->rWifiVar.aucMacAddress));
#endif
			}
		}

#if CFG_TCP_IP_CHKSUM_OFFLOAD
		/* set HW checksum offload */
		if (prAdapter->fgIsSupportCsumOffload) {
			uint32_t rStatus = WLAN_STATUS_FAILURE;
			uint32_t u4CSUMFlags = CSUM_OFFLOAD_EN_ALL;
			uint32_t u4SetInfoLen = 0;

			rStatus = kalIoctl(prGlueInfo, wlanoidSetCSUMOffload,
					   (void *) &u4CSUMFlags,
					   sizeof(uint32_t),
					   FALSE, FALSE, TRUE, &u4SetInfoLen);

			if (rStatus == WLAN_STATUS_SUCCESS) {
				prGlueInfo->prDevHandler->features =
							NETIF_F_IP_CSUM |
							NETIF_F_IPV6_CSUM |
							NETIF_F_RXCSUM;
			} else {
				DBGLOG(INIT, WARN,
				       "set HW checksum offload fail 0x%x\n",
				       rStatus);
				prAdapter->fgIsSupportCsumOffload = FALSE;
			}
		}
#endif
#if CFG_SUPPORT_802_11K
		{
			uint32_t rStatus = WLAN_STATUS_FAILURE;
			uint32_t u4SetInfoLen = 0;

			rStatus = kalIoctl(prGlueInfo,
					   wlanoidSync11kCapabilities, NULL, 0,
					   FALSE, FALSE, TRUE, &u4SetInfoLen);

			if (rStatus != WLAN_STATUS_SUCCESS)
				DBGLOG(INIT, WARN,
				       "RRM: Set 11k Capabilities fail 0x%x\n",
				       rStatus);
		}
#endif

		/* 4 <3> Register the card */
		i4DevIdx = wlanNetRegister(prWdev);
		if (i4DevIdx < 0) {
			i4Status = -ENXIO;
			DBGLOG(INIT, ERROR,
			       "wlanProbe: Cannot register the net_device context to the kernel\n");
			eFailReason = NET_REGISTER_FAIL;
			break;
		}
		/* 4 <4> Register early suspend callback */
#if CFG_ENABLE_EARLY_SUSPEND
		glRegisterEarlySuspend(&wlan_early_suspend_desc,
				       wlan_early_suspend, wlan_late_resume);
#endif

		/* 4 <5> Register Notifier callback */
		wlanRegisterNotifier();

		/* 4 <6> Initialize /proc filesystem */
#if WLAN_INCLUDE_PROC
		i4Status = procCreateFsEntry(prGlueInfo);
		if (i4Status < 0) {
			DBGLOG(INIT, ERROR, "wlanProbe: init procfs failed\n");
			eFailReason = PROC_INIT_FAIL;
			break;
		}
#endif /* WLAN_INCLUDE_PROC */

#if CFG_MET_PACKET_TRACE_SUPPORT
		kalMetInit(prGlueInfo);
#endif

#if CFG_ENABLE_BT_OVER_WIFI
		prGlueInfo->rBowInfo.fgIsNetRegistered = FALSE;
		prGlueInfo->rBowInfo.fgIsRegistered = FALSE;
		glRegisterAmpc(prGlueInfo);
#endif

#if (CFG_ENABLE_WIFI_DIRECT && CFG_MTK_ANDROID_WMT)
		register_set_p2p_mode_handler(set_p2p_mode_handler);
#elif (CFG_ENABLE_WIFI_DIRECT)
		if (prAdapter->rWifiVar.u4RegP2pIfAtProbe) {
			struct PARAM_CUSTOM_P2P_SET_STRUCT rSetP2P;

			rSetP2P.u4Enable = 1;

#ifdef CFG_DRIVER_INITIAL_RUNNING_MODE
			rSetP2P.u4Mode = CFG_DRIVER_INITIAL_RUNNING_MODE;
#else
			rSetP2P.u4Mode = RUNNING_P2P_MODE;
#endif /* CFG_DRIVER_RUNNING_MODE */
			if (set_p2p_mode_handler(prWdev->netdev, rSetP2P) == 0)
				DBGLOG(INIT, INFO,
					"%s: p2p device registered\n",
					__func__);
			else
				DBGLOG(INIT, ERROR,
					"%s: Failed to register p2p device\n",
					__func__);
		}
#endif
#if (CFG_MET_PACKET_TRACE_SUPPORT == 1)
		DBGLOG(INIT, TRACE, "init MET procfs...\n");
		i4Status = kalMetInitProcfs(prGlueInfo);
		if (i4Status < 0) {
			DBGLOG(INIT, ERROR,
			       "wlanProbe: init MET procfs failed\n");
			eFailReason = FAIL_MET_INIT_PROCFS;
			break;
		}
#endif
		kalMemZero(&prGlueInfo->rFtIeForTx,
			   sizeof(prGlueInfo->rFtIeForTx));

		/* Configure 5G band for registered wiphy */
		if (prAdapter->fgEnable5GBand)
			prWdev->wiphy->bands[KAL_BAND_5GHZ] = &mtk_band_5ghz;
		else
			prWdev->wiphy->bands[KAL_BAND_5GHZ] = NULL;

		for (i = 0 ; i < KAL_P2P_NUM; i++) {
			if (gprP2pRoleWdev[i] == NULL)
				continue;

			if (prAdapter->fgEnable5GBand)
				gprP2pRoleWdev[i]->wiphy->bands[KAL_BAND_5GHZ] =
				&mtk_band_5ghz;
			else
				gprP2pRoleWdev[i]->wiphy->bands[KAL_BAND_5GHZ] =
				NULL;
		}
	} while (FALSE);

	if (i4Status == 0) {
#if CFG_SUPPORT_AGPS_ASSIST
		kalIndicateAgpsNotify(prAdapter, AGPS_EVENT_WLAN_ON, NULL,
				      0);
#endif
#if CFG_SUPPORT_EASY_DEBUG
		/* move before reading file
		 * wlanLoadDefaultCustomerSetting(prAdapter);
		 */
		wlanFeatureToFw(prGlueInfo->prAdapter, NULL, false);
#endif
		wlanCfgSetSwCtrl(prGlueInfo->prAdapter);
		wlanCfgSetChip(prGlueInfo->prAdapter);
		wlanCfgSetCountryCode(prGlueInfo->prAdapter);
		kalPerMonInit(prGlueInfo);
#if CFG_MET_TAG_SUPPORT
		if (met_tag_init() != 0)
			DBGLOG(INIT, ERROR, "MET_TAG_INIT error!\n");
#endif

#if CFG_SUPPORT_CAL_RESULT_BACKUP_TO_HOST
		/* Calibration Backup Flow */
		if (!g_fgIsCalDataBackuped) {
			if (rlmTriggerCalBackup(prGlueInfo->prAdapter,
			    g_fgIsCalDataBackuped) == WLAN_STATUS_FAILURE) {
				DBGLOG(RFTEST, INFO,
				       "Error : Boot Time Wi-Fi Enable Fail........\n");
				i4Status = -1;
				goto WLAN_PROBE_RETURN;
			}

			g_fgIsCalDataBackuped = TRUE;
		} else {
			if (rlmTriggerCalBackup(prGlueInfo->prAdapter,
			    g_fgIsCalDataBackuped) == WLAN_STATUS_FAILURE) {
				DBGLOG(RFTEST, INFO,
				       "Error : Normal Wi-Fi Enable Fail........\n");
				i4Status = -1;
				goto WLAN_PROBE_RETURN;
			}
		}
#endif

#if CFG_SUPPORT_REPLAY_DETECTION
		ucRpyDetectOffload = prAdapter->rWifiVar.ucRpyDetectOffload;

		if (ucRpyDetectOffload == FEATURE_ENABLED) {
			DBGLOG(INIT, INFO,
				"Send CMD to enable Replay Detection offload feature\n");
				wlanSuspendRekeyOffload(prAdapter->prGlueInfo,
				GTK_REKEY_CMD_MODE_RPY_OFFLOAD_ON);
		} else {
			DBGLOG(INIT, INFO,
				"Send CMD to disable Replay Detection offload feature\n");
				wlanSuspendRekeyOffload(prAdapter->prGlueInfo,
				GTK_REKEY_CMD_MODE_RPY_OFFLOAD_OFF);
		}
#endif

		/* card is ready */
		prGlueInfo->u4ReadyFlag = 1;

		kalSetHalted(FALSE);
		wlanDbgGetGlobalLogLevel(ENUM_WIFI_LOG_MODULE_FW,
					 &u4LogLevel);
		if (u4LogLevel > ENUM_WIFI_LOG_LEVEL_DEFAULT)
			wlanDbgSetLogLevelImpl(prAdapter,
					       ENUM_WIFI_LOG_LEVEL_VERSION_V1,
					       ENUM_WIFI_LOG_MODULE_FW,
					       u4LogLevel);

#if (CFG_MTK_CONNSYS_DEDICATED_LOG_PATH == 1)
		/* sync log status with firmware */
		if (u4LogOnOffCache != -1) /* -1: connsysD does not set */
			consys_log_event_notification((int)FW_LOG_CMD_ON_OFF,
				u4LogOnOffCache);
#endif

#if CFG_SUPPORT_TPENHANCE_MODE
		wlanTpeInit(prGlueInfo);
#endif /* CFG_SUPPORT_TPENHANCE_MODE */

		DBGLOG(INIT, STATE, "wlanProbe: probe success\n");
#if CFG_CHIP_RESET_HANG
		if (fgIsResetHangState == SER_L0_HANG_RST_TRGING) {
			DBGLOG(INIT, STATE, "[SER][L0] SET SQC hang!\n");
			fgIsResetHangState = SER_L0_HANG_RST_HANG;
			fgIsResetting = TRUE;
		}
#endif
	} else {
		DBGLOG(INIT, ERROR, "wlanProbe: probe failed, reason:%d\n",
		       eFailReason);
		switch (eFailReason) {
		case FAIL_MET_INIT_PROCFS:
#if WLAN_INCLUDE_PROC
			procRemoveProcfs();
#endif
			kal_fallthrough;
		case PROC_INIT_FAIL:
			wlanUnregisterNotifier();
#if CFG_ENABLE_EARLY_SUSPEND
			glUnregisterEarlySuspend(&wlan_early_suspend_desc);
#endif
			wlanNetUnregister(prWdev);
			kal_fallthrough;
		case NET_REGISTER_FAIL:
		case NON_MAIN_TREAD_CREATE_FAIL:
			set_bit(GLUE_FLAG_HALT_BIT, &prGlueInfo->ulFlag);
			/* wake up main thread */
			wake_up_interruptible(&prGlueInfo->waitq);
			/* wait main thread stops */
			wait_for_completion_interruptible(
							&prGlueInfo->rHaltComp);
			kal_fallthrough;
		case MAIN_TREAD_CREATE_FAIL:
			wlanAdapterStop(prAdapter);
			kal_fallthrough;
		case ADAPTER_START_FAIL:
			glBusFreeIrq(prWdev->netdev,
				*((struct GLUE_INFO **)
						netdev_priv(prWdev->netdev)));
			kal_fallthrough;
		case BUS_SET_IRQ_FAIL:
			wlanWakeLockUninit(prGlueInfo);
			wlanNetDestroy(prWdev);
			/* prGlueInfo->prAdapter is released in
			 * wlanNetDestroy
			 */
			/* Set NULL value for local prAdapter as well */
			prAdapter = NULL;
			break;
		case NET_CREATE_FAIL:
			break;
		case BUS_INIT_FAIL:
			break;
		default:
			break;
		}
	}
#if CFG_THERMAL_API_SUPPORT
	if (i4Status == 0)
		g_fgIsWifiEnabled = TRUE;

	pbridge.thermal_query_cb = mtk_wcn_temp_query_ctrl;
	wcn_export_platform_bridge_register(&pbridge);
#endif

	/* i4Status is necessary before goto WLAN_PROBE_RETURN */
WLAN_PROBE_RETURN:
	glReseProbeRemoveDone(prGlueInfo, i4Status, TRUE);
	atomic_set(&g_wlanProbing, 0);

	return i4Status;
}				/* end of wlanProbe() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief A method to stop driver operation and release all resources. Following
 *        this call, no frame should go up or down through this interface.
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
static void wlanRemove(void)
{
	struct net_device *prDev = NULL;
	struct WLANDEV_INFO *prWlandevInfo = NULL;
	struct GLUE_INFO *prGlueInfo = NULL;
	struct ADAPTER *prAdapter = NULL;
	u_int8_t fgResult = FALSE;

	DBGLOG(INIT, STATE, "Remove wlan!\n");

	if (atomic_read(&g_wlanRemoving)) {
		DBGLOG(INIT, ERROR, "wlanRemove in process\n");
		return;
	}
	atomic_set(&g_wlanRemoving, 1);

	kalSetHalted(TRUE);

	/* 4 <0> Sanity check */
	ASSERT(u4WlanDevNum <= CFG_MAX_WLAN_DEVICES);
	if (u4WlanDevNum == 0) {
		DBGLOG(INIT, ERROR, "u4WlanDevNum = 0\n");
		goto WLAN_REMOVE_RETURN;
	}
#if (CFG_ENABLE_WIFI_DIRECT && CFG_MTK_ANDROID_WMT)
	register_set_p2p_mode_handler(NULL);
#endif
	if (u4WlanDevNum > 0
	    && u4WlanDevNum <= CFG_MAX_WLAN_DEVICES) {
		prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev;
		prWlandevInfo = &arWlanDevInfo[u4WlanDevNum - 1];
	}

	ASSERT(prDev);
	if (prDev == NULL) {
		DBGLOG(INIT, ERROR, "prDev is NULL\n");
		goto WLAN_REMOVE_RETURN;
	}

	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
	ASSERT(prGlueInfo);
	if (prGlueInfo == NULL) {
		DBGLOG(INIT, STATE, "prGlueInfo is NULL\n");
		free_netdev(prDev);
		goto WLAN_REMOVE_RETURN;
	}

	/* Hold mutex in order to asynchronously check whether the net */
	/* interface is available					*/
	KAL_ACQUIRE_MUTEX(prGlueInfo->prAdapter, MUTEX_DEL_INF);
	/* to avoid that wpa_supplicant/hostapd triogger new cfg80211 command */
	prGlueInfo->u4ReadyFlag = 0;
	KAL_RELEASE_MUTEX(prGlueInfo->prAdapter, MUTEX_DEL_INF);

	/* Have tried to do scan done here, but the exception occurs for */
	/* the P2P scan. Keep the original design that scan done in the	 */
	/* p2pStop/wlanStop.						 */

#if WLAN_INCLUDE_PROC
	procRemoveProcfs();
#endif /* WLAN_INCLUDE_PROC */

	prAdapter = prGlueInfo->prAdapter;
	kalPerMonDestroy(prGlueInfo);

#if CFG_SUPPORT_TPENHANCE_MODE
	wlanTpeUninit(prGlueInfo);
#endif /* CFG_SUPPORT_TPENHANCE_MODE */

#if CFG_SUPPORT_SER
#if defined(_HIF_USB)
	cnmTimerStopTimer(prAdapter, &prAdapter->rSerSyncTimer);
#endif  /* _HIF_USB */
#endif  /* CFG_SUPPORT_SER */

	/* complete possible pending oid, which may block wlanRemove some time
	 * and then whole chip reset may failed
	 */
	if (kalIsResetting())
		wlanReleasePendingOid(prGlueInfo->prAdapter, 1);

#if CFG_ENABLE_BT_OVER_WIFI
	if (prGlueInfo->rBowInfo.fgIsNetRegistered) {
		bowNotifyAllLinkDisconnected(prGlueInfo->prAdapter);
		/* wait 300ms for BoW module to send deauth */
		kalMsleep(300);
	}
#endif

	if (prGlueInfo->eParamMediaStateIndicated ==
	    PARAM_MEDIA_STATE_CONNECTED) {

#if CFG_WPS_DISCONNECT || (KERNEL_VERSION(4, 2, 0) <= CFG80211_VERSION_CODE)
		cfg80211_disconnected(prGlueInfo->prDevHandler, 0, NULL, 0,
				      TRUE, GFP_KERNEL);
#else
		cfg80211_disconnected(prGlueInfo->prDevHandler, 0, NULL, 0,
				      GFP_KERNEL);
#endif
		kalMsleep(500);
	}

	flush_delayed_work(&workq);

	/* 20150205 work queue for sched_scan */

	flush_delayed_work(&sched_workq);

#if CFG_SUPPORT_CFG80211_AUTH
#if CFG_SUPPORT_CFG80211_QUEUE
	flush_delayed_work(&cfg80211_workq);
#endif
#endif

#if IS_ENABLED(CFG_CCN7_SAP_EASYMESH)
	cancel_delayed_work_sync(&prAdapter->prGlueInfo->rChanNoiseControlWork);
	cancel_delayed_work_sync(&prAdapter->prGlueInfo->rChanNoiseGetInfoWork);
#endif

	down(&g_halt_sem);
	g_u4HaltFlag = 1;
	up(&g_halt_sem);

	/* 4 <2> Mark HALT, notify main thread to stop, and clean up queued
	 *       requests
	 */
	set_bit(GLUE_FLAG_HALT_BIT, &prGlueInfo->ulFlag);

	/* Stop works */
	flush_work(&prGlueInfo->rTxMsduFreeWork);
	cancel_delayed_work_sync(&prGlueInfo->rRxPktDeAggWork);

#if CFG_SUPPORT_MULTITHREAD
	wake_up_interruptible(&prGlueInfo->waitq_hif);
	wait_for_completion_interruptible(
		&prGlueInfo->rHifHaltComp);
	HAL_AGG_THREAD_WAKE_UP(prGlueInfo->prAdapter);
#if CFG_SDIO_RX_DE_AGG_IN_THREAD
	wake_up_interruptible(&prGlueInfo->waitq_rxDeAgg);
	wait_for_completion_interruptible(&prGlueInfo->rxDeAggHaltComp);
#endif
	wake_up_interruptible(&prGlueInfo->waitq_rx);
	wait_for_completion_interruptible(&prGlueInfo->rRxHaltComp);
#endif

	/* wake up main thread */
	wake_up_interruptible(&prGlueInfo->waitq);
	/* wait main thread stops */
	wait_for_completion_interruptible(&prGlueInfo->rHaltComp);

	DBGLOG(INIT, INFO, "wlan thread stopped\n");

	/* prGlueInfo->rHifInfo.main_thread = NULL; */
	prGlueInfo->main_thread = NULL;
#if CFG_SUPPORT_MULTITHREAD
	prGlueInfo->hif_thread = NULL;
	prGlueInfo->rx_thread = NULL;
#if CFG_SDIO_RX_DE_AGG_IN_THREAD
	prGlueInfo->rx_DeAgg_thread = NULL;
#endif

	prGlueInfo->u4TxThreadPid = 0xffffffff;
	prGlueInfo->u4HifThreadPid = 0xffffffff;
#endif

	if (HAL_IS_TX_DIRECT(prAdapter)) {
		if (prAdapter->fgTxDirectInited) {
			del_timer_sync(&prAdapter->rTxDirectSkbTimer);
			del_timer_sync(&prAdapter->rTxDirectHifTimer);
		}
	}

	/* Destroy wakelock */
	wlanWakeLockUninit(prGlueInfo);

	kalMemSet(&(prGlueInfo->prAdapter->rWlanInfo), 0,
		  sizeof(struct WLAN_INFO));

#if CFG_ENABLE_WIFI_DIRECT
	if (prGlueInfo->prAdapter->fgIsP2PRegistered) {
		DBGLOG(INIT, INFO, "p2pNetUnregister...\n");
		p2pNetUnregister(prGlueInfo, FALSE);
		DBGLOG(INIT, INFO, "p2pRemove...\n");
		/*p2pRemove must before wlanAdapterStop */
		p2pRemove(prGlueInfo);
	}
#endif

#if CFG_ENABLE_BT_OVER_WIFI
	if (prGlueInfo->rBowInfo.fgIsRegistered)
		glUnregisterAmpc(prGlueInfo);
#endif

#if (CFG_MET_PACKET_TRACE_SUPPORT == 1)
	kalMetRemoveProcfs();
#endif

#if CFG_MET_TAG_SUPPORT
	if (GL_MET_TAG_UNINIT() != 0)
		DBGLOG(INIT, ERROR, "MET_TAG_UNINIT error!\n");
#endif

	/* 4 <4> wlanAdapterStop */
#if CFG_SUPPORT_AGPS_ASSIST
	kalIndicateAgpsNotify(prAdapter, AGPS_EVENT_WLAN_OFF, NULL,
			      0);
#endif

	wlanAdapterStop(prAdapter);

	HAL_LP_OWN_SET(prAdapter, &fgResult);
	DBGLOG(INIT, INFO, "HAL_LP_OWN_SET(%d)\n",
	       (uint32_t) fgResult);

	/* 4 <x> Stopping handling interrupt and free IRQ */
	glBusFreeIrq(prDev, prGlueInfo);

	/* 4 <5> Release the Bus */
	glBusRelease(prDev);

#if (CFG_SUPPORT_TRACE_TC4 == 1)
	wlanDebugTC4Uninit();
#endif
	/* 4 <6> Unregister the card */
	wlanNetUnregister(gprWdev);

	/* 4 <7> Destroy the device */
	wlanNetDestroy(gprWdev);
	prDev = NULL;

	tasklet_kill(&prGlueInfo->rTxCompleteTask);
	tasklet_kill(&prGlueInfo->rRxTask);

	/* 4 <8> Unregister early suspend callback */
#if CFG_ENABLE_EARLY_SUSPEND
	glUnregisterEarlySuspend(&wlan_early_suspend_desc);
#endif

	gprWdev->netdev = NULL;

	/* 4 <9> Unregister notifier callback */
	wlanUnregisterNotifier();

#if CFG_CHIP_RESET_SUPPORT & !CFG_WMT_RESET_API_SUPPORT
	fgIsResetting = FALSE;
#endif

#if CFG_THERMAL_API_SUPPORT
	g_fgIsWifiEnabled = FALSE;
	wcn_export_platform_bridge_unregister();
#endif

WLAN_REMOVE_RETURN:
	DBGLOG(INIT, STATE, "end\n");
	glReseProbeRemoveDone(prGlueInfo, 0, FALSE);
	atomic_set(&g_wlanRemoving, 0);
}				/* end of wlanRemove() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief Driver entry point when the driver is configured as a Linux Module,
 *        and is called once at module load time, by the user-level modutils
 *        application: insmod or modprobe.
 *
 * \retval 0     Success
 */
/*----------------------------------------------------------------------------*/
/* 1 Module Entry Point */
static int initWlan(void)
{
	int ret = 0;
	struct GLUE_INFO *prGlueInfo = NULL;

	DBGLOG(INIT, INFO, "initWlan\n");
	atomic_set(&g_wlanProbing, 0);
	atomic_set(&g_wlanRemoving, 0);

#if (CFG_CHIP_RESET_SUPPORT)
#if CFG_CHIP_RESET_KO_SUPPORT
resetko_register_module(RESET_MODULE_TYPE_WIFI,
			"wifi",
#if (RESETKO_API_VERSION == 1)
			TRIGGER_RESET_TYPE_GPIO_API,
			resetkoReset,
#endif
			resetkoNotifyFunc);
#endif  /* CFG_CHIP_RESET_KO_SUPPORT */
#endif  /* CFG_CHIP_RESET_SUPPORT */

#ifdef CFG_DRIVER_INF_NAME_CHANGE

	if (kalStrLen(gprifnamesta) > CUSTOM_IFNAMESIZ ||
	    kalStrLen(gprifnamep2p) > CUSTOM_IFNAMESIZ ||
	    kalStrLen(gprifnameap) > CUSTOM_IFNAMESIZ) {
		DBGLOG(INIT, ERROR, "custom infname len illegal > %d\n",
		       CUSTOM_IFNAMESIZ);
		ret = -EINVAL;
		goto INIT_WLAN_RETURN;
	}

#endif /*  CFG_DRIVER_INF_NAME_CHANGE */

	wlanDebugInit();

	/* memory pre-allocation */
#if CFG_PRE_ALLOCATION_IO_BUFFER
	kalInitIOBuffer(TRUE);
#else
	kalInitIOBuffer(FALSE);
#endif


#if WLAN_INCLUDE_PROC
	procInitFs();
#endif

	wlanCreateWirelessDevice();
	if (gprWdev == NULL) {
		ret = -ENOMEM;
		goto INIT_WLAN_RETURN;
	}

	prGlueInfo = (struct GLUE_INFO *) wiphy_priv(
			     gprWdev->wiphy);
	if (gprWdev) {
		/* P2PDev and P2PRole[0] share the same Wdev */
		if (glP2pCreateWirelessDevice(prGlueInfo) == TRUE)
			gprP2pWdev = gprP2pRoleWdev[0];
	}
	gPrDev = NULL;


	/*  For DTV Ref project -> Default enable */

#if CFG_DC_USB_WOW_CALLBACK

	/* register system DC wow enable/disable callback function */
		do {
			typedef void (*func_ptr) (int (*f) (void));
			char *func_name = "WifiRegisterPdwncCallback";
			func_ptr pFunc =
				(func_ptr)kal_kallsyms_lookup_name(func_name);
			char *func_name2 = "RegisterPdwncCallbackWifi";
			func_ptr pFunc2 =
				(func_ptr)kal_kallsyms_lookup_name(func_name2);

			if (pFunc) {
				DBGLOG(INIT, ERROR,
				"Register Wow callback1 success\n");
					pFunc(&kalDcSetWow);
			} else if (pFunc2) {
				DBGLOG(INIT, ERROR,
				"Register Wow callback2 success\n");
				pFunc2(&kalDcSetWow);
			} else
				DBGLOG(INIT, ERROR,
				"No Exported Func Found:%s\n",
				func_name);
		} while (0);
#endif

#if (CFG_CHIP_RESET_SUPPORT)
	glResetInit(prGlueInfo, wlanRemove);
#endif

	ret = ((glRegisterBus(wlanProbe,
			      wlanRemove) == WLAN_STATUS_SUCCESS) ? 0 : -EIO);

	if (ret == -EIO) {
		kalUninitIOBuffer();
		goto INIT_WLAN_RETURN;
	}
	kalFbNotifierReg((struct GLUE_INFO *) wiphy_priv(
				 gprWdev->wiphy));

#if (CFG_MTK_CONNSYS_DEDICATED_LOG_PATH == 1)
	wifi_fwlog_event_func_register(consys_log_event_notification);
#endif

#if CFG_MTK_ANDROID_EMI
	/* Set WIFI EMI protection to consys permitted on system boot up */
	kalSetEmiMpuProtection(gConEmiPhyBase, WIFI_EMI_MEM_OFFSET,
			       WIFI_EMI_MEM_SIZE, true);
#endif

#if CFG_POWER_OFF_CTRL_SUPPORT
	wlanRegisterRebootNotifier();
#endif
#if IS_ENABLED(CFG_CCN7_SAP_EASYMESH)
	nl_sk = netlink_kernel_create(&init_net, NETLINK_OSS_KERNEL, NULL);
	if (!nl_sk) {
		DBGLOG(INIT, ERROR, "netlink create failed!\n");
		goto INIT_WLAN_RETURN;
	}
#endif /* CFG_CCN7_SAP_EASYMESH */

	/* ret is necessary before goto INIT_WLAN_RETURN */
INIT_WLAN_RETURN:
#if CFG_CHIP_RESET_SUPPORT
#if CFG_CHIP_RESET_KO_SUPPORT
	if (ret)
		resetko_unregister_module(RESET_MODULE_TYPE_WIFI);
#endif  /* CFG_CHIP_RESET_KO_SUPPORT */
#endif  /* CFG_CHIP_RESET_SUPPORT */

	return ret;
}				/* end of initWlan() */

/*----------------------------------------------------------------------------*/
/*!
 * \brief Driver exit point when the driver as a Linux Module is removed. Called
 *        at module unload time, by the user level modutils application: rmmod.
 *        This is our last chance to clean up after ourselves.
 *
 * \return (none)
 */
/*----------------------------------------------------------------------------*/
/* 1 Module Leave Point */
static void exitWlan(void)
{
#if IS_ENABLED(CFG_CCN7_SAP_EASYMESH)
	if (nl_sk != NULL)
		netlink_kernel_release(nl_sk);
#endif /* CFG_CCN7_SAP_EASYMESH */

	kalFbNotifierUnReg();
#if CFG_CHIP_RESET_SUPPORT
	glResetUninit();
#endif

	glUnregisterBus(wlanRemove);

	/*  For DTV Ref project -> Default enable */

#if CFG_DC_USB_WOW_CALLBACK

	/*Unregister system DC wow enable/disable callback function */
	do {
		typedef void (*func_ptr) (int (*f) (void));
		char *func_name = "WifiRegisterPdwncCallback";
		func_ptr pFunc =
			(func_ptr)kal_kallsyms_lookup_name(func_name);
		char *func_name2 = "RegisterPdwncCallbackWifi";
		func_ptr pFunc2 =
			(func_ptr)kal_kallsyms_lookup_name(func_name2);

		if (pFunc) {
			DBGLOG(INIT, STATE,
			"Unregister Wow callback1 success\n");
			pFunc(NULL);
		} else if (pFunc2) {
			DBGLOG(INIT, STATE,
			"Unregister Wow callback2 success\n");
			pFunc2(NULL);
		} else
			DBGLOG(INIT, ERROR,
			"No Exported Func Found:%s\n",
			func_name);
		} while (0);
#endif

	/* free pre-allocated memory */
	kalUninitIOBuffer();

	wlanDestroyAllWdev();

#if WLAN_INCLUDE_PROC
	procUninitProcFs();
#endif
#if CFG_POWER_OFF_CTRL_SUPPORT
	wlanUnregisterRebootNotifier();
#endif
#if CFG_CHIP_RESET_SUPPORT
#if CFG_CHIP_RESET_KO_SUPPORT
	resetko_unregister_module(RESET_MODULE_TYPE_WIFI);
#endif  /* CFG_CHIP_RESET_KO_SUPPORT */
#endif  /* CFG_CHIP_RESET_SUPPORT */

	DBGLOG(INIT, INFO, "exitWlan\n");

}				/* end of exitWlan() */

#if CFG_POWER_OFF_CTRL_SUPPORT
static int wf_pdwnc_notify(struct notifier_block *nb,
		unsigned long event, void *unused)
{
	if (event == SYS_RESTART) {
		DBGLOG(HAL, STATE, "wf_pdwnc_notify()\n");
		kalFbNotifierUnReg();
#if CFG_CHIP_RESET_SUPPORT
		glResetUninit();
#endif
		glUnregisterBus(wlanRemove);
		/* free pre-allocated memory */
		kalUninitIOBuffer();
		wlanDestroyAllWdev();

#if WLAN_INCLUDE_PROC
		procUninitProcFs();
#endif
		DBGLOG(HAL, STATE, "wf_pdwnc_notify() done\n");
	}

	if (event == SYS_POWER_OFF || event == SYS_HALT) {
		DBGLOG(HAL, STATE, "DC Set WoW\n");
		kalDcSetWow();
	}
	return 0;
}

static struct notifier_block wf_pdwnc_notifier = {
	.notifier_call = wf_pdwnc_notify,
	.next = NULL,
	.priority = 0,
};

void wlanRegisterRebootNotifier(void)
{
	DBGLOG(HAL, STATE, "wlanRegisterRebootNotifier()\n");
	register_reboot_notifier(&wf_pdwnc_notifier);
	DBGLOG(HAL, STATE, "wlanRegisterRebootNotifier() done\n");
}

void wlanUnregisterRebootNotifier(void)
{
	DBGLOG(HAL, STATE, "wlanUnregisterRebootNotifier()\n");
	unregister_reboot_notifier(&wf_pdwnc_notifier);
	DBGLOG(HAL, STATE, "wlanUnregisterRebootNotifier() done\n");
}

#endif

#if CFG_THERMAL_API_SUPPORT


/*----------------------------------------------------------------------------*/
/*!
* \brief export API for thermal
*
* \retval >0 Success
* \retval -1000 invalid
*/
/*----------------------------------------------------------------------------*/
static int mtk_wcn_temp_query_ctrl(void)
{
	struct net_device *prDev = NULL;
	struct GLUE_INFO *prGlueInfo = NULL;
	struct ADAPTER *prAdapter = NULL;
	int i4Temperature = 0;
#ifdef CFG_GET_TEMPURATURE
	uint32_t u4BufLen;
	uint32_t rStatus = WLAN_STATUS_SUCCESS;
#endif
	/* 1. check if wifi is enabled */
	if (!g_fgIsWifiEnabled)
		return -1000;

	ASSERT(u4WlanDevNum <= CFG_MAX_WLAN_DEVICES);
	if (u4WlanDevNum == 0) {
		DBGLOG(INIT, INFO, "0 == u4WlanDevNum\n");
		return -1000;
	}

	if (u4WlanDevNum > 0 && u4WlanDevNum <= CFG_MAX_WLAN_DEVICES)
		prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev;

	ASSERT(prDev);
	if (prDev == NULL) {
		DBGLOG(INIT, INFO, "NULL == prDev\n");
		return -1000;
	}
	prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev));
	ASSERT(prGlueInfo);
	if (prGlueInfo == NULL) {
		DBGLOG(INIT, INFO, "NULL == prGlueInfo\n");
		return -1000;
	}
	prAdapter = prGlueInfo->prAdapter;
	ASSERT(prAdapter);
#ifdef CFG_GET_TEMPURATURE
	rStatus = kalIoctl(prGlueInfo,
					wlanoidGetTemperature,
					&i4Temperature,
					sizeof(i4Temperature),
					TRUE, TRUE, TRUE, &u4BufLen);

	if (rStatus != WLAN_STATUS_SUCCESS) {
		DBGLOG(INIT, ERROR, "Query ucTemperature failed!\n");
		return -1000;
	}
#else
	return -1000;
#endif
	return i4Temperature;

}
#endif

#if ((MTK_WCN_HIF_SDIO == 1) && (CFG_BUILT_IN_DRIVER == 1)) || \
	((MTK_WCN_HIF_AXI == 1) && (CFG_BUILT_IN_DRIVER == 1))

int mtk_wcn_wlan_gen4_init(void)
{
	return initWlan();
}
EXPORT_SYMBOL(mtk_wcn_wlan_gen4_init);

void mtk_wcn_wlan_gen4_exit(void)
{
	return exitWlan();
}
EXPORT_SYMBOL(mtk_wcn_wlan_gen4_exit);

#elif ((MTK_WCN_HIF_SDIO == 0) && (CFG_BUILT_IN_DRIVER == 1))

device_initcall(initWlan);

#else

module_init(initWlan);
module_exit(exitWlan);

#endif
