162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NXP Wireless LAN device driver: HW/FW Initialization
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011-2020 NXP
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "decl.h"
962306a36Sopenharmony_ci#include "ioctl.h"
1062306a36Sopenharmony_ci#include "util.h"
1162306a36Sopenharmony_ci#include "fw.h"
1262306a36Sopenharmony_ci#include "main.h"
1362306a36Sopenharmony_ci#include "wmm.h"
1462306a36Sopenharmony_ci#include "11n.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * This function adds a BSS priority table to the table list.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * The function allocates a new BSS priority table node and adds it to
2062306a36Sopenharmony_ci * the end of BSS priority table list, kept in driver memory.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
2562306a36Sopenharmony_ci	struct mwifiex_bss_prio_node *bss_prio;
2662306a36Sopenharmony_ci	struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL);
2962306a36Sopenharmony_ci	if (!bss_prio)
3062306a36Sopenharmony_ci		return -ENOMEM;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	bss_prio->priv = priv;
3362306a36Sopenharmony_ci	INIT_LIST_HEAD(&bss_prio->list);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock);
3662306a36Sopenharmony_ci	list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head);
3762306a36Sopenharmony_ci	spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void wakeup_timer_fn(struct timer_list *t)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct mwifiex_adapter *adapter = from_timer(adapter, t, wakeup_timer);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n");
4762306a36Sopenharmony_ci	adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
4862306a36Sopenharmony_ci	mwifiex_cancel_all_pending_cmd(adapter);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (adapter->if_ops.card_reset)
5162306a36Sopenharmony_ci		adapter->if_ops.card_reset(adapter);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void fw_dump_work(struct work_struct *work)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct mwifiex_adapter *adapter =
5762306a36Sopenharmony_ci		container_of(work, struct mwifiex_adapter, devdump_work.work);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	mwifiex_upload_device_dump(adapter);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * This function initializes the private structure and sets default
6462306a36Sopenharmony_ci * values to the members.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * Additionally, it also initializes all the locks and sets up all the
6762306a36Sopenharmony_ci * lists.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ciint mwifiex_init_priv(struct mwifiex_private *priv)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	u32 i;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	priv->media_connected = false;
7462306a36Sopenharmony_ci	eth_broadcast_addr(priv->curr_addr);
7562306a36Sopenharmony_ci	priv->port_open = false;
7662306a36Sopenharmony_ci	priv->usb_port = MWIFIEX_USB_EP_DATA;
7762306a36Sopenharmony_ci	priv->pkt_tx_ctrl = 0;
7862306a36Sopenharmony_ci	priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
7962306a36Sopenharmony_ci	priv->data_rate = 0;	/* Initially indicate the rate as auto */
8062306a36Sopenharmony_ci	priv->is_data_rate_auto = true;
8162306a36Sopenharmony_ci	priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
8262306a36Sopenharmony_ci	priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	priv->sec_info.wep_enabled = 0;
8562306a36Sopenharmony_ci	priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
8662306a36Sopenharmony_ci	priv->sec_info.encryption_mode = 0;
8762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
8862306a36Sopenharmony_ci		memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key));
8962306a36Sopenharmony_ci	priv->wep_key_curr_index = 0;
9062306a36Sopenharmony_ci	priv->curr_pkt_filter = HostCmd_ACT_MAC_DYNAMIC_BW_ENABLE |
9162306a36Sopenharmony_ci				HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON |
9262306a36Sopenharmony_ci				HostCmd_ACT_MAC_ETHERNETII_ENABLE;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	priv->beacon_period = 100; /* beacon interval */
9562306a36Sopenharmony_ci	priv->attempted_bss_desc = NULL;
9662306a36Sopenharmony_ci	memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
9762306a36Sopenharmony_ci	priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid));
10062306a36Sopenharmony_ci	memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid));
10162306a36Sopenharmony_ci	memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf));
10262306a36Sopenharmony_ci	priv->assoc_rsp_size = 0;
10362306a36Sopenharmony_ci	priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
10462306a36Sopenharmony_ci	priv->atim_window = 0;
10562306a36Sopenharmony_ci	priv->adhoc_state = ADHOC_IDLE;
10662306a36Sopenharmony_ci	priv->tx_power_level = 0;
10762306a36Sopenharmony_ci	priv->max_tx_power_level = 0;
10862306a36Sopenharmony_ci	priv->min_tx_power_level = 0;
10962306a36Sopenharmony_ci	priv->tx_ant = 0;
11062306a36Sopenharmony_ci	priv->rx_ant = 0;
11162306a36Sopenharmony_ci	priv->tx_rate = 0;
11262306a36Sopenharmony_ci	priv->rxpd_htinfo = 0;
11362306a36Sopenharmony_ci	priv->rxpd_rate = 0;
11462306a36Sopenharmony_ci	priv->rate_bitmap = 0;
11562306a36Sopenharmony_ci	priv->data_rssi_last = 0;
11662306a36Sopenharmony_ci	priv->data_rssi_avg = 0;
11762306a36Sopenharmony_ci	priv->data_nf_avg = 0;
11862306a36Sopenharmony_ci	priv->data_nf_last = 0;
11962306a36Sopenharmony_ci	priv->bcn_rssi_last = 0;
12062306a36Sopenharmony_ci	priv->bcn_rssi_avg = 0;
12162306a36Sopenharmony_ci	priv->bcn_nf_avg = 0;
12262306a36Sopenharmony_ci	priv->bcn_nf_last = 0;
12362306a36Sopenharmony_ci	memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie));
12462306a36Sopenharmony_ci	memset(&priv->aes_key, 0, sizeof(priv->aes_key));
12562306a36Sopenharmony_ci	priv->wpa_ie_len = 0;
12662306a36Sopenharmony_ci	priv->wpa_is_gtk_set = false;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf));
12962306a36Sopenharmony_ci	priv->assoc_tlv_buf_len = 0;
13062306a36Sopenharmony_ci	memset(&priv->wps, 0, sizeof(priv->wps));
13162306a36Sopenharmony_ci	memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf));
13262306a36Sopenharmony_ci	priv->gen_ie_buf_len = 0;
13362306a36Sopenharmony_ci	memset(priv->vs_ie, 0, sizeof(priv->vs_ie));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	priv->wmm_required = true;
13662306a36Sopenharmony_ci	priv->wmm_enabled = false;
13762306a36Sopenharmony_ci	priv->wmm_qosinfo = 0;
13862306a36Sopenharmony_ci	priv->curr_bcn_buf = NULL;
13962306a36Sopenharmony_ci	priv->curr_bcn_size = 0;
14062306a36Sopenharmony_ci	priv->wps_ie = NULL;
14162306a36Sopenharmony_ci	priv->wps_ie_len = 0;
14262306a36Sopenharmony_ci	priv->ap_11n_enabled = 0;
14362306a36Sopenharmony_ci	memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg));
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	priv->scan_block = false;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	priv->csa_chan = 0;
14862306a36Sopenharmony_ci	priv->csa_expire_time = 0;
14962306a36Sopenharmony_ci	priv->del_list_idx = 0;
15062306a36Sopenharmony_ci	priv->hs2_enabled = false;
15162306a36Sopenharmony_ci	priv->check_tdls_tx = false;
15262306a36Sopenharmony_ci	memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	mwifiex_init_11h_params(priv);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return mwifiex_add_bss_prio_tbl(priv);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/*
16062306a36Sopenharmony_ci * This function allocates buffers for members of the adapter
16162306a36Sopenharmony_ci * structure.
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * The memory allocated includes scan table, command buffers, and
16462306a36Sopenharmony_ci * sleep confirm command buffer. In addition, the queues are
16562306a36Sopenharmony_ci * also initialized.
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* Allocate command buffer */
17262306a36Sopenharmony_ci	ret = mwifiex_alloc_cmd_buffer(adapter);
17362306a36Sopenharmony_ci	if (ret) {
17462306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
17562306a36Sopenharmony_ci			    "%s: failed to alloc cmd buffer\n",
17662306a36Sopenharmony_ci			    __func__);
17762306a36Sopenharmony_ci		return -1;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	adapter->sleep_cfm =
18162306a36Sopenharmony_ci		dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
18262306a36Sopenharmony_ci			      + INTF_HEADER_LEN);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!adapter->sleep_cfm) {
18562306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
18662306a36Sopenharmony_ci			    "%s: failed to alloc sleep cfm\t"
18762306a36Sopenharmony_ci			    " cmd buffer\n", __func__);
18862306a36Sopenharmony_ci		return -1;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/*
19662306a36Sopenharmony_ci * This function initializes the adapter structure and sets default
19762306a36Sopenharmony_ci * values to the members of adapter.
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci * This also initializes the WMM related parameters in the driver private
20062306a36Sopenharmony_ci * structures.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm));
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	adapter->cmd_sent = false;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_SDIO)
21162306a36Sopenharmony_ci		adapter->data_sent = true;
21262306a36Sopenharmony_ci	else
21362306a36Sopenharmony_ci		adapter->data_sent = false;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_USB)
21662306a36Sopenharmony_ci		adapter->intf_hdr_len = 0;
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		adapter->intf_hdr_len = INTF_HEADER_LEN;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	adapter->cmd_resp_received = false;
22162306a36Sopenharmony_ci	adapter->event_received = false;
22262306a36Sopenharmony_ci	adapter->data_received = false;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
22962306a36Sopenharmony_ci	adapter->ps_state = PS_STATE_AWAKE;
23062306a36Sopenharmony_ci	adapter->need_to_wakeup = false;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	adapter->scan_mode = HostCmd_BSS_MODE_ANY;
23362306a36Sopenharmony_ci	adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME;
23462306a36Sopenharmony_ci	adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME;
23562306a36Sopenharmony_ci	adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME;
23662306a36Sopenharmony_ci	adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	adapter->scan_probes = 1;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	adapter->multiple_dtim = 1;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	adapter->local_listen_interval = 0;	/* default value in firmware
24362306a36Sopenharmony_ci						   will be used */
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	adapter->is_deep_sleep = false;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	adapter->delay_null_pkt = false;
24862306a36Sopenharmony_ci	adapter->delay_to_ps = 1000;
24962306a36Sopenharmony_ci	adapter->enhanced_ps_mode = PS_MODE_AUTO;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	adapter->gen_null_pkt = false;	/* Disable NULL Pkg generation by
25262306a36Sopenharmony_ci					   default */
25362306a36Sopenharmony_ci	adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by
25462306a36Sopenharmony_ci					   default */
25562306a36Sopenharmony_ci	adapter->pm_wakeup_card_req = false;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	adapter->pm_wakeup_fw_try = false;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
26262306a36Sopenharmony_ci	adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF);
26362306a36Sopenharmony_ci	adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF;
26462306a36Sopenharmony_ci	adapter->hs_cfg.gap = HS_CFG_GAP_DEF;
26562306a36Sopenharmony_ci	adapter->hs_activated = false;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	memset(adapter->event_body, 0, sizeof(adapter->event_body));
26862306a36Sopenharmony_ci	adapter->hw_dot_11n_dev_cap = 0;
26962306a36Sopenharmony_ci	adapter->hw_dev_mcs_support = 0;
27062306a36Sopenharmony_ci	adapter->sec_chan_offset = 0;
27162306a36Sopenharmony_ci	adapter->adhoc_11n_enabled = false;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	mwifiex_wmm_init(adapter);
27462306a36Sopenharmony_ci	atomic_set(&adapter->tx_hw_pending, 0);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
27762306a36Sopenharmony_ci					adapter->sleep_cfm->data;
27862306a36Sopenharmony_ci	memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
27962306a36Sopenharmony_ci	sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
28062306a36Sopenharmony_ci	sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
28162306a36Sopenharmony_ci	sleep_cfm_buf->result = 0;
28262306a36Sopenharmony_ci	sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
28362306a36Sopenharmony_ci	sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
28662306a36Sopenharmony_ci	adapter->tx_lock_flag = false;
28762306a36Sopenharmony_ci	adapter->null_pkt_interval = 0;
28862306a36Sopenharmony_ci	adapter->fw_bands = 0;
28962306a36Sopenharmony_ci	adapter->config_bands = 0;
29062306a36Sopenharmony_ci	adapter->adhoc_start_band = 0;
29162306a36Sopenharmony_ci	adapter->fw_release_number = 0;
29262306a36Sopenharmony_ci	adapter->fw_cap_info = 0;
29362306a36Sopenharmony_ci	memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf));
29462306a36Sopenharmony_ci	adapter->event_cause = 0;
29562306a36Sopenharmony_ci	adapter->region_code = 0;
29662306a36Sopenharmony_ci	adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT;
29762306a36Sopenharmony_ci	adapter->adhoc_awake_period = 0;
29862306a36Sopenharmony_ci	memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
29962306a36Sopenharmony_ci	adapter->arp_filter_size = 0;
30062306a36Sopenharmony_ci	adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
30162306a36Sopenharmony_ci	adapter->mfg_mode = mfg_mode;
30262306a36Sopenharmony_ci	adapter->key_api_major_ver = 0;
30362306a36Sopenharmony_ci	adapter->key_api_minor_ver = 0;
30462306a36Sopenharmony_ci	eth_broadcast_addr(adapter->perm_addr);
30562306a36Sopenharmony_ci	adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM;
30662306a36Sopenharmony_ci	adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM;
30762306a36Sopenharmony_ci	adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM;
30862306a36Sopenharmony_ci	adapter->active_scan_triggered = false;
30962306a36Sopenharmony_ci	timer_setup(&adapter->wakeup_timer, wakeup_timer_fn, 0);
31062306a36Sopenharmony_ci	adapter->devdump_len = 0;
31162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&adapter->devdump_work, fw_dump_work);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/*
31562306a36Sopenharmony_ci * This function sets trans_start per tx_queue
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_civoid mwifiex_set_trans_start(struct net_device *dev)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	int i;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	for (i = 0; i < dev->num_tx_queues; i++)
32262306a36Sopenharmony_ci		txq_trans_cond_update(netdev_get_tx_queue(dev, i));
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	netif_trans_update(dev);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/*
32862306a36Sopenharmony_ci * This function wakes up all queues in net_device
32962306a36Sopenharmony_ci */
33062306a36Sopenharmony_civoid mwifiex_wake_up_net_dev_queue(struct net_device *netdev,
33162306a36Sopenharmony_ci					struct mwifiex_adapter *adapter)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	spin_lock_bh(&adapter->queue_lock);
33462306a36Sopenharmony_ci	netif_tx_wake_all_queues(netdev);
33562306a36Sopenharmony_ci	spin_unlock_bh(&adapter->queue_lock);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci/*
33962306a36Sopenharmony_ci * This function stops all queues in net_device
34062306a36Sopenharmony_ci */
34162306a36Sopenharmony_civoid mwifiex_stop_net_dev_queue(struct net_device *netdev,
34262306a36Sopenharmony_ci					struct mwifiex_adapter *adapter)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	spin_lock_bh(&adapter->queue_lock);
34562306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
34662306a36Sopenharmony_ci	spin_unlock_bh(&adapter->queue_lock);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/*
35062306a36Sopenharmony_ci * This function invalidates the list heads.
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic void mwifiex_invalidate_lists(struct mwifiex_adapter *adapter)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct mwifiex_private *priv;
35562306a36Sopenharmony_ci	s32 i, j;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	list_del(&adapter->cmd_free_q);
35862306a36Sopenharmony_ci	list_del(&adapter->cmd_pending_q);
35962306a36Sopenharmony_ci	list_del(&adapter->scan_pending_q);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++)
36262306a36Sopenharmony_ci		list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
36562306a36Sopenharmony_ci		if (adapter->priv[i]) {
36662306a36Sopenharmony_ci			priv = adapter->priv[i];
36762306a36Sopenharmony_ci			for (j = 0; j < MAX_NUM_TID; ++j)
36862306a36Sopenharmony_ci				list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
36962306a36Sopenharmony_ci			list_del(&priv->tx_ba_stream_tbl_ptr);
37062306a36Sopenharmony_ci			list_del(&priv->rx_reorder_tbl_ptr);
37162306a36Sopenharmony_ci			list_del(&priv->sta_list);
37262306a36Sopenharmony_ci			list_del(&priv->auto_tdls_list);
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/*
37862306a36Sopenharmony_ci * This function performs cleanup for adapter structure.
37962306a36Sopenharmony_ci *
38062306a36Sopenharmony_ci * The cleanup is done recursively, by canceling all pending
38162306a36Sopenharmony_ci * commands, freeing the member buffers previously allocated
38262306a36Sopenharmony_ci * (command buffers, scan table buffer, sleep confirm command
38362306a36Sopenharmony_ci * buffer), stopping the timers and calling the cleanup routines
38462306a36Sopenharmony_ci * for every interface.
38562306a36Sopenharmony_ci */
38662306a36Sopenharmony_cistatic void
38762306a36Sopenharmony_cimwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	del_timer(&adapter->wakeup_timer);
39062306a36Sopenharmony_ci	cancel_delayed_work_sync(&adapter->devdump_work);
39162306a36Sopenharmony_ci	mwifiex_cancel_all_pending_cmd(adapter);
39262306a36Sopenharmony_ci	wake_up_interruptible(&adapter->cmd_wait_q.wait);
39362306a36Sopenharmony_ci	wake_up_interruptible(&adapter->hs_activate_wait_q);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_civoid mwifiex_free_cmd_buffers(struct mwifiex_adapter *adapter)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	mwifiex_invalidate_lists(adapter);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* Free command buffer */
40162306a36Sopenharmony_ci	mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n");
40262306a36Sopenharmony_ci	mwifiex_free_cmd_buffer(adapter);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (adapter->sleep_cfm)
40562306a36Sopenharmony_ci		dev_kfree_skb_any(adapter->sleep_cfm);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/*
40962306a36Sopenharmony_ci *  This function intializes the lock variables and
41062306a36Sopenharmony_ci *  the list heads.
41162306a36Sopenharmony_ci */
41262306a36Sopenharmony_ciint mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct mwifiex_private *priv;
41562306a36Sopenharmony_ci	s32 i, j;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	spin_lock_init(&adapter->int_lock);
41862306a36Sopenharmony_ci	spin_lock_init(&adapter->main_proc_lock);
41962306a36Sopenharmony_ci	spin_lock_init(&adapter->mwifiex_cmd_lock);
42062306a36Sopenharmony_ci	spin_lock_init(&adapter->queue_lock);
42162306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
42262306a36Sopenharmony_ci		if (adapter->priv[i]) {
42362306a36Sopenharmony_ci			priv = adapter->priv[i];
42462306a36Sopenharmony_ci			spin_lock_init(&priv->wmm.ra_list_spinlock);
42562306a36Sopenharmony_ci			spin_lock_init(&priv->curr_bcn_buf_lock);
42662306a36Sopenharmony_ci			spin_lock_init(&priv->sta_list_spinlock);
42762306a36Sopenharmony_ci			spin_lock_init(&priv->auto_tdls_lock);
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Initialize cmd_free_q */
43262306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->cmd_free_q);
43362306a36Sopenharmony_ci	/* Initialize cmd_pending_q */
43462306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->cmd_pending_q);
43562306a36Sopenharmony_ci	/* Initialize scan_pending_q */
43662306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->scan_pending_q);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	spin_lock_init(&adapter->cmd_free_q_lock);
43962306a36Sopenharmony_ci	spin_lock_init(&adapter->cmd_pending_q_lock);
44062306a36Sopenharmony_ci	spin_lock_init(&adapter->scan_pending_q_lock);
44162306a36Sopenharmony_ci	spin_lock_init(&adapter->rx_proc_lock);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	skb_queue_head_init(&adapter->rx_data_q);
44462306a36Sopenharmony_ci	skb_queue_head_init(&adapter->tx_data_q);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; ++i) {
44762306a36Sopenharmony_ci		INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
44862306a36Sopenharmony_ci		spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
45262306a36Sopenharmony_ci		if (!adapter->priv[i])
45362306a36Sopenharmony_ci			continue;
45462306a36Sopenharmony_ci		priv = adapter->priv[i];
45562306a36Sopenharmony_ci		for (j = 0; j < MAX_NUM_TID; ++j)
45662306a36Sopenharmony_ci			INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
45762306a36Sopenharmony_ci		INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
45862306a36Sopenharmony_ci		INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
45962306a36Sopenharmony_ci		INIT_LIST_HEAD(&priv->sta_list);
46062306a36Sopenharmony_ci		INIT_LIST_HEAD(&priv->auto_tdls_list);
46162306a36Sopenharmony_ci		skb_queue_head_init(&priv->tdls_txq);
46262306a36Sopenharmony_ci		skb_queue_head_init(&priv->bypass_txq);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		spin_lock_init(&priv->tx_ba_stream_tbl_lock);
46562306a36Sopenharmony_ci		spin_lock_init(&priv->rx_reorder_tbl_lock);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		spin_lock_init(&priv->ack_status_lock);
46862306a36Sopenharmony_ci		idr_init(&priv->ack_status_frames);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/*
47562306a36Sopenharmony_ci * This function initializes the firmware.
47662306a36Sopenharmony_ci *
47762306a36Sopenharmony_ci * The following operations are performed sequentially -
47862306a36Sopenharmony_ci *      - Allocate adapter structure
47962306a36Sopenharmony_ci *      - Initialize the adapter structure
48062306a36Sopenharmony_ci *      - Initialize the private structure
48162306a36Sopenharmony_ci *      - Add BSS priority tables to the adapter structure
48262306a36Sopenharmony_ci *      - For each interface, send the init commands to firmware
48362306a36Sopenharmony_ci *      - Send the first command in command pending queue, if available
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_ciint mwifiex_init_fw(struct mwifiex_adapter *adapter)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	int ret;
48862306a36Sopenharmony_ci	struct mwifiex_private *priv;
48962306a36Sopenharmony_ci	u8 i, first_sta = true;
49062306a36Sopenharmony_ci	int is_cmd_pend_q_empty;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* Allocate memory for member of adapter structure */
49562306a36Sopenharmony_ci	ret = mwifiex_allocate_adapter(adapter);
49662306a36Sopenharmony_ci	if (ret)
49762306a36Sopenharmony_ci		return -1;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* Initialize adapter structure */
50062306a36Sopenharmony_ci	mwifiex_init_adapter(adapter);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
50362306a36Sopenharmony_ci		if (adapter->priv[i]) {
50462306a36Sopenharmony_ci			priv = adapter->priv[i];
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci			/* Initialize private structure */
50762306a36Sopenharmony_ci			ret = mwifiex_init_priv(priv);
50862306a36Sopenharmony_ci			if (ret)
50962306a36Sopenharmony_ci				return -1;
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci	if (adapter->mfg_mode) {
51362306a36Sopenharmony_ci		adapter->hw_status = MWIFIEX_HW_STATUS_READY;
51462306a36Sopenharmony_ci		ret = -EINPROGRESS;
51562306a36Sopenharmony_ci	} else {
51662306a36Sopenharmony_ci		for (i = 0; i < adapter->priv_num; i++) {
51762306a36Sopenharmony_ci			if (adapter->priv[i]) {
51862306a36Sopenharmony_ci				ret = mwifiex_sta_init_cmd(adapter->priv[i],
51962306a36Sopenharmony_ci							   first_sta, true);
52062306a36Sopenharmony_ci				if (ret == -1)
52162306a36Sopenharmony_ci					return -1;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci				first_sta = false;
52462306a36Sopenharmony_ci			}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	spin_lock_bh(&adapter->cmd_pending_q_lock);
53262306a36Sopenharmony_ci	is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
53362306a36Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_pending_q_lock);
53462306a36Sopenharmony_ci	if (!is_cmd_pend_q_empty) {
53562306a36Sopenharmony_ci		/* Send the first command in queue and return */
53662306a36Sopenharmony_ci		if (mwifiex_main_process(adapter) != -1)
53762306a36Sopenharmony_ci			ret = -EINPROGRESS;
53862306a36Sopenharmony_ci	} else {
53962306a36Sopenharmony_ci		adapter->hw_status = MWIFIEX_HW_STATUS_READY;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return ret;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/*
54662306a36Sopenharmony_ci * This function deletes the BSS priority tables.
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * The function traverses through all the allocated BSS priority nodes
54962306a36Sopenharmony_ci * in every BSS priority table and frees them.
55062306a36Sopenharmony_ci */
55162306a36Sopenharmony_cistatic void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	int i;
55462306a36Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
55562306a36Sopenharmony_ci	struct mwifiex_bss_prio_node *bssprio_node, *tmp_node;
55662306a36Sopenharmony_ci	struct list_head *head;
55762306a36Sopenharmony_ci	spinlock_t *lock; /* bss priority lock */
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; ++i) {
56062306a36Sopenharmony_ci		head = &adapter->bss_prio_tbl[i].bss_prio_head;
56162306a36Sopenharmony_ci		lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
56262306a36Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
56362306a36Sopenharmony_ci			    "info: delete BSS priority table,\t"
56462306a36Sopenharmony_ci			    "bss_type = %d, bss_num = %d, i = %d,\t"
56562306a36Sopenharmony_ci			    "head = %p\n",
56662306a36Sopenharmony_ci			    priv->bss_type, priv->bss_num, i, head);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		{
56962306a36Sopenharmony_ci			spin_lock_bh(lock);
57062306a36Sopenharmony_ci			list_for_each_entry_safe(bssprio_node, tmp_node, head,
57162306a36Sopenharmony_ci						 list) {
57262306a36Sopenharmony_ci				if (bssprio_node->priv == priv) {
57362306a36Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
57462306a36Sopenharmony_ci						    "info: Delete\t"
57562306a36Sopenharmony_ci						    "node %p, next = %p\n",
57662306a36Sopenharmony_ci						    bssprio_node, tmp_node);
57762306a36Sopenharmony_ci					list_del(&bssprio_node->list);
57862306a36Sopenharmony_ci					kfree(bssprio_node);
57962306a36Sopenharmony_ci				}
58062306a36Sopenharmony_ci			}
58162306a36Sopenharmony_ci			spin_unlock_bh(lock);
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/*
58762306a36Sopenharmony_ci * This function frees the private structure, including cleans
58862306a36Sopenharmony_ci * up the TX and RX queues and frees the BSS priority tables.
58962306a36Sopenharmony_ci */
59062306a36Sopenharmony_civoid mwifiex_free_priv(struct mwifiex_private *priv)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	mwifiex_clean_txrx(priv);
59362306a36Sopenharmony_ci	mwifiex_delete_bss_prio_tbl(priv);
59462306a36Sopenharmony_ci	mwifiex_free_curr_bcn(priv);
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/*
59862306a36Sopenharmony_ci * This function is used to shutdown the driver.
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * The following operations are performed sequentially -
60162306a36Sopenharmony_ci *      - Check if already shut down
60262306a36Sopenharmony_ci *      - Make sure the main process has stopped
60362306a36Sopenharmony_ci *      - Clean up the Tx and Rx queues
60462306a36Sopenharmony_ci *      - Delete BSS priority tables
60562306a36Sopenharmony_ci *      - Free the adapter
60662306a36Sopenharmony_ci *      - Notify completion
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_civoid
60962306a36Sopenharmony_cimwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct mwifiex_private *priv;
61262306a36Sopenharmony_ci	s32 i;
61362306a36Sopenharmony_ci	struct sk_buff *skb;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* mwifiex already shutdown */
61662306a36Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)
61762306a36Sopenharmony_ci		return;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* cancel current command */
62062306a36Sopenharmony_ci	if (adapter->curr_cmd) {
62162306a36Sopenharmony_ci		mwifiex_dbg(adapter, WARN,
62262306a36Sopenharmony_ci			    "curr_cmd is still in processing\n");
62362306a36Sopenharmony_ci		del_timer_sync(&adapter->cmd_timer);
62462306a36Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
62562306a36Sopenharmony_ci		adapter->curr_cmd = NULL;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* shut down mwifiex */
62962306a36Sopenharmony_ci	mwifiex_dbg(adapter, MSG,
63062306a36Sopenharmony_ci		    "info: shutdown mwifiex...\n");
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* Clean up Tx/Rx queues and delete BSS priority table */
63362306a36Sopenharmony_ci	for (i = 0; i < adapter->priv_num; i++) {
63462306a36Sopenharmony_ci		if (adapter->priv[i]) {
63562306a36Sopenharmony_ci			priv = adapter->priv[i];
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci			mwifiex_clean_auto_tdls(priv);
63862306a36Sopenharmony_ci			mwifiex_abort_cac(priv);
63962306a36Sopenharmony_ci			mwifiex_free_priv(priv);
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	atomic_set(&adapter->tx_queued, 0);
64462306a36Sopenharmony_ci	while ((skb = skb_dequeue(&adapter->tx_data_q)))
64562306a36Sopenharmony_ci		mwifiex_write_data_complete(adapter, skb, 0, 0);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	spin_lock_bh(&adapter->rx_proc_lock);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	while ((skb = skb_dequeue(&adapter->rx_data_q))) {
65062306a36Sopenharmony_ci		struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		atomic_dec(&adapter->rx_pending);
65362306a36Sopenharmony_ci		priv = adapter->priv[rx_info->bss_num];
65462306a36Sopenharmony_ci		if (priv)
65562306a36Sopenharmony_ci			priv->stats.rx_dropped++;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	spin_unlock_bh(&adapter->rx_proc_lock);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	mwifiex_adapter_cleanup(adapter);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/*
66862306a36Sopenharmony_ci * This function downloads the firmware to the card.
66962306a36Sopenharmony_ci *
67062306a36Sopenharmony_ci * The actual download is preceded by two sanity checks -
67162306a36Sopenharmony_ci *      - Check if firmware is already running
67262306a36Sopenharmony_ci *      - Check if the interface is the winner to download the firmware
67362306a36Sopenharmony_ci *
67462306a36Sopenharmony_ci * ...and followed by another -
67562306a36Sopenharmony_ci *      - Check if the firmware is downloaded successfully
67662306a36Sopenharmony_ci *
67762306a36Sopenharmony_ci * After download is successfully completed, the host interrupts are enabled.
67862306a36Sopenharmony_ci */
67962306a36Sopenharmony_ciint mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
68062306a36Sopenharmony_ci		    struct mwifiex_fw_image *pmfw)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	int ret;
68362306a36Sopenharmony_ci	u32 poll_num = 1;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* check if firmware is already running */
68662306a36Sopenharmony_ci	ret = adapter->if_ops.check_fw_status(adapter, poll_num);
68762306a36Sopenharmony_ci	if (!ret) {
68862306a36Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
68962306a36Sopenharmony_ci			    "WLAN FW already running! Skip FW dnld\n");
69062306a36Sopenharmony_ci		return 0;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/* check if we are the winner for downloading FW */
69462306a36Sopenharmony_ci	if (adapter->if_ops.check_winner_status) {
69562306a36Sopenharmony_ci		adapter->winner = 0;
69662306a36Sopenharmony_ci		ret = adapter->if_ops.check_winner_status(adapter);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		poll_num = MAX_FIRMWARE_POLL_TRIES;
69962306a36Sopenharmony_ci		if (ret) {
70062306a36Sopenharmony_ci			mwifiex_dbg(adapter, MSG,
70162306a36Sopenharmony_ci				    "WLAN read winner status failed!\n");
70262306a36Sopenharmony_ci			return ret;
70362306a36Sopenharmony_ci		}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		if (!adapter->winner) {
70662306a36Sopenharmony_ci			mwifiex_dbg(adapter, MSG,
70762306a36Sopenharmony_ci				    "WLAN is not the winner! Skip FW dnld\n");
70862306a36Sopenharmony_ci			goto poll_fw;
70962306a36Sopenharmony_ci		}
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (pmfw) {
71362306a36Sopenharmony_ci		/* Download firmware with helper */
71462306a36Sopenharmony_ci		ret = adapter->if_ops.prog_fw(adapter, pmfw);
71562306a36Sopenharmony_ci		if (ret) {
71662306a36Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
71762306a36Sopenharmony_ci				    "prog_fw failed ret=%#x\n", ret);
71862306a36Sopenharmony_ci			return ret;
71962306a36Sopenharmony_ci		}
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cipoll_fw:
72362306a36Sopenharmony_ci	/* Check if the firmware is downloaded successfully or not */
72462306a36Sopenharmony_ci	ret = adapter->if_ops.check_fw_status(adapter, poll_num);
72562306a36Sopenharmony_ci	if (ret)
72662306a36Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
72762306a36Sopenharmony_ci			    "FW failed to be active in time\n");
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return ret;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_dnld_fw);
732