162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NXP Wireless LAN device driver: major functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011-2020 NXP
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/suspend.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "main.h"
1162306a36Sopenharmony_ci#include "wmm.h"
1262306a36Sopenharmony_ci#include "cfg80211.h"
1362306a36Sopenharmony_ci#include "11n.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define VERSION	"1.0"
1662306a36Sopenharmony_ci#define MFG_FIRMWARE	"mwifiex_mfg.bin"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK;
1962306a36Sopenharmony_cimodule_param(debug_mask, uint, 0);
2062306a36Sopenharmony_ciMODULE_PARM_DESC(debug_mask, "bitmap for debug flags");
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciconst char driver_version[] = "mwifiex " VERSION " (%s) ";
2362306a36Sopenharmony_cistatic char *cal_data_cfg;
2462306a36Sopenharmony_cimodule_param(cal_data_cfg, charp, 0);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic unsigned short driver_mode;
2762306a36Sopenharmony_cimodule_param(driver_mode, ushort, 0);
2862306a36Sopenharmony_ciMODULE_PARM_DESC(driver_mode,
2962306a36Sopenharmony_ci		 "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cibool mfg_mode;
3262306a36Sopenharmony_cimodule_param(mfg_mode, bool, 0);
3362306a36Sopenharmony_ciMODULE_PARM_DESC(mfg_mode, "manufacturing mode enable:1, disable:0");
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cibool aggr_ctrl;
3662306a36Sopenharmony_cimodule_param(aggr_ctrl, bool, 0000);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(aggr_ctrl, "usb tx aggregation enable:1, disable:0");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciconst u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * This function registers the device and performs all the necessary
4362306a36Sopenharmony_ci * initializations.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * The following initialization operations are performed -
4662306a36Sopenharmony_ci *      - Allocate adapter structure
4762306a36Sopenharmony_ci *      - Save interface specific operations table in adapter
4862306a36Sopenharmony_ci *      - Call interface specific initialization routine
4962306a36Sopenharmony_ci *      - Allocate private structures
5062306a36Sopenharmony_ci *      - Set default adapter structure parameters
5162306a36Sopenharmony_ci *      - Initialize locks
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * In case of any errors during inittialization, this function also ensures
5462306a36Sopenharmony_ci * proper cleanup before exiting.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistatic int mwifiex_register(void *card, struct device *dev,
5762306a36Sopenharmony_ci			    struct mwifiex_if_ops *if_ops, void **padapter)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct mwifiex_adapter *adapter;
6062306a36Sopenharmony_ci	int i;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL);
6362306a36Sopenharmony_ci	if (!adapter)
6462306a36Sopenharmony_ci		return -ENOMEM;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	*padapter = adapter;
6762306a36Sopenharmony_ci	adapter->dev = dev;
6862306a36Sopenharmony_ci	adapter->card = card;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Save interface specific operations in adapter */
7162306a36Sopenharmony_ci	memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops));
7262306a36Sopenharmony_ci	adapter->debug_mask = debug_mask;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* card specific initialization has been deferred until now .. */
7562306a36Sopenharmony_ci	if (adapter->if_ops.init_if)
7662306a36Sopenharmony_ci		if (adapter->if_ops.init_if(adapter))
7762306a36Sopenharmony_ci			goto error;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	adapter->priv_num = 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
8262306a36Sopenharmony_ci		/* Allocate memory for private structure */
8362306a36Sopenharmony_ci		adapter->priv[i] =
8462306a36Sopenharmony_ci			kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL);
8562306a36Sopenharmony_ci		if (!adapter->priv[i])
8662306a36Sopenharmony_ci			goto error;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		adapter->priv[i]->adapter = adapter;
8962306a36Sopenharmony_ci		adapter->priv_num++;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	mwifiex_init_lock_list(adapter);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	timer_setup(&adapter->cmd_timer, mwifiex_cmd_timeout_func, 0);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cierror:
9862306a36Sopenharmony_ci	mwifiex_dbg(adapter, ERROR,
9962306a36Sopenharmony_ci		    "info: leave mwifiex_register with error\n");
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++)
10262306a36Sopenharmony_ci		kfree(adapter->priv[i]);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	kfree(adapter);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return -1;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * This function unregisters the device and performs all the necessary
11162306a36Sopenharmony_ci * cleanups.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * The following cleanup operations are performed -
11462306a36Sopenharmony_ci *      - Free the timers
11562306a36Sopenharmony_ci *      - Free beacon buffers
11662306a36Sopenharmony_ci *      - Free private structures
11762306a36Sopenharmony_ci *      - Free adapter structure
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_cistatic int mwifiex_unregister(struct mwifiex_adapter *adapter)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	s32 i;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (adapter->if_ops.cleanup_if)
12462306a36Sopenharmony_ci		adapter->if_ops.cleanup_if(adapter);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	timer_shutdown_sync(&adapter->cmd_timer);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Free private structures */
12962306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
13062306a36Sopenharmony_ci		if (adapter->priv[i]) {
13162306a36Sopenharmony_ci			mwifiex_free_curr_bcn(adapter->priv[i]);
13262306a36Sopenharmony_ci			kfree(adapter->priv[i]);
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (adapter->nd_info) {
13762306a36Sopenharmony_ci		for (i = 0 ; i < adapter->nd_info->n_matches ; i++)
13862306a36Sopenharmony_ci			kfree(adapter->nd_info->matches[i]);
13962306a36Sopenharmony_ci		kfree(adapter->nd_info);
14062306a36Sopenharmony_ci		adapter->nd_info = NULL;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	kfree(adapter->regd);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	kfree(adapter);
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	unsigned long flags;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->main_proc_lock, flags);
15462306a36Sopenharmony_ci	if (adapter->mwifiex_processing) {
15562306a36Sopenharmony_ci		adapter->more_task_flag = true;
15662306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
15762306a36Sopenharmony_ci	} else {
15862306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
15962306a36Sopenharmony_ci		queue_work(adapter->workqueue, &adapter->main_work);
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	spin_lock_bh(&adapter->rx_proc_lock);
16762306a36Sopenharmony_ci	if (adapter->rx_processing) {
16862306a36Sopenharmony_ci		spin_unlock_bh(&adapter->rx_proc_lock);
16962306a36Sopenharmony_ci	} else {
17062306a36Sopenharmony_ci		spin_unlock_bh(&adapter->rx_proc_lock);
17162306a36Sopenharmony_ci		queue_work(adapter->rx_workqueue, &adapter->rx_work);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int mwifiex_process_rx(struct mwifiex_adapter *adapter)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct sk_buff *skb;
17862306a36Sopenharmony_ci	struct mwifiex_rxinfo *rx_info;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	spin_lock_bh(&adapter->rx_proc_lock);
18162306a36Sopenharmony_ci	if (adapter->rx_processing || adapter->rx_locked) {
18262306a36Sopenharmony_ci		spin_unlock_bh(&adapter->rx_proc_lock);
18362306a36Sopenharmony_ci		goto exit_rx_proc;
18462306a36Sopenharmony_ci	} else {
18562306a36Sopenharmony_ci		adapter->rx_processing = true;
18662306a36Sopenharmony_ci		spin_unlock_bh(&adapter->rx_proc_lock);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Check for Rx data */
19062306a36Sopenharmony_ci	while ((skb = skb_dequeue(&adapter->rx_data_q))) {
19162306a36Sopenharmony_ci		atomic_dec(&adapter->rx_pending);
19262306a36Sopenharmony_ci		if ((adapter->delay_main_work ||
19362306a36Sopenharmony_ci		     adapter->iface_type == MWIFIEX_USB) &&
19462306a36Sopenharmony_ci		    (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
19562306a36Sopenharmony_ci			if (adapter->if_ops.submit_rem_rx_urbs)
19662306a36Sopenharmony_ci				adapter->if_ops.submit_rem_rx_urbs(adapter);
19762306a36Sopenharmony_ci			adapter->delay_main_work = false;
19862306a36Sopenharmony_ci			mwifiex_queue_main_work(adapter);
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci		rx_info = MWIFIEX_SKB_RXCB(skb);
20162306a36Sopenharmony_ci		if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
20262306a36Sopenharmony_ci			if (adapter->if_ops.deaggr_pkt)
20362306a36Sopenharmony_ci				adapter->if_ops.deaggr_pkt(adapter, skb);
20462306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
20562306a36Sopenharmony_ci		} else {
20662306a36Sopenharmony_ci			mwifiex_handle_rx_packet(adapter, skb);
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	spin_lock_bh(&adapter->rx_proc_lock);
21062306a36Sopenharmony_ci	adapter->rx_processing = false;
21162306a36Sopenharmony_ci	spin_unlock_bh(&adapter->rx_proc_lock);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciexit_rx_proc:
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void maybe_quirk_fw_disable_ds(struct mwifiex_adapter *adapter)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
22062306a36Sopenharmony_ci	struct mwifiex_ver_ext ver_ext;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (test_and_set_bit(MWIFIEX_IS_REQUESTING_FW_VEREXT, &adapter->work_flags))
22362306a36Sopenharmony_ci		return;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	memset(&ver_ext, 0, sizeof(ver_ext));
22662306a36Sopenharmony_ci	ver_ext.version_str_sel = 1;
22762306a36Sopenharmony_ci	if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT,
22862306a36Sopenharmony_ci			     HostCmd_ACT_GEN_GET, 0, &ver_ext, false)) {
22962306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, MSG,
23062306a36Sopenharmony_ci			    "Checking hardware revision failed.\n");
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * The main process.
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * This function is the main procedure of the driver and handles various driver
23862306a36Sopenharmony_ci * operations. It runs in a loop and provides the core functionalities.
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci * The main responsibilities of this function are -
24162306a36Sopenharmony_ci *      - Ensure concurrency control
24262306a36Sopenharmony_ci *      - Handle pending interrupts and call interrupt handlers
24362306a36Sopenharmony_ci *      - Wake up the card if required
24462306a36Sopenharmony_ci *      - Handle command responses and call response handlers
24562306a36Sopenharmony_ci *      - Handle events and call event handlers
24662306a36Sopenharmony_ci *      - Execute pending commands
24762306a36Sopenharmony_ci *      - Transmit pending data packets
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_ciint mwifiex_main_process(struct mwifiex_adapter *adapter)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int ret = 0;
25262306a36Sopenharmony_ci	unsigned long flags;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->main_proc_lock, flags);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Check if already processing */
25762306a36Sopenharmony_ci	if (adapter->mwifiex_processing || adapter->main_locked) {
25862306a36Sopenharmony_ci		adapter->more_task_flag = true;
25962306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
26062306a36Sopenharmony_ci		return 0;
26162306a36Sopenharmony_ci	} else {
26262306a36Sopenharmony_ci		adapter->mwifiex_processing = true;
26362306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ciprocess_start:
26662306a36Sopenharmony_ci	do {
26762306a36Sopenharmony_ci		if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)
26862306a36Sopenharmony_ci			break;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		/* For non-USB interfaces, If we process interrupts first, it
27162306a36Sopenharmony_ci		 * would increase RX pending even further. Avoid this by
27262306a36Sopenharmony_ci		 * checking if rx_pending has crossed high threshold and
27362306a36Sopenharmony_ci		 * schedule rx work queue and then process interrupts.
27462306a36Sopenharmony_ci		 * For USB interface, there are no interrupts. We already have
27562306a36Sopenharmony_ci		 * HIGH_RX_PENDING check in usb.c
27662306a36Sopenharmony_ci		 */
27762306a36Sopenharmony_ci		if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
27862306a36Sopenharmony_ci		    adapter->iface_type != MWIFIEX_USB) {
27962306a36Sopenharmony_ci			adapter->delay_main_work = true;
28062306a36Sopenharmony_ci			mwifiex_queue_rx_work(adapter);
28162306a36Sopenharmony_ci			break;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		/* Handle pending interrupt if any */
28562306a36Sopenharmony_ci		if (adapter->int_status) {
28662306a36Sopenharmony_ci			if (adapter->hs_activated)
28762306a36Sopenharmony_ci				mwifiex_process_hs_config(adapter);
28862306a36Sopenharmony_ci			if (adapter->if_ops.process_int_status)
28962306a36Sopenharmony_ci				adapter->if_ops.process_int_status(adapter);
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		if (adapter->rx_work_enabled && adapter->data_received)
29362306a36Sopenharmony_ci			mwifiex_queue_rx_work(adapter);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		/* Need to wake up the card ? */
29662306a36Sopenharmony_ci		if ((adapter->ps_state == PS_STATE_SLEEP) &&
29762306a36Sopenharmony_ci		    (adapter->pm_wakeup_card_req &&
29862306a36Sopenharmony_ci		     !adapter->pm_wakeup_fw_try) &&
29962306a36Sopenharmony_ci		    (is_command_pending(adapter) ||
30062306a36Sopenharmony_ci		     !skb_queue_empty(&adapter->tx_data_q) ||
30162306a36Sopenharmony_ci		     !mwifiex_bypass_txlist_empty(adapter) ||
30262306a36Sopenharmony_ci		     !mwifiex_wmm_lists_empty(adapter))) {
30362306a36Sopenharmony_ci			adapter->pm_wakeup_fw_try = true;
30462306a36Sopenharmony_ci			mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
30562306a36Sopenharmony_ci			adapter->if_ops.wakeup(adapter);
30662306a36Sopenharmony_ci			continue;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		if (IS_CARD_RX_RCVD(adapter)) {
31062306a36Sopenharmony_ci			adapter->data_received = false;
31162306a36Sopenharmony_ci			adapter->pm_wakeup_fw_try = false;
31262306a36Sopenharmony_ci			del_timer(&adapter->wakeup_timer);
31362306a36Sopenharmony_ci			if (adapter->ps_state == PS_STATE_SLEEP)
31462306a36Sopenharmony_ci				adapter->ps_state = PS_STATE_AWAKE;
31562306a36Sopenharmony_ci		} else {
31662306a36Sopenharmony_ci			/* We have tried to wakeup the card already */
31762306a36Sopenharmony_ci			if (adapter->pm_wakeup_fw_try)
31862306a36Sopenharmony_ci				break;
31962306a36Sopenharmony_ci			if (adapter->ps_state == PS_STATE_PRE_SLEEP)
32062306a36Sopenharmony_ci				mwifiex_check_ps_cond(adapter);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci			if (adapter->ps_state != PS_STATE_AWAKE)
32362306a36Sopenharmony_ci				break;
32462306a36Sopenharmony_ci			if (adapter->tx_lock_flag) {
32562306a36Sopenharmony_ci				if (adapter->iface_type == MWIFIEX_USB) {
32662306a36Sopenharmony_ci					if (!adapter->usb_mc_setup)
32762306a36Sopenharmony_ci						break;
32862306a36Sopenharmony_ci				} else
32962306a36Sopenharmony_ci					break;
33062306a36Sopenharmony_ci			}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			if ((!adapter->scan_chan_gap_enabled &&
33362306a36Sopenharmony_ci			     adapter->scan_processing) || adapter->data_sent ||
33462306a36Sopenharmony_ci			     mwifiex_is_tdls_chan_switching
33562306a36Sopenharmony_ci			     (mwifiex_get_priv(adapter,
33662306a36Sopenharmony_ci					       MWIFIEX_BSS_ROLE_STA)) ||
33762306a36Sopenharmony_ci			    (mwifiex_wmm_lists_empty(adapter) &&
33862306a36Sopenharmony_ci			     mwifiex_bypass_txlist_empty(adapter) &&
33962306a36Sopenharmony_ci			     skb_queue_empty(&adapter->tx_data_q))) {
34062306a36Sopenharmony_ci				if (adapter->cmd_sent || adapter->curr_cmd ||
34162306a36Sopenharmony_ci					!mwifiex_is_send_cmd_allowed
34262306a36Sopenharmony_ci						(mwifiex_get_priv(adapter,
34362306a36Sopenharmony_ci						MWIFIEX_BSS_ROLE_STA)) ||
34462306a36Sopenharmony_ci				    (!is_command_pending(adapter)))
34562306a36Sopenharmony_ci					break;
34662306a36Sopenharmony_ci			}
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		/* Check for event */
35062306a36Sopenharmony_ci		if (adapter->event_received) {
35162306a36Sopenharmony_ci			adapter->event_received = false;
35262306a36Sopenharmony_ci			mwifiex_process_event(adapter);
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		/* Check for Cmd Resp */
35662306a36Sopenharmony_ci		if (adapter->cmd_resp_received) {
35762306a36Sopenharmony_ci			adapter->cmd_resp_received = false;
35862306a36Sopenharmony_ci			mwifiex_process_cmdresp(adapter);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci			/* call mwifiex back when init_fw is done */
36162306a36Sopenharmony_ci			if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) {
36262306a36Sopenharmony_ci				adapter->hw_status = MWIFIEX_HW_STATUS_READY;
36362306a36Sopenharmony_ci				mwifiex_init_fw_complete(adapter);
36462306a36Sopenharmony_ci				maybe_quirk_fw_disable_ds(adapter);
36562306a36Sopenharmony_ci			}
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		/* Check if we need to confirm Sleep Request
36962306a36Sopenharmony_ci		   received previously */
37062306a36Sopenharmony_ci		if (adapter->ps_state == PS_STATE_PRE_SLEEP)
37162306a36Sopenharmony_ci			mwifiex_check_ps_cond(adapter);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/* * The ps_state may have been changed during processing of
37462306a36Sopenharmony_ci		 * Sleep Request event.
37562306a36Sopenharmony_ci		 */
37662306a36Sopenharmony_ci		if ((adapter->ps_state == PS_STATE_SLEEP) ||
37762306a36Sopenharmony_ci		    (adapter->ps_state == PS_STATE_PRE_SLEEP) ||
37862306a36Sopenharmony_ci		    (adapter->ps_state == PS_STATE_SLEEP_CFM)) {
37962306a36Sopenharmony_ci			continue;
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (adapter->tx_lock_flag) {
38362306a36Sopenharmony_ci			if (adapter->iface_type == MWIFIEX_USB) {
38462306a36Sopenharmony_ci				if (!adapter->usb_mc_setup)
38562306a36Sopenharmony_ci					continue;
38662306a36Sopenharmony_ci			} else
38762306a36Sopenharmony_ci				continue;
38862306a36Sopenharmony_ci		}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		if (!adapter->cmd_sent && !adapter->curr_cmd &&
39162306a36Sopenharmony_ci		    mwifiex_is_send_cmd_allowed
39262306a36Sopenharmony_ci		    (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
39362306a36Sopenharmony_ci			if (mwifiex_exec_next_cmd(adapter) == -1) {
39462306a36Sopenharmony_ci				ret = -1;
39562306a36Sopenharmony_ci				break;
39662306a36Sopenharmony_ci			}
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/** If USB Multi channel setup ongoing,
40062306a36Sopenharmony_ci		 *  wait for ready to tx data.
40162306a36Sopenharmony_ci		 */
40262306a36Sopenharmony_ci		if (adapter->iface_type == MWIFIEX_USB &&
40362306a36Sopenharmony_ci		    adapter->usb_mc_setup)
40462306a36Sopenharmony_ci			continue;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		if ((adapter->scan_chan_gap_enabled ||
40762306a36Sopenharmony_ci		     !adapter->scan_processing) &&
40862306a36Sopenharmony_ci		    !adapter->data_sent &&
40962306a36Sopenharmony_ci		    !skb_queue_empty(&adapter->tx_data_q)) {
41062306a36Sopenharmony_ci			if (adapter->hs_activated_manually) {
41162306a36Sopenharmony_ci				mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY),
41262306a36Sopenharmony_ci						  MWIFIEX_ASYNC_CMD);
41362306a36Sopenharmony_ci				adapter->hs_activated_manually = false;
41462306a36Sopenharmony_ci			}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci			mwifiex_process_tx_queue(adapter);
41762306a36Sopenharmony_ci			if (adapter->hs_activated) {
41862306a36Sopenharmony_ci				clear_bit(MWIFIEX_IS_HS_CONFIGURED,
41962306a36Sopenharmony_ci					  &adapter->work_flags);
42062306a36Sopenharmony_ci				mwifiex_hs_activated_event
42162306a36Sopenharmony_ci					(mwifiex_get_priv
42262306a36Sopenharmony_ci					(adapter, MWIFIEX_BSS_ROLE_ANY),
42362306a36Sopenharmony_ci					false);
42462306a36Sopenharmony_ci			}
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		if ((adapter->scan_chan_gap_enabled ||
42862306a36Sopenharmony_ci		     !adapter->scan_processing) &&
42962306a36Sopenharmony_ci		    !adapter->data_sent &&
43062306a36Sopenharmony_ci		    !mwifiex_bypass_txlist_empty(adapter) &&
43162306a36Sopenharmony_ci		    !mwifiex_is_tdls_chan_switching
43262306a36Sopenharmony_ci			(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
43362306a36Sopenharmony_ci			if (adapter->hs_activated_manually) {
43462306a36Sopenharmony_ci				mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY),
43562306a36Sopenharmony_ci						  MWIFIEX_ASYNC_CMD);
43662306a36Sopenharmony_ci				adapter->hs_activated_manually = false;
43762306a36Sopenharmony_ci			}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci			mwifiex_process_bypass_tx(adapter);
44062306a36Sopenharmony_ci			if (adapter->hs_activated) {
44162306a36Sopenharmony_ci				clear_bit(MWIFIEX_IS_HS_CONFIGURED,
44262306a36Sopenharmony_ci					  &adapter->work_flags);
44362306a36Sopenharmony_ci				mwifiex_hs_activated_event
44462306a36Sopenharmony_ci					(mwifiex_get_priv
44562306a36Sopenharmony_ci					 (adapter, MWIFIEX_BSS_ROLE_ANY),
44662306a36Sopenharmony_ci					 false);
44762306a36Sopenharmony_ci			}
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if ((adapter->scan_chan_gap_enabled ||
45162306a36Sopenharmony_ci		     !adapter->scan_processing) &&
45262306a36Sopenharmony_ci		    !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) &&
45362306a36Sopenharmony_ci		    !mwifiex_is_tdls_chan_switching
45462306a36Sopenharmony_ci			(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
45562306a36Sopenharmony_ci			if (adapter->hs_activated_manually) {
45662306a36Sopenharmony_ci				mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY),
45762306a36Sopenharmony_ci						  MWIFIEX_ASYNC_CMD);
45862306a36Sopenharmony_ci				adapter->hs_activated_manually = false;
45962306a36Sopenharmony_ci			}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci			mwifiex_wmm_process_tx(adapter);
46262306a36Sopenharmony_ci			if (adapter->hs_activated) {
46362306a36Sopenharmony_ci				clear_bit(MWIFIEX_IS_HS_CONFIGURED,
46462306a36Sopenharmony_ci					  &adapter->work_flags);
46562306a36Sopenharmony_ci				mwifiex_hs_activated_event
46662306a36Sopenharmony_ci					(mwifiex_get_priv
46762306a36Sopenharmony_ci					 (adapter, MWIFIEX_BSS_ROLE_ANY),
46862306a36Sopenharmony_ci					 false);
46962306a36Sopenharmony_ci			}
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		if (adapter->delay_null_pkt && !adapter->cmd_sent &&
47362306a36Sopenharmony_ci		    !adapter->curr_cmd && !is_command_pending(adapter) &&
47462306a36Sopenharmony_ci		    (mwifiex_wmm_lists_empty(adapter) &&
47562306a36Sopenharmony_ci		     mwifiex_bypass_txlist_empty(adapter) &&
47662306a36Sopenharmony_ci		     skb_queue_empty(&adapter->tx_data_q))) {
47762306a36Sopenharmony_ci			if (!mwifiex_send_null_packet
47862306a36Sopenharmony_ci			    (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
47962306a36Sopenharmony_ci			     MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
48062306a36Sopenharmony_ci			     MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) {
48162306a36Sopenharmony_ci				adapter->delay_null_pkt = false;
48262306a36Sopenharmony_ci				adapter->ps_state = PS_STATE_SLEEP;
48362306a36Sopenharmony_ci			}
48462306a36Sopenharmony_ci			break;
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci	} while (true);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	spin_lock_irqsave(&adapter->main_proc_lock, flags);
48962306a36Sopenharmony_ci	if (adapter->more_task_flag) {
49062306a36Sopenharmony_ci		adapter->more_task_flag = false;
49162306a36Sopenharmony_ci		spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
49262306a36Sopenharmony_ci		goto process_start;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci	adapter->mwifiex_processing = false;
49562306a36Sopenharmony_ci	spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return ret;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_main_process);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci/*
50262306a36Sopenharmony_ci * This function frees the adapter structure.
50362306a36Sopenharmony_ci *
50462306a36Sopenharmony_ci * Additionally, this closes the netlink socket, frees the timers
50562306a36Sopenharmony_ci * and private structures.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic void mwifiex_free_adapter(struct mwifiex_adapter *adapter)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	if (!adapter) {
51062306a36Sopenharmony_ci		pr_err("%s: adapter is NULL\n", __func__);
51162306a36Sopenharmony_ci		return;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	mwifiex_unregister(adapter);
51562306a36Sopenharmony_ci	pr_debug("info: %s: free adapter\n", __func__);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci/*
51962306a36Sopenharmony_ci * This function cancels all works in the queue and destroys
52062306a36Sopenharmony_ci * the main workqueue.
52162306a36Sopenharmony_ci */
52262306a36Sopenharmony_cistatic void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	if (adapter->workqueue) {
52562306a36Sopenharmony_ci		destroy_workqueue(adapter->workqueue);
52662306a36Sopenharmony_ci		adapter->workqueue = NULL;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (adapter->rx_workqueue) {
53062306a36Sopenharmony_ci		destroy_workqueue(adapter->rx_workqueue);
53162306a36Sopenharmony_ci		adapter->rx_workqueue = NULL;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/*
53662306a36Sopenharmony_ci * This function gets firmware and initializes it.
53762306a36Sopenharmony_ci *
53862306a36Sopenharmony_ci * The main initialization steps followed are -
53962306a36Sopenharmony_ci *      - Download the correct firmware to card
54062306a36Sopenharmony_ci *      - Issue the init commands to firmware
54162306a36Sopenharmony_ci */
54262306a36Sopenharmony_cistatic int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	int ret;
54562306a36Sopenharmony_ci	char fmt[64];
54662306a36Sopenharmony_ci	struct mwifiex_adapter *adapter = context;
54762306a36Sopenharmony_ci	struct mwifiex_fw_image fw;
54862306a36Sopenharmony_ci	bool init_failed = false;
54962306a36Sopenharmony_ci	struct wireless_dev *wdev;
55062306a36Sopenharmony_ci	struct completion *fw_done = adapter->fw_done;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (!firmware) {
55362306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
55462306a36Sopenharmony_ci			    "Failed to get firmware %s\n", adapter->fw_name);
55562306a36Sopenharmony_ci		goto err_dnld_fw;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	memset(&fw, 0, sizeof(struct mwifiex_fw_image));
55962306a36Sopenharmony_ci	adapter->firmware = firmware;
56062306a36Sopenharmony_ci	fw.fw_buf = (u8 *) adapter->firmware->data;
56162306a36Sopenharmony_ci	fw.fw_len = adapter->firmware->size;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (adapter->if_ops.dnld_fw) {
56462306a36Sopenharmony_ci		ret = adapter->if_ops.dnld_fw(adapter, &fw);
56562306a36Sopenharmony_ci	} else {
56662306a36Sopenharmony_ci		ret = mwifiex_dnld_fw(adapter, &fw);
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (ret == -1)
57062306a36Sopenharmony_ci		goto err_dnld_fw;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG, "WLAN FW is active\n");
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (cal_data_cfg) {
57562306a36Sopenharmony_ci		if ((request_firmware(&adapter->cal_data, cal_data_cfg,
57662306a36Sopenharmony_ci				      adapter->dev)) < 0)
57762306a36Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
57862306a36Sopenharmony_ci				    "Cal data request_firmware() failed\n");
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* enable host interrupt after fw dnld is successful */
58262306a36Sopenharmony_ci	if (adapter->if_ops.enable_int) {
58362306a36Sopenharmony_ci		if (adapter->if_ops.enable_int(adapter))
58462306a36Sopenharmony_ci			goto err_dnld_fw;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	adapter->init_wait_q_woken = false;
58862306a36Sopenharmony_ci	ret = mwifiex_init_fw(adapter);
58962306a36Sopenharmony_ci	if (ret == -1) {
59062306a36Sopenharmony_ci		goto err_init_fw;
59162306a36Sopenharmony_ci	} else if (!ret) {
59262306a36Sopenharmony_ci		adapter->hw_status = MWIFIEX_HW_STATUS_READY;
59362306a36Sopenharmony_ci		goto done;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci	/* Wait for mwifiex_init to complete */
59662306a36Sopenharmony_ci	if (!adapter->mfg_mode) {
59762306a36Sopenharmony_ci		wait_event_interruptible(adapter->init_wait_q,
59862306a36Sopenharmony_ci					 adapter->init_wait_q_woken);
59962306a36Sopenharmony_ci		if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
60062306a36Sopenharmony_ci			goto err_init_fw;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (!adapter->wiphy) {
60462306a36Sopenharmony_ci		if (mwifiex_register_cfg80211(adapter)) {
60562306a36Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
60662306a36Sopenharmony_ci				    "cannot register with cfg80211\n");
60762306a36Sopenharmony_ci			goto err_init_fw;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (mwifiex_init_channel_scan_gap(adapter)) {
61262306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
61362306a36Sopenharmony_ci			    "could not init channel stats table\n");
61462306a36Sopenharmony_ci		goto err_init_chan_scan;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (driver_mode) {
61862306a36Sopenharmony_ci		driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
61962306a36Sopenharmony_ci		driver_mode |= MWIFIEX_DRIVER_MODE_STA;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	rtnl_lock();
62362306a36Sopenharmony_ci	wiphy_lock(adapter->wiphy);
62462306a36Sopenharmony_ci	/* Create station interface by default */
62562306a36Sopenharmony_ci	wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
62662306a36Sopenharmony_ci					NL80211_IFTYPE_STATION, NULL);
62762306a36Sopenharmony_ci	if (IS_ERR(wdev)) {
62862306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
62962306a36Sopenharmony_ci			    "cannot create default STA interface\n");
63062306a36Sopenharmony_ci		wiphy_unlock(adapter->wiphy);
63162306a36Sopenharmony_ci		rtnl_unlock();
63262306a36Sopenharmony_ci		goto err_add_intf;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
63662306a36Sopenharmony_ci		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
63762306a36Sopenharmony_ci						NL80211_IFTYPE_AP, NULL);
63862306a36Sopenharmony_ci		if (IS_ERR(wdev)) {
63962306a36Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
64062306a36Sopenharmony_ci				    "cannot create AP interface\n");
64162306a36Sopenharmony_ci			wiphy_unlock(adapter->wiphy);
64262306a36Sopenharmony_ci			rtnl_unlock();
64362306a36Sopenharmony_ci			goto err_add_intf;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
64862306a36Sopenharmony_ci		wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM,
64962306a36Sopenharmony_ci						NL80211_IFTYPE_P2P_CLIENT, NULL);
65062306a36Sopenharmony_ci		if (IS_ERR(wdev)) {
65162306a36Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
65262306a36Sopenharmony_ci				    "cannot create p2p client interface\n");
65362306a36Sopenharmony_ci			wiphy_unlock(adapter->wiphy);
65462306a36Sopenharmony_ci			rtnl_unlock();
65562306a36Sopenharmony_ci			goto err_add_intf;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci	wiphy_unlock(adapter->wiphy);
65962306a36Sopenharmony_ci	rtnl_unlock();
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
66262306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt);
66362306a36Sopenharmony_ci	adapter->is_up = true;
66462306a36Sopenharmony_ci	goto done;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cierr_add_intf:
66762306a36Sopenharmony_ci	vfree(adapter->chan_stats);
66862306a36Sopenharmony_cierr_init_chan_scan:
66962306a36Sopenharmony_ci	wiphy_unregister(adapter->wiphy);
67062306a36Sopenharmony_ci	wiphy_free(adapter->wiphy);
67162306a36Sopenharmony_cierr_init_fw:
67262306a36Sopenharmony_ci	if (adapter->if_ops.disable_int)
67362306a36Sopenharmony_ci		adapter->if_ops.disable_int(adapter);
67462306a36Sopenharmony_cierr_dnld_fw:
67562306a36Sopenharmony_ci	mwifiex_dbg(adapter, ERROR,
67662306a36Sopenharmony_ci		    "info: %s: unregister device\n", __func__);
67762306a36Sopenharmony_ci	if (adapter->if_ops.unregister_dev)
67862306a36Sopenharmony_ci		adapter->if_ops.unregister_dev(adapter);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
68162306a36Sopenharmony_ci	mwifiex_terminate_workqueue(adapter);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
68462306a36Sopenharmony_ci		pr_debug("info: %s: shutdown mwifiex\n", __func__);
68562306a36Sopenharmony_ci		mwifiex_shutdown_drv(adapter);
68662306a36Sopenharmony_ci		mwifiex_free_cmd_buffers(adapter);
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	init_failed = true;
69062306a36Sopenharmony_cidone:
69162306a36Sopenharmony_ci	if (adapter->cal_data) {
69262306a36Sopenharmony_ci		release_firmware(adapter->cal_data);
69362306a36Sopenharmony_ci		adapter->cal_data = NULL;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	if (adapter->firmware) {
69662306a36Sopenharmony_ci		release_firmware(adapter->firmware);
69762306a36Sopenharmony_ci		adapter->firmware = NULL;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci	if (init_failed) {
70062306a36Sopenharmony_ci		if (adapter->irq_wakeup >= 0)
70162306a36Sopenharmony_ci			device_init_wakeup(adapter->dev, false);
70262306a36Sopenharmony_ci		mwifiex_free_adapter(adapter);
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	/* Tell all current and future waiters we're finished */
70562306a36Sopenharmony_ci	complete_all(fw_done);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return init_failed ? -EIO : 0;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	_mwifiex_fw_dpc(firmware, context);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/*
71662306a36Sopenharmony_ci * This function gets the firmware and (if called asynchronously) kicks off the
71762306a36Sopenharmony_ci * HW init when done.
71862306a36Sopenharmony_ci */
71962306a36Sopenharmony_cistatic int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter,
72062306a36Sopenharmony_ci			      bool req_fw_nowait)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	int ret;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* Override default firmware with manufacturing one if
72562306a36Sopenharmony_ci	 * manufacturing mode is enabled
72662306a36Sopenharmony_ci	 */
72762306a36Sopenharmony_ci	if (mfg_mode)
72862306a36Sopenharmony_ci		strscpy(adapter->fw_name, MFG_FIRMWARE,
72962306a36Sopenharmony_ci			sizeof(adapter->fw_name));
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (req_fw_nowait) {
73262306a36Sopenharmony_ci		ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
73362306a36Sopenharmony_ci					      adapter->dev, GFP_KERNEL, adapter,
73462306a36Sopenharmony_ci					      mwifiex_fw_dpc);
73562306a36Sopenharmony_ci	} else {
73662306a36Sopenharmony_ci		ret = request_firmware(&adapter->firmware,
73762306a36Sopenharmony_ci				       adapter->fw_name,
73862306a36Sopenharmony_ci				       adapter->dev);
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (ret < 0)
74262306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR, "request_firmware%s error %d\n",
74362306a36Sopenharmony_ci			    req_fw_nowait ? "_nowait" : "", ret);
74462306a36Sopenharmony_ci	return ret;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/*
74862306a36Sopenharmony_ci * CFG802.11 network device handler for open.
74962306a36Sopenharmony_ci *
75062306a36Sopenharmony_ci * Starts the data queue.
75162306a36Sopenharmony_ci */
75262306a36Sopenharmony_cistatic int
75362306a36Sopenharmony_cimwifiex_open(struct net_device *dev)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	netif_carrier_off(dev);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return 0;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci/*
76162306a36Sopenharmony_ci * CFG802.11 network device handler for close.
76262306a36Sopenharmony_ci */
76362306a36Sopenharmony_cistatic int
76462306a36Sopenharmony_cimwifiex_close(struct net_device *dev)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (priv->scan_request) {
76962306a36Sopenharmony_ci		struct cfg80211_scan_info info = {
77062306a36Sopenharmony_ci			.aborted = true,
77162306a36Sopenharmony_ci		};
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, INFO,
77462306a36Sopenharmony_ci			    "aborting scan on ndo_stop\n");
77562306a36Sopenharmony_ci		cfg80211_scan_done(priv->scan_request, &info);
77662306a36Sopenharmony_ci		priv->scan_request = NULL;
77762306a36Sopenharmony_ci		priv->scan_aborting = true;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (priv->sched_scanning) {
78162306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, INFO,
78262306a36Sopenharmony_ci			    "aborting bgscan on ndo_stop\n");
78362306a36Sopenharmony_ci		mwifiex_stop_bg_scan(priv);
78462306a36Sopenharmony_ci		cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic bool
79162306a36Sopenharmony_cimwifiex_bypass_tx_queue(struct mwifiex_private *priv,
79262306a36Sopenharmony_ci			struct sk_buff *skb)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (ntohs(eth_hdr->h_proto) == ETH_P_PAE ||
79762306a36Sopenharmony_ci	    mwifiex_is_skb_mgmt_frame(skb) ||
79862306a36Sopenharmony_ci	    (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
79962306a36Sopenharmony_ci	     ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
80062306a36Sopenharmony_ci	     (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) {
80162306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, DATA,
80262306a36Sopenharmony_ci			    "bypass txqueue; eth type %#x, mgmt %d\n",
80362306a36Sopenharmony_ci			     ntohs(eth_hdr->h_proto),
80462306a36Sopenharmony_ci			     mwifiex_is_skb_mgmt_frame(skb));
80562306a36Sopenharmony_ci		return true;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return false;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci/*
81162306a36Sopenharmony_ci * Add buffer into wmm tx queue and queue work to transmit it.
81262306a36Sopenharmony_ci */
81362306a36Sopenharmony_ciint mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct netdev_queue *txq;
81662306a36Sopenharmony_ci	int index = mwifiex_1d_to_wmm_queue[skb->priority];
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
81962306a36Sopenharmony_ci		txq = netdev_get_tx_queue(priv->netdev, index);
82062306a36Sopenharmony_ci		if (!netif_tx_queue_stopped(txq)) {
82162306a36Sopenharmony_ci			netif_tx_stop_queue(txq);
82262306a36Sopenharmony_ci			mwifiex_dbg(priv->adapter, DATA,
82362306a36Sopenharmony_ci				    "stop queue: %d\n", index);
82462306a36Sopenharmony_ci		}
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (mwifiex_bypass_tx_queue(priv, skb)) {
82862306a36Sopenharmony_ci		atomic_inc(&priv->adapter->tx_pending);
82962306a36Sopenharmony_ci		atomic_inc(&priv->adapter->bypass_tx_pending);
83062306a36Sopenharmony_ci		mwifiex_wmm_add_buf_bypass_txqueue(priv, skb);
83162306a36Sopenharmony_ci	 } else {
83262306a36Sopenharmony_ci		atomic_inc(&priv->adapter->tx_pending);
83362306a36Sopenharmony_ci		mwifiex_wmm_add_buf_txqueue(priv, skb);
83462306a36Sopenharmony_ci	 }
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	mwifiex_queue_main_work(priv->adapter);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return 0;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistruct sk_buff *
84262306a36Sopenharmony_cimwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
84362306a36Sopenharmony_ci				struct sk_buff *skb, u8 flag, u64 *cookie)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	struct sk_buff *orig_skb = skb;
84662306a36Sopenharmony_ci	struct mwifiex_txinfo *tx_info, *orig_tx_info;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	skb = skb_clone(skb, GFP_ATOMIC);
84962306a36Sopenharmony_ci	if (skb) {
85062306a36Sopenharmony_ci		int id;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		spin_lock_bh(&priv->ack_status_lock);
85362306a36Sopenharmony_ci		id = idr_alloc(&priv->ack_status_frames, orig_skb,
85462306a36Sopenharmony_ci			       1, 0x10, GFP_ATOMIC);
85562306a36Sopenharmony_ci		spin_unlock_bh(&priv->ack_status_lock);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		if (id >= 0) {
85862306a36Sopenharmony_ci			tx_info = MWIFIEX_SKB_TXCB(skb);
85962306a36Sopenharmony_ci			tx_info->ack_frame_id = id;
86062306a36Sopenharmony_ci			tx_info->flags |= flag;
86162306a36Sopenharmony_ci			orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
86262306a36Sopenharmony_ci			orig_tx_info->ack_frame_id = id;
86362306a36Sopenharmony_ci			orig_tx_info->flags |= flag;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci			if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
86662306a36Sopenharmony_ci				orig_tx_info->cookie = *cookie;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		} else if (skb_shared(skb)) {
86962306a36Sopenharmony_ci			kfree_skb(orig_skb);
87062306a36Sopenharmony_ci		} else {
87162306a36Sopenharmony_ci			kfree_skb(skb);
87262306a36Sopenharmony_ci			skb = orig_skb;
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci	} else {
87562306a36Sopenharmony_ci		/* couldn't clone -- lose tx status ... */
87662306a36Sopenharmony_ci		skb = orig_skb;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return skb;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci/*
88362306a36Sopenharmony_ci * CFG802.11 network device handler for data transmission.
88462306a36Sopenharmony_ci */
88562306a36Sopenharmony_cistatic netdev_tx_t
88662306a36Sopenharmony_cimwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
88962306a36Sopenharmony_ci	struct sk_buff *new_skb;
89062306a36Sopenharmony_ci	struct mwifiex_txinfo *tx_info;
89162306a36Sopenharmony_ci	bool multicast;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	mwifiex_dbg(priv->adapter, DATA,
89462306a36Sopenharmony_ci		    "data: %lu BSS(%d-%d): Data <= kernel\n",
89562306a36Sopenharmony_ci		    jiffies, priv->bss_type, priv->bss_num);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &priv->adapter->work_flags)) {
89862306a36Sopenharmony_ci		kfree_skb(skb);
89962306a36Sopenharmony_ci		priv->stats.tx_dropped++;
90062306a36Sopenharmony_ci		return 0;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci	if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
90362306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR,
90462306a36Sopenharmony_ci			    "Tx: bad skb len %d\n", skb->len);
90562306a36Sopenharmony_ci		kfree_skb(skb);
90662306a36Sopenharmony_ci		priv->stats.tx_dropped++;
90762306a36Sopenharmony_ci		return 0;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
91062306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, DATA,
91162306a36Sopenharmony_ci			    "data: Tx: insufficient skb headroom %d\n",
91262306a36Sopenharmony_ci			    skb_headroom(skb));
91362306a36Sopenharmony_ci		/* Insufficient skb headroom - allocate a new skb */
91462306a36Sopenharmony_ci		new_skb =
91562306a36Sopenharmony_ci			skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
91662306a36Sopenharmony_ci		if (unlikely(!new_skb)) {
91762306a36Sopenharmony_ci			mwifiex_dbg(priv->adapter, ERROR,
91862306a36Sopenharmony_ci				    "Tx: cannot alloca new_skb\n");
91962306a36Sopenharmony_ci			kfree_skb(skb);
92062306a36Sopenharmony_ci			priv->stats.tx_dropped++;
92162306a36Sopenharmony_ci			return 0;
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci		kfree_skb(skb);
92462306a36Sopenharmony_ci		skb = new_skb;
92562306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, INFO,
92662306a36Sopenharmony_ci			    "info: new skb headroomd %d\n",
92762306a36Sopenharmony_ci			    skb_headroom(skb));
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	tx_info = MWIFIEX_SKB_TXCB(skb);
93162306a36Sopenharmony_ci	memset(tx_info, 0, sizeof(*tx_info));
93262306a36Sopenharmony_ci	tx_info->bss_num = priv->bss_num;
93362306a36Sopenharmony_ci	tx_info->bss_type = priv->bss_type;
93462306a36Sopenharmony_ci	tx_info->pkt_len = skb->len;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	multicast = is_multicast_ether_addr(skb->data);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (unlikely(!multicast && skb->sk &&
93962306a36Sopenharmony_ci		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
94062306a36Sopenharmony_ci		     priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
94162306a36Sopenharmony_ci		skb = mwifiex_clone_skb_for_tx_status(priv,
94262306a36Sopenharmony_ci						      skb,
94362306a36Sopenharmony_ci					MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/* Record the current time the packet was queued; used to
94662306a36Sopenharmony_ci	 * determine the amount of time the packet was queued in
94762306a36Sopenharmony_ci	 * the driver before it was sent to the firmware.
94862306a36Sopenharmony_ci	 * The delay is then sent along with the packet to the
94962306a36Sopenharmony_ci	 * firmware for aggregate delay calculation for stats and
95062306a36Sopenharmony_ci	 * MSDU lifetime expiry.
95162306a36Sopenharmony_ci	 */
95262306a36Sopenharmony_ci	__net_timestamp(skb);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
95562306a36Sopenharmony_ci	    priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
95662306a36Sopenharmony_ci	    !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
95762306a36Sopenharmony_ci		if (priv->adapter->auto_tdls && priv->check_tdls_tx)
95862306a36Sopenharmony_ci			mwifiex_tdls_check_tx(priv, skb);
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	mwifiex_queue_tx_pkt(priv, skb);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ciint mwifiex_set_mac_address(struct mwifiex_private *priv,
96762306a36Sopenharmony_ci			    struct net_device *dev, bool external,
96862306a36Sopenharmony_ci			    u8 *new_mac)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	int ret;
97162306a36Sopenharmony_ci	u64 mac_addr, old_mac_addr;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	old_mac_addr = ether_addr_to_u64(priv->curr_addr);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (external) {
97662306a36Sopenharmony_ci		mac_addr = ether_addr_to_u64(new_mac);
97762306a36Sopenharmony_ci	} else {
97862306a36Sopenharmony_ci		/* Internal mac address change */
97962306a36Sopenharmony_ci		if (priv->bss_type == MWIFIEX_BSS_TYPE_ANY)
98062306a36Sopenharmony_ci			return -EOPNOTSUPP;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		mac_addr = old_mac_addr;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) {
98562306a36Sopenharmony_ci			mac_addr |= BIT_ULL(MWIFIEX_MAC_LOCAL_ADMIN_BIT);
98662306a36Sopenharmony_ci			mac_addr += priv->bss_num;
98762306a36Sopenharmony_ci		} else if (priv->adapter->priv[0] != priv) {
98862306a36Sopenharmony_ci			/* Set mac address based on bss_type/bss_num */
98962306a36Sopenharmony_ci			mac_addr ^= BIT_ULL(priv->bss_type + 8);
99062306a36Sopenharmony_ci			mac_addr += priv->bss_num;
99162306a36Sopenharmony_ci		}
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	u64_to_ether_addr(mac_addr, priv->curr_addr);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* Send request to firmware */
99762306a36Sopenharmony_ci	ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS,
99862306a36Sopenharmony_ci			       HostCmd_ACT_GEN_SET, 0, NULL, true);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	if (ret) {
100162306a36Sopenharmony_ci		u64_to_ether_addr(old_mac_addr, priv->curr_addr);
100262306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR,
100362306a36Sopenharmony_ci			    "set mac address failed: ret=%d\n", ret);
100462306a36Sopenharmony_ci		return ret;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	eth_hw_addr_set(dev, priv->curr_addr);
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci/* CFG802.11 network device handler for setting MAC address.
101262306a36Sopenharmony_ci */
101362306a36Sopenharmony_cistatic int
101462306a36Sopenharmony_cimwifiex_ndo_set_mac_address(struct net_device *dev, void *addr)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
101762306a36Sopenharmony_ci	struct sockaddr *hw_addr = addr;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	return mwifiex_set_mac_address(priv, dev, true, hw_addr->sa_data);
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci/*
102362306a36Sopenharmony_ci * CFG802.11 network device handler for setting multicast list.
102462306a36Sopenharmony_ci */
102562306a36Sopenharmony_cistatic void mwifiex_set_multicast_list(struct net_device *dev)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
102862306a36Sopenharmony_ci	struct mwifiex_multicast_list mcast_list;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
103162306a36Sopenharmony_ci		mcast_list.mode = MWIFIEX_PROMISC_MODE;
103262306a36Sopenharmony_ci	} else if (dev->flags & IFF_ALLMULTI ||
103362306a36Sopenharmony_ci		   netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) {
103462306a36Sopenharmony_ci		mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
103562306a36Sopenharmony_ci	} else {
103662306a36Sopenharmony_ci		mcast_list.mode = MWIFIEX_MULTICAST_MODE;
103762306a36Sopenharmony_ci		mcast_list.num_multicast_addr =
103862306a36Sopenharmony_ci			mwifiex_copy_mcast_addr(&mcast_list, dev);
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci	mwifiex_request_set_multicast_list(priv, &mcast_list);
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci/*
104462306a36Sopenharmony_ci * CFG802.11 network device handler for transmission timeout.
104562306a36Sopenharmony_ci */
104662306a36Sopenharmony_cistatic void
104762306a36Sopenharmony_cimwifiex_tx_timeout(struct net_device *dev, unsigned int txqueue)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	priv->num_tx_timeout++;
105262306a36Sopenharmony_ci	priv->tx_timeout_cnt++;
105362306a36Sopenharmony_ci	mwifiex_dbg(priv->adapter, ERROR,
105462306a36Sopenharmony_ci		    "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
105562306a36Sopenharmony_ci		    jiffies, priv->tx_timeout_cnt, priv->bss_type,
105662306a36Sopenharmony_ci		    priv->bss_num);
105762306a36Sopenharmony_ci	mwifiex_set_trans_start(dev);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
106062306a36Sopenharmony_ci	    priv->adapter->if_ops.card_reset) {
106162306a36Sopenharmony_ci		mwifiex_dbg(priv->adapter, ERROR,
106262306a36Sopenharmony_ci			    "tx_timeout_cnt exceeds threshold.\t"
106362306a36Sopenharmony_ci			    "Triggering card reset!\n");
106462306a36Sopenharmony_ci		priv->adapter->if_ops.card_reset(priv->adapter);
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_civoid mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct usb_card_rec *card = adapter->card;
107162306a36Sopenharmony_ci	struct mwifiex_private *priv;
107262306a36Sopenharmony_ci	u16 tx_buf_size;
107362306a36Sopenharmony_ci	int i, ret;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	card->mc_resync_flag = true;
107662306a36Sopenharmony_ci	for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
107762306a36Sopenharmony_ci		if (atomic_read(&card->port[i].tx_data_urb_pending)) {
107862306a36Sopenharmony_ci			mwifiex_dbg(adapter, WARN, "pending data urb in sys\n");
107962306a36Sopenharmony_ci			return;
108062306a36Sopenharmony_ci		}
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	card->mc_resync_flag = false;
108462306a36Sopenharmony_ci	tx_buf_size = 0xffff;
108562306a36Sopenharmony_ci	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
108662306a36Sopenharmony_ci	ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
108762306a36Sopenharmony_ci			       HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false);
108862306a36Sopenharmony_ci	if (ret)
108962306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
109062306a36Sopenharmony_ci			    "send reconfig tx buf size cmd err\n");
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_civoid mwifiex_upload_device_dump(struct mwifiex_adapter *adapter)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	/* Dump all the memory data into single file, a userspace script will
109762306a36Sopenharmony_ci	 * be used to split all the memory data to multiple files
109862306a36Sopenharmony_ci	 */
109962306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG,
110062306a36Sopenharmony_ci		    "== mwifiex dump information to /sys/class/devcoredump start\n");
110162306a36Sopenharmony_ci	dev_coredumpv(adapter->dev, adapter->devdump_data, adapter->devdump_len,
110262306a36Sopenharmony_ci		      GFP_KERNEL);
110362306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG,
110462306a36Sopenharmony_ci		    "== mwifiex dump information to /sys/class/devcoredump end\n");
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	/* Device dump data will be freed in device coredump release function
110762306a36Sopenharmony_ci	 * after 5 min. Here reset adapter->devdump_data and ->devdump_len
110862306a36Sopenharmony_ci	 * to avoid it been accidentally reused.
110962306a36Sopenharmony_ci	 */
111062306a36Sopenharmony_ci	adapter->devdump_data = NULL;
111162306a36Sopenharmony_ci	adapter->devdump_len = 0;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_upload_device_dump);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_civoid mwifiex_drv_info_dump(struct mwifiex_adapter *adapter)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	char *p;
111862306a36Sopenharmony_ci	char drv_version[64];
111962306a36Sopenharmony_ci	struct usb_card_rec *cardp;
112062306a36Sopenharmony_ci	struct sdio_mmc_card *sdio_card;
112162306a36Sopenharmony_ci	struct mwifiex_private *priv;
112262306a36Sopenharmony_ci	int i, idx;
112362306a36Sopenharmony_ci	struct netdev_queue *txq;
112462306a36Sopenharmony_ci	struct mwifiex_debug_info *debug_info;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n");
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	p = adapter->devdump_data;
112962306a36Sopenharmony_ci	strcpy(p, "========Start dump driverinfo========\n");
113062306a36Sopenharmony_ci	p += strlen("========Start dump driverinfo========\n");
113162306a36Sopenharmony_ci	p += sprintf(p, "driver_name = " "\"mwifiex\"\n");
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	mwifiex_drv_get_driver_version(adapter, drv_version,
113462306a36Sopenharmony_ci				       sizeof(drv_version) - 1);
113562306a36Sopenharmony_ci	p += sprintf(p, "driver_version = %s\n", drv_version);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_USB) {
113862306a36Sopenharmony_ci		cardp = (struct usb_card_rec *)adapter->card;
113962306a36Sopenharmony_ci		p += sprintf(p, "tx_cmd_urb_pending = %d\n",
114062306a36Sopenharmony_ci			     atomic_read(&cardp->tx_cmd_urb_pending));
114162306a36Sopenharmony_ci		p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n",
114262306a36Sopenharmony_ci			     atomic_read(&cardp->port[0].tx_data_urb_pending));
114362306a36Sopenharmony_ci		p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n",
114462306a36Sopenharmony_ci			     atomic_read(&cardp->port[1].tx_data_urb_pending));
114562306a36Sopenharmony_ci		p += sprintf(p, "rx_cmd_urb_pending = %d\n",
114662306a36Sopenharmony_ci			     atomic_read(&cardp->rx_cmd_urb_pending));
114762306a36Sopenharmony_ci		p += sprintf(p, "rx_data_urb_pending = %d\n",
114862306a36Sopenharmony_ci			     atomic_read(&cardp->rx_data_urb_pending));
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	p += sprintf(p, "tx_pending = %d\n",
115262306a36Sopenharmony_ci		     atomic_read(&adapter->tx_pending));
115362306a36Sopenharmony_ci	p += sprintf(p, "rx_pending = %d\n",
115462306a36Sopenharmony_ci		     atomic_read(&adapter->rx_pending));
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_SDIO) {
115762306a36Sopenharmony_ci		sdio_card = (struct sdio_mmc_card *)adapter->card;
115862306a36Sopenharmony_ci		p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
115962306a36Sopenharmony_ci			     sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
116062306a36Sopenharmony_ci		p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
116162306a36Sopenharmony_ci			     sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
116562306a36Sopenharmony_ci		if (!adapter->priv[i] || !adapter->priv[i]->netdev)
116662306a36Sopenharmony_ci			continue;
116762306a36Sopenharmony_ci		priv = adapter->priv[i];
116862306a36Sopenharmony_ci		p += sprintf(p, "\n[interface  : \"%s\"]\n",
116962306a36Sopenharmony_ci			     priv->netdev->name);
117062306a36Sopenharmony_ci		p += sprintf(p, "wmm_tx_pending[0] = %d\n",
117162306a36Sopenharmony_ci			     atomic_read(&priv->wmm_tx_pending[0]));
117262306a36Sopenharmony_ci		p += sprintf(p, "wmm_tx_pending[1] = %d\n",
117362306a36Sopenharmony_ci			     atomic_read(&priv->wmm_tx_pending[1]));
117462306a36Sopenharmony_ci		p += sprintf(p, "wmm_tx_pending[2] = %d\n",
117562306a36Sopenharmony_ci			     atomic_read(&priv->wmm_tx_pending[2]));
117662306a36Sopenharmony_ci		p += sprintf(p, "wmm_tx_pending[3] = %d\n",
117762306a36Sopenharmony_ci			     atomic_read(&priv->wmm_tx_pending[3]));
117862306a36Sopenharmony_ci		p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
117962306a36Sopenharmony_ci			     "Disconnected" : "Connected");
118062306a36Sopenharmony_ci		p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
118162306a36Sopenharmony_ci			     ? "on" : "off"));
118262306a36Sopenharmony_ci		for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
118362306a36Sopenharmony_ci			txq = netdev_get_tx_queue(priv->netdev, idx);
118462306a36Sopenharmony_ci			p += sprintf(p, "tx queue %d:%s  ", idx,
118562306a36Sopenharmony_ci				     netif_tx_queue_stopped(txq) ?
118662306a36Sopenharmony_ci				     "stopped" : "started");
118762306a36Sopenharmony_ci		}
118862306a36Sopenharmony_ci		p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
118962306a36Sopenharmony_ci			     priv->netdev->name, priv->num_tx_timeout);
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_SDIO ||
119362306a36Sopenharmony_ci	    adapter->iface_type == MWIFIEX_PCIE) {
119462306a36Sopenharmony_ci		p += sprintf(p, "\n=== %s register dump===\n",
119562306a36Sopenharmony_ci			     adapter->iface_type == MWIFIEX_SDIO ?
119662306a36Sopenharmony_ci							"SDIO" : "PCIE");
119762306a36Sopenharmony_ci		if (adapter->if_ops.reg_dump)
119862306a36Sopenharmony_ci			p += adapter->if_ops.reg_dump(adapter, p);
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci	p += sprintf(p, "\n=== more debug information\n");
120162306a36Sopenharmony_ci	debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
120262306a36Sopenharmony_ci	if (debug_info) {
120362306a36Sopenharmony_ci		for (i = 0; i < adapter->priv_num; i++) {
120462306a36Sopenharmony_ci			if (!adapter->priv[i] || !adapter->priv[i]->netdev)
120562306a36Sopenharmony_ci				continue;
120662306a36Sopenharmony_ci			priv = adapter->priv[i];
120762306a36Sopenharmony_ci			mwifiex_get_debug_info(priv, debug_info);
120862306a36Sopenharmony_ci			p += mwifiex_debug_info_to_buffer(priv, p, debug_info);
120962306a36Sopenharmony_ci			break;
121062306a36Sopenharmony_ci		}
121162306a36Sopenharmony_ci		kfree(debug_info);
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	strcpy(p, "\n========End dump========\n");
121562306a36Sopenharmony_ci	p += strlen("\n========End dump========\n");
121662306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n");
121762306a36Sopenharmony_ci	adapter->devdump_len = p - (char *)adapter->devdump_data;
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_drv_info_dump);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_civoid mwifiex_prepare_fw_dump_info(struct mwifiex_adapter *adapter)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	u8 idx;
122462306a36Sopenharmony_ci	char *fw_dump_ptr;
122562306a36Sopenharmony_ci	u32 dump_len = 0;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	for (idx = 0; idx < adapter->num_mem_types; idx++) {
122862306a36Sopenharmony_ci		struct memory_type_mapping *entry =
122962306a36Sopenharmony_ci				&adapter->mem_type_mapping_tbl[idx];
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		if (entry->mem_ptr) {
123262306a36Sopenharmony_ci			dump_len += (strlen("========Start dump ") +
123362306a36Sopenharmony_ci					strlen(entry->mem_name) +
123462306a36Sopenharmony_ci					strlen("========\n") +
123562306a36Sopenharmony_ci					(entry->mem_size + 1) +
123662306a36Sopenharmony_ci					strlen("\n========End dump========\n"));
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	if (dump_len + 1 + adapter->devdump_len > MWIFIEX_FW_DUMP_SIZE) {
124162306a36Sopenharmony_ci		/* Realloc in case buffer overflow */
124262306a36Sopenharmony_ci		fw_dump_ptr = vzalloc(dump_len + 1 + adapter->devdump_len);
124362306a36Sopenharmony_ci		mwifiex_dbg(adapter, MSG, "Realloc device dump data.\n");
124462306a36Sopenharmony_ci		if (!fw_dump_ptr) {
124562306a36Sopenharmony_ci			vfree(adapter->devdump_data);
124662306a36Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
124762306a36Sopenharmony_ci				    "vzalloc devdump data failure!\n");
124862306a36Sopenharmony_ci			return;
124962306a36Sopenharmony_ci		}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci		memmove(fw_dump_ptr, adapter->devdump_data,
125262306a36Sopenharmony_ci			adapter->devdump_len);
125362306a36Sopenharmony_ci		vfree(adapter->devdump_data);
125462306a36Sopenharmony_ci		adapter->devdump_data = fw_dump_ptr;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	fw_dump_ptr = (char *)adapter->devdump_data + adapter->devdump_len;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	for (idx = 0; idx < adapter->num_mem_types; idx++) {
126062306a36Sopenharmony_ci		struct memory_type_mapping *entry =
126162306a36Sopenharmony_ci					&adapter->mem_type_mapping_tbl[idx];
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		if (entry->mem_ptr) {
126462306a36Sopenharmony_ci			strcpy(fw_dump_ptr, "========Start dump ");
126562306a36Sopenharmony_ci			fw_dump_ptr += strlen("========Start dump ");
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci			strcpy(fw_dump_ptr, entry->mem_name);
126862306a36Sopenharmony_ci			fw_dump_ptr += strlen(entry->mem_name);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci			strcpy(fw_dump_ptr, "========\n");
127162306a36Sopenharmony_ci			fw_dump_ptr += strlen("========\n");
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci			memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
127462306a36Sopenharmony_ci			fw_dump_ptr += entry->mem_size;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci			strcpy(fw_dump_ptr, "\n========End dump========\n");
127762306a36Sopenharmony_ci			fw_dump_ptr += strlen("\n========End dump========\n");
127862306a36Sopenharmony_ci		}
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	adapter->devdump_len = fw_dump_ptr - (char *)adapter->devdump_data;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	for (idx = 0; idx < adapter->num_mem_types; idx++) {
128462306a36Sopenharmony_ci		struct memory_type_mapping *entry =
128562306a36Sopenharmony_ci			&adapter->mem_type_mapping_tbl[idx];
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		vfree(entry->mem_ptr);
128862306a36Sopenharmony_ci		entry->mem_ptr = NULL;
128962306a36Sopenharmony_ci		entry->mem_size = 0;
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_prepare_fw_dump_info);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci/*
129562306a36Sopenharmony_ci * CFG802.11 network device handler for statistics retrieval.
129662306a36Sopenharmony_ci */
129762306a36Sopenharmony_cistatic struct net_device_stats *mwifiex_get_stats(struct net_device *dev)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	return &priv->stats;
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic u16
130562306a36Sopenharmony_cimwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
130662306a36Sopenharmony_ci				struct net_device *sb_dev)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	skb->priority = cfg80211_classify8021d(skb, NULL);
130962306a36Sopenharmony_ci	return mwifiex_1d_to_wmm_queue[skb->priority];
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci/* Network device handlers */
131362306a36Sopenharmony_cistatic const struct net_device_ops mwifiex_netdev_ops = {
131462306a36Sopenharmony_ci	.ndo_open = mwifiex_open,
131562306a36Sopenharmony_ci	.ndo_stop = mwifiex_close,
131662306a36Sopenharmony_ci	.ndo_start_xmit = mwifiex_hard_start_xmit,
131762306a36Sopenharmony_ci	.ndo_set_mac_address = mwifiex_ndo_set_mac_address,
131862306a36Sopenharmony_ci	.ndo_validate_addr = eth_validate_addr,
131962306a36Sopenharmony_ci	.ndo_tx_timeout = mwifiex_tx_timeout,
132062306a36Sopenharmony_ci	.ndo_get_stats = mwifiex_get_stats,
132162306a36Sopenharmony_ci	.ndo_set_rx_mode = mwifiex_set_multicast_list,
132262306a36Sopenharmony_ci	.ndo_select_queue = mwifiex_netdev_select_wmm_queue,
132362306a36Sopenharmony_ci};
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci/*
132662306a36Sopenharmony_ci * This function initializes the private structure parameters.
132762306a36Sopenharmony_ci *
132862306a36Sopenharmony_ci * The following wait queues are initialized -
132962306a36Sopenharmony_ci *      - IOCTL wait queue
133062306a36Sopenharmony_ci *      - Command wait queue
133162306a36Sopenharmony_ci *      - Statistics wait queue
133262306a36Sopenharmony_ci *
133362306a36Sopenharmony_ci * ...and the following default parameters are set -
133462306a36Sopenharmony_ci *      - Current key index     : Set to 0
133562306a36Sopenharmony_ci *      - Rate index            : Set to auto
133662306a36Sopenharmony_ci *      - Media connected       : Set to disconnected
133762306a36Sopenharmony_ci *      - Adhoc link sensed     : Set to false
133862306a36Sopenharmony_ci *      - Nick name             : Set to null
133962306a36Sopenharmony_ci *      - Number of Tx timeout  : Set to 0
134062306a36Sopenharmony_ci *      - Device address        : Set to current address
134162306a36Sopenharmony_ci *      - Rx histogram statistc : Set to 0
134262306a36Sopenharmony_ci *
134362306a36Sopenharmony_ci * In addition, the CFG80211 work queue is also created.
134462306a36Sopenharmony_ci */
134562306a36Sopenharmony_civoid mwifiex_init_priv_params(struct mwifiex_private *priv,
134662306a36Sopenharmony_ci			      struct net_device *dev)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	dev->netdev_ops = &mwifiex_netdev_ops;
134962306a36Sopenharmony_ci	dev->needs_free_netdev = true;
135062306a36Sopenharmony_ci	/* Initialize private structure */
135162306a36Sopenharmony_ci	priv->current_key_index = 0;
135262306a36Sopenharmony_ci	priv->media_connected = false;
135362306a36Sopenharmony_ci	memset(priv->mgmt_ie, 0,
135462306a36Sopenharmony_ci	       sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX);
135562306a36Sopenharmony_ci	priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK;
135662306a36Sopenharmony_ci	priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK;
135762306a36Sopenharmony_ci	priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK;
135862306a36Sopenharmony_ci	priv->gen_idx = MWIFIEX_AUTO_IDX_MASK;
135962306a36Sopenharmony_ci	priv->num_tx_timeout = 0;
136062306a36Sopenharmony_ci	if (is_valid_ether_addr(dev->dev_addr))
136162306a36Sopenharmony_ci		ether_addr_copy(priv->curr_addr, dev->dev_addr);
136262306a36Sopenharmony_ci	else
136362306a36Sopenharmony_ci		ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
136662306a36Sopenharmony_ci	    GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
136762306a36Sopenharmony_ci		priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
136862306a36Sopenharmony_ci		if (priv->hist_data)
136962306a36Sopenharmony_ci			mwifiex_hist_data_reset(priv);
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci/*
137462306a36Sopenharmony_ci * This function check if command is pending.
137562306a36Sopenharmony_ci */
137662306a36Sopenharmony_ciint is_command_pending(struct mwifiex_adapter *adapter)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	int is_cmd_pend_q_empty;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	spin_lock_bh(&adapter->cmd_pending_q_lock);
138162306a36Sopenharmony_ci	is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
138262306a36Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_pending_q_lock);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	return !is_cmd_pend_q_empty;
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci/*
138862306a36Sopenharmony_ci * This is the RX work queue function.
138962306a36Sopenharmony_ci *
139062306a36Sopenharmony_ci * It handles the RX operations.
139162306a36Sopenharmony_ci */
139262306a36Sopenharmony_cistatic void mwifiex_rx_work_queue(struct work_struct *work)
139362306a36Sopenharmony_ci{
139462306a36Sopenharmony_ci	struct mwifiex_adapter *adapter =
139562306a36Sopenharmony_ci		container_of(work, struct mwifiex_adapter, rx_work);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags))
139862306a36Sopenharmony_ci		return;
139962306a36Sopenharmony_ci	mwifiex_process_rx(adapter);
140062306a36Sopenharmony_ci}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci/*
140362306a36Sopenharmony_ci * This is the main work queue function.
140462306a36Sopenharmony_ci *
140562306a36Sopenharmony_ci * It handles the main process, which in turn handles the complete
140662306a36Sopenharmony_ci * driver operations.
140762306a36Sopenharmony_ci */
140862306a36Sopenharmony_cistatic void mwifiex_main_work_queue(struct work_struct *work)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct mwifiex_adapter *adapter =
141162306a36Sopenharmony_ci		container_of(work, struct mwifiex_adapter, main_work);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags))
141462306a36Sopenharmony_ci		return;
141562306a36Sopenharmony_ci	mwifiex_main_process(adapter);
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci/* Common teardown code used for both device removal and reset */
141962306a36Sopenharmony_cistatic void mwifiex_uninit_sw(struct mwifiex_adapter *adapter)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	struct mwifiex_private *priv;
142262306a36Sopenharmony_ci	int i;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/* We can no longer handle interrupts once we start doing the teardown
142562306a36Sopenharmony_ci	 * below.
142662306a36Sopenharmony_ci	 */
142762306a36Sopenharmony_ci	if (adapter->if_ops.disable_int)
142862306a36Sopenharmony_ci		adapter->if_ops.disable_int(adapter);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
143162306a36Sopenharmony_ci	mwifiex_terminate_workqueue(adapter);
143262306a36Sopenharmony_ci	adapter->int_status = 0;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	/* Stop data */
143562306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
143662306a36Sopenharmony_ci		priv = adapter->priv[i];
143762306a36Sopenharmony_ci		if (priv && priv->netdev) {
143862306a36Sopenharmony_ci			mwifiex_stop_net_dev_queue(priv->netdev, adapter);
143962306a36Sopenharmony_ci			if (netif_carrier_ok(priv->netdev))
144062306a36Sopenharmony_ci				netif_carrier_off(priv->netdev);
144162306a36Sopenharmony_ci			netif_device_detach(priv->netdev);
144262306a36Sopenharmony_ci		}
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	mwifiex_dbg(adapter, CMD, "cmd: calling mwifiex_shutdown_drv...\n");
144662306a36Sopenharmony_ci	mwifiex_shutdown_drv(adapter);
144762306a36Sopenharmony_ci	mwifiex_dbg(adapter, CMD, "cmd: mwifiex_shutdown_drv done\n");
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (atomic_read(&adapter->rx_pending) ||
145062306a36Sopenharmony_ci	    atomic_read(&adapter->tx_pending) ||
145162306a36Sopenharmony_ci	    atomic_read(&adapter->cmd_pending)) {
145262306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
145362306a36Sopenharmony_ci			    "rx_pending=%d, tx_pending=%d,\t"
145462306a36Sopenharmony_ci			    "cmd_pending=%d\n",
145562306a36Sopenharmony_ci			    atomic_read(&adapter->rx_pending),
145662306a36Sopenharmony_ci			    atomic_read(&adapter->tx_pending),
145762306a36Sopenharmony_ci			    atomic_read(&adapter->cmd_pending));
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
146162306a36Sopenharmony_ci		priv = adapter->priv[i];
146262306a36Sopenharmony_ci		if (!priv)
146362306a36Sopenharmony_ci			continue;
146462306a36Sopenharmony_ci		rtnl_lock();
146562306a36Sopenharmony_ci		if (priv->netdev &&
146662306a36Sopenharmony_ci		    priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) {
146762306a36Sopenharmony_ci			/*
146862306a36Sopenharmony_ci			 * Close the netdev now, because if we do it later, the
146962306a36Sopenharmony_ci			 * netdev notifiers will need to acquire the wiphy lock
147062306a36Sopenharmony_ci			 * again --> deadlock.
147162306a36Sopenharmony_ci			 */
147262306a36Sopenharmony_ci			dev_close(priv->wdev.netdev);
147362306a36Sopenharmony_ci			wiphy_lock(adapter->wiphy);
147462306a36Sopenharmony_ci			mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
147562306a36Sopenharmony_ci			wiphy_unlock(adapter->wiphy);
147662306a36Sopenharmony_ci		}
147762306a36Sopenharmony_ci		rtnl_unlock();
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	wiphy_unregister(adapter->wiphy);
148162306a36Sopenharmony_ci	wiphy_free(adapter->wiphy);
148262306a36Sopenharmony_ci	adapter->wiphy = NULL;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	vfree(adapter->chan_stats);
148562306a36Sopenharmony_ci	mwifiex_free_cmd_buffers(adapter);
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci/*
148962306a36Sopenharmony_ci * This function can be used for shutting down the adapter SW.
149062306a36Sopenharmony_ci */
149162306a36Sopenharmony_ciint mwifiex_shutdown_sw(struct mwifiex_adapter *adapter)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	struct mwifiex_private *priv;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	if (!adapter)
149662306a36Sopenharmony_ci		return 0;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	wait_for_completion(adapter->fw_done);
149962306a36Sopenharmony_ci	/* Caller should ensure we aren't suspending while this happens */
150062306a36Sopenharmony_ci	reinit_completion(adapter->fw_done);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
150362306a36Sopenharmony_ci	mwifiex_deauthenticate(priv, NULL);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	mwifiex_uninit_sw(adapter);
150862306a36Sopenharmony_ci	adapter->is_up = false;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	if (adapter->if_ops.down_dev)
151162306a36Sopenharmony_ci		adapter->if_ops.down_dev(adapter);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	return 0;
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_shutdown_sw);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci/* This function can be used for reinitting the adapter SW. Required
151862306a36Sopenharmony_ci * code is extracted from mwifiex_add_card()
151962306a36Sopenharmony_ci */
152062306a36Sopenharmony_ciint
152162306a36Sopenharmony_cimwifiex_reinit_sw(struct mwifiex_adapter *adapter)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	int ret;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	mwifiex_init_lock_list(adapter);
152662306a36Sopenharmony_ci	if (adapter->if_ops.up_dev)
152762306a36Sopenharmony_ci		adapter->if_ops.up_dev(adapter);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
153062306a36Sopenharmony_ci	clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
153162306a36Sopenharmony_ci	init_waitqueue_head(&adapter->init_wait_q);
153262306a36Sopenharmony_ci	clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
153362306a36Sopenharmony_ci	adapter->hs_activated = false;
153462306a36Sopenharmony_ci	clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);
153562306a36Sopenharmony_ci	init_waitqueue_head(&adapter->hs_activate_wait_q);
153662306a36Sopenharmony_ci	init_waitqueue_head(&adapter->cmd_wait_q.wait);
153762306a36Sopenharmony_ci	adapter->cmd_wait_q.status = 0;
153862306a36Sopenharmony_ci	adapter->scan_wait_q_woken = false;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB)
154162306a36Sopenharmony_ci		adapter->rx_work_enabled = true;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	adapter->workqueue =
154462306a36Sopenharmony_ci		alloc_workqueue("MWIFIEX_WORK_QUEUE",
154562306a36Sopenharmony_ci				WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
154662306a36Sopenharmony_ci	if (!adapter->workqueue)
154762306a36Sopenharmony_ci		goto err_kmalloc;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (adapter->rx_work_enabled) {
155262306a36Sopenharmony_ci		adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
155362306a36Sopenharmony_ci							WQ_HIGHPRI |
155462306a36Sopenharmony_ci							WQ_MEM_RECLAIM |
155562306a36Sopenharmony_ci							WQ_UNBOUND, 0);
155662306a36Sopenharmony_ci		if (!adapter->rx_workqueue)
155762306a36Sopenharmony_ci			goto err_kmalloc;
155862306a36Sopenharmony_ci		INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	/* Register the device. Fill up the private data structure with
156262306a36Sopenharmony_ci	 * relevant information from the card. Some code extracted from
156362306a36Sopenharmony_ci	 * mwifiex_register_dev()
156462306a36Sopenharmony_ci	 */
156562306a36Sopenharmony_ci	mwifiex_dbg(adapter, INFO, "%s, mwifiex_init_hw_fw()...\n", __func__);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	if (mwifiex_init_hw_fw(adapter, false)) {
156862306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
156962306a36Sopenharmony_ci			    "%s: firmware init failed\n", __func__);
157062306a36Sopenharmony_ci		goto err_init_fw;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	/* _mwifiex_fw_dpc() does its own cleanup */
157462306a36Sopenharmony_ci	ret = _mwifiex_fw_dpc(adapter->firmware, adapter);
157562306a36Sopenharmony_ci	if (ret) {
157662306a36Sopenharmony_ci		pr_err("Failed to bring up adapter: %d\n", ret);
157762306a36Sopenharmony_ci		return ret;
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci	mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	return 0;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_cierr_init_fw:
158462306a36Sopenharmony_ci	mwifiex_dbg(adapter, ERROR, "info: %s: unregister device\n", __func__);
158562306a36Sopenharmony_ci	if (adapter->if_ops.unregister_dev)
158662306a36Sopenharmony_ci		adapter->if_ops.unregister_dev(adapter);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cierr_kmalloc:
158962306a36Sopenharmony_ci	set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
159062306a36Sopenharmony_ci	mwifiex_terminate_workqueue(adapter);
159162306a36Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
159262306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
159362306a36Sopenharmony_ci			    "info: %s: shutdown mwifiex\n", __func__);
159462306a36Sopenharmony_ci		mwifiex_shutdown_drv(adapter);
159562306a36Sopenharmony_ci		mwifiex_free_cmd_buffers(adapter);
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	complete_all(adapter->fw_done);
159962306a36Sopenharmony_ci	mwifiex_dbg(adapter, INFO, "%s, error\n", __func__);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	return -1;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_reinit_sw);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic irqreturn_t mwifiex_irq_wakeup_handler(int irq, void *priv)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct mwifiex_adapter *adapter = priv;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	dev_dbg(adapter->dev, "%s: wake by wifi", __func__);
161062306a36Sopenharmony_ci	adapter->wake_by_wifi = true;
161162306a36Sopenharmony_ci	disable_irq_nosync(irq);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	/* Notify PM core we are wakeup source */
161462306a36Sopenharmony_ci	pm_wakeup_event(adapter->dev, 0);
161562306a36Sopenharmony_ci	pm_system_wakeup();
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	return IRQ_HANDLED;
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic void mwifiex_probe_of(struct mwifiex_adapter *adapter)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	int ret;
162362306a36Sopenharmony_ci	struct device *dev = adapter->dev;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (!dev->of_node)
162662306a36Sopenharmony_ci		goto err_exit;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	adapter->dt_node = dev->of_node;
162962306a36Sopenharmony_ci	adapter->irq_wakeup = irq_of_parse_and_map(adapter->dt_node, 0);
163062306a36Sopenharmony_ci	if (!adapter->irq_wakeup) {
163162306a36Sopenharmony_ci		dev_dbg(dev, "fail to parse irq_wakeup from device tree\n");
163262306a36Sopenharmony_ci		goto err_exit;
163362306a36Sopenharmony_ci	}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	ret = devm_request_irq(dev, adapter->irq_wakeup,
163662306a36Sopenharmony_ci			       mwifiex_irq_wakeup_handler, IRQF_TRIGGER_LOW,
163762306a36Sopenharmony_ci			       "wifi_wake", adapter);
163862306a36Sopenharmony_ci	if (ret) {
163962306a36Sopenharmony_ci		dev_err(dev, "Failed to request irq_wakeup %d (%d)\n",
164062306a36Sopenharmony_ci			adapter->irq_wakeup, ret);
164162306a36Sopenharmony_ci		goto err_exit;
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	disable_irq(adapter->irq_wakeup);
164562306a36Sopenharmony_ci	if (device_init_wakeup(dev, true)) {
164662306a36Sopenharmony_ci		dev_err(dev, "fail to init wakeup for mwifiex\n");
164762306a36Sopenharmony_ci		goto err_exit;
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci	return;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_cierr_exit:
165262306a36Sopenharmony_ci	adapter->irq_wakeup = -1;
165362306a36Sopenharmony_ci}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci/*
165662306a36Sopenharmony_ci * This function adds the card.
165762306a36Sopenharmony_ci *
165862306a36Sopenharmony_ci * This function follows the following major steps to set up the device -
165962306a36Sopenharmony_ci *      - Initialize software. This includes probing the card, registering
166062306a36Sopenharmony_ci *        the interface operations table, and allocating/initializing the
166162306a36Sopenharmony_ci *        adapter structure
166262306a36Sopenharmony_ci *      - Set up the netlink socket
166362306a36Sopenharmony_ci *      - Create and start the main work queue
166462306a36Sopenharmony_ci *      - Register the device
166562306a36Sopenharmony_ci *      - Initialize firmware and hardware
166662306a36Sopenharmony_ci *      - Add logical interfaces
166762306a36Sopenharmony_ci */
166862306a36Sopenharmony_ciint
166962306a36Sopenharmony_cimwifiex_add_card(void *card, struct completion *fw_done,
167062306a36Sopenharmony_ci		 struct mwifiex_if_ops *if_ops, u8 iface_type,
167162306a36Sopenharmony_ci		 struct device *dev)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	struct mwifiex_adapter *adapter;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (mwifiex_register(card, dev, if_ops, (void **)&adapter)) {
167662306a36Sopenharmony_ci		pr_err("%s: software init failed\n", __func__);
167762306a36Sopenharmony_ci		goto err_init_sw;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	mwifiex_probe_of(adapter);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	adapter->iface_type = iface_type;
168362306a36Sopenharmony_ci	adapter->fw_done = fw_done;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
168662306a36Sopenharmony_ci	clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
168762306a36Sopenharmony_ci	init_waitqueue_head(&adapter->init_wait_q);
168862306a36Sopenharmony_ci	clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
168962306a36Sopenharmony_ci	adapter->hs_activated = false;
169062306a36Sopenharmony_ci	init_waitqueue_head(&adapter->hs_activate_wait_q);
169162306a36Sopenharmony_ci	init_waitqueue_head(&adapter->cmd_wait_q.wait);
169262306a36Sopenharmony_ci	adapter->cmd_wait_q.status = 0;
169362306a36Sopenharmony_ci	adapter->scan_wait_q_woken = false;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB)
169662306a36Sopenharmony_ci		adapter->rx_work_enabled = true;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	adapter->workqueue =
169962306a36Sopenharmony_ci		alloc_workqueue("MWIFIEX_WORK_QUEUE",
170062306a36Sopenharmony_ci				WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
170162306a36Sopenharmony_ci	if (!adapter->workqueue)
170262306a36Sopenharmony_ci		goto err_kmalloc;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	if (adapter->rx_work_enabled) {
170762306a36Sopenharmony_ci		adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE",
170862306a36Sopenharmony_ci							WQ_HIGHPRI |
170962306a36Sopenharmony_ci							WQ_MEM_RECLAIM |
171062306a36Sopenharmony_ci							WQ_UNBOUND, 0);
171162306a36Sopenharmony_ci		if (!adapter->rx_workqueue)
171262306a36Sopenharmony_ci			goto err_kmalloc;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
171562306a36Sopenharmony_ci	}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	/* Register the device. Fill up the private data structure with relevant
171862306a36Sopenharmony_ci	   information from the card. */
171962306a36Sopenharmony_ci	if (adapter->if_ops.register_dev(adapter)) {
172062306a36Sopenharmony_ci		pr_err("%s: failed to register mwifiex device\n", __func__);
172162306a36Sopenharmony_ci		goto err_registerdev;
172262306a36Sopenharmony_ci	}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	if (mwifiex_init_hw_fw(adapter, true)) {
172562306a36Sopenharmony_ci		pr_err("%s: firmware init failed\n", __func__);
172662306a36Sopenharmony_ci		goto err_init_fw;
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	return 0;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_cierr_init_fw:
173262306a36Sopenharmony_ci	pr_debug("info: %s: unregister device\n", __func__);
173362306a36Sopenharmony_ci	if (adapter->if_ops.unregister_dev)
173462306a36Sopenharmony_ci		adapter->if_ops.unregister_dev(adapter);
173562306a36Sopenharmony_cierr_registerdev:
173662306a36Sopenharmony_ci	set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
173762306a36Sopenharmony_ci	mwifiex_terminate_workqueue(adapter);
173862306a36Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) {
173962306a36Sopenharmony_ci		pr_debug("info: %s: shutdown mwifiex\n", __func__);
174062306a36Sopenharmony_ci		mwifiex_shutdown_drv(adapter);
174162306a36Sopenharmony_ci		mwifiex_free_cmd_buffers(adapter);
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_cierr_kmalloc:
174462306a36Sopenharmony_ci	if (adapter->irq_wakeup >= 0)
174562306a36Sopenharmony_ci		device_init_wakeup(adapter->dev, false);
174662306a36Sopenharmony_ci	mwifiex_free_adapter(adapter);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_cierr_init_sw:
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	return -1;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_add_card);
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci/*
175562306a36Sopenharmony_ci * This function removes the card.
175662306a36Sopenharmony_ci *
175762306a36Sopenharmony_ci * This function follows the following major steps to remove the device -
175862306a36Sopenharmony_ci *      - Stop data traffic
175962306a36Sopenharmony_ci *      - Shutdown firmware
176062306a36Sopenharmony_ci *      - Remove the logical interfaces
176162306a36Sopenharmony_ci *      - Terminate the work queue
176262306a36Sopenharmony_ci *      - Unregister the device
176362306a36Sopenharmony_ci *      - Free the adapter structure
176462306a36Sopenharmony_ci */
176562306a36Sopenharmony_ciint mwifiex_remove_card(struct mwifiex_adapter *adapter)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	if (!adapter)
176862306a36Sopenharmony_ci		return 0;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	if (adapter->is_up)
177162306a36Sopenharmony_ci		mwifiex_uninit_sw(adapter);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	if (adapter->irq_wakeup >= 0)
177462306a36Sopenharmony_ci		device_init_wakeup(adapter->dev, false);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	/* Unregister device */
177762306a36Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
177862306a36Sopenharmony_ci		    "info: unregister device\n");
177962306a36Sopenharmony_ci	if (adapter->if_ops.unregister_dev)
178062306a36Sopenharmony_ci		adapter->if_ops.unregister_dev(adapter);
178162306a36Sopenharmony_ci	/* Free adapter structure */
178262306a36Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
178362306a36Sopenharmony_ci		    "info: free adapter\n");
178462306a36Sopenharmony_ci	mwifiex_free_adapter(adapter);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	return 0;
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_remove_card);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_civoid _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask,
179162306a36Sopenharmony_ci		  const char *fmt, ...)
179262306a36Sopenharmony_ci{
179362306a36Sopenharmony_ci	struct va_format vaf;
179462306a36Sopenharmony_ci	va_list args;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	if (!(adapter->debug_mask & mask))
179762306a36Sopenharmony_ci		return;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	va_start(args, fmt);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	vaf.fmt = fmt;
180262306a36Sopenharmony_ci	vaf.va = &args;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (adapter->dev)
180562306a36Sopenharmony_ci		dev_info(adapter->dev, "%pV", &vaf);
180662306a36Sopenharmony_ci	else
180762306a36Sopenharmony_ci		pr_info("%pV", &vaf);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	va_end(args);
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(_mwifiex_dbg);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci/*
181462306a36Sopenharmony_ci * This function initializes the module.
181562306a36Sopenharmony_ci *
181662306a36Sopenharmony_ci * The debug FS is also initialized if configured.
181762306a36Sopenharmony_ci */
181862306a36Sopenharmony_cistatic int
181962306a36Sopenharmony_cimwifiex_init_module(void)
182062306a36Sopenharmony_ci{
182162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
182262306a36Sopenharmony_ci	mwifiex_debugfs_init();
182362306a36Sopenharmony_ci#endif
182462306a36Sopenharmony_ci	return 0;
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci/*
182862306a36Sopenharmony_ci * This function cleans up the module.
182962306a36Sopenharmony_ci *
183062306a36Sopenharmony_ci * The debug FS is removed if available.
183162306a36Sopenharmony_ci */
183262306a36Sopenharmony_cistatic void
183362306a36Sopenharmony_cimwifiex_cleanup_module(void)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
183662306a36Sopenharmony_ci	mwifiex_debugfs_remove();
183762306a36Sopenharmony_ci#endif
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cimodule_init(mwifiex_init_module);
184162306a36Sopenharmony_cimodule_exit(mwifiex_cleanup_module);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd.");
184462306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION);
184562306a36Sopenharmony_ciMODULE_VERSION(VERSION);
184662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1847