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