18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: commands and events
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2011-2020 NXP
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by NXP
78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License Version 2, June 1991
88c2ecf20Sopenharmony_ci * (the "License").  You may use, redistribute and/or modify this File in
98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which
108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc.,
118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
158c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
168c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
178c2ecf20Sopenharmony_ci * this warranty disclaimer.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
218c2ecf20Sopenharmony_ci#include "decl.h"
228c2ecf20Sopenharmony_ci#include "ioctl.h"
238c2ecf20Sopenharmony_ci#include "util.h"
248c2ecf20Sopenharmony_ci#include "fw.h"
258c2ecf20Sopenharmony_ci#include "main.h"
268c2ecf20Sopenharmony_ci#include "wmm.h"
278c2ecf20Sopenharmony_ci#include "11n.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * This function initializes a command node.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * The actual allocation of the node is not done by this function. It only
358c2ecf20Sopenharmony_ci * initiates a node by filling it with default parameters. Similarly,
368c2ecf20Sopenharmony_ci * allocation of the different buffers used (IOCTL buffer, data buffer) are
378c2ecf20Sopenharmony_ci * not done by this function either.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic void
408c2ecf20Sopenharmony_cimwifiex_init_cmd_node(struct mwifiex_private *priv,
418c2ecf20Sopenharmony_ci		      struct cmd_ctrl_node *cmd_node,
428c2ecf20Sopenharmony_ci		      u32 cmd_no, void *data_buf, bool sync)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	cmd_node->priv = priv;
458c2ecf20Sopenharmony_ci	cmd_node->cmd_no = cmd_no;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (sync) {
488c2ecf20Sopenharmony_ci		cmd_node->wait_q_enabled = true;
498c2ecf20Sopenharmony_ci		cmd_node->cmd_wait_q_woken = false;
508c2ecf20Sopenharmony_ci		cmd_node->condition = &cmd_node->cmd_wait_q_woken;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci	cmd_node->data_buf = data_buf;
538c2ecf20Sopenharmony_ci	cmd_node->cmd_skb = cmd_node->skb;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * This function returns a command node from the free queue depending upon
588c2ecf20Sopenharmony_ci * availability.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic struct cmd_ctrl_node *
618c2ecf20Sopenharmony_cimwifiex_get_cmd_node(struct mwifiex_adapter *adapter)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->cmd_free_q_lock);
668c2ecf20Sopenharmony_ci	if (list_empty(&adapter->cmd_free_q)) {
678c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
688c2ecf20Sopenharmony_ci			    "GET_CMD_NODE: cmd node not available\n");
698c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->cmd_free_q_lock);
708c2ecf20Sopenharmony_ci		return NULL;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci	cmd_node = list_first_entry(&adapter->cmd_free_q,
738c2ecf20Sopenharmony_ci				    struct cmd_ctrl_node, list);
748c2ecf20Sopenharmony_ci	list_del(&cmd_node->list);
758c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_free_q_lock);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return cmd_node;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * This function cleans up a command node.
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * The function resets the fields including the buffer pointers.
848c2ecf20Sopenharmony_ci * This function does not try to free the buffers. They must be
858c2ecf20Sopenharmony_ci * freed before calling this function.
868c2ecf20Sopenharmony_ci *
878c2ecf20Sopenharmony_ci * This function will however call the receive completion callback
888c2ecf20Sopenharmony_ci * in case a response buffer is still available before resetting
898c2ecf20Sopenharmony_ci * the pointer.
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_cistatic void
928c2ecf20Sopenharmony_cimwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,
938c2ecf20Sopenharmony_ci		       struct cmd_ctrl_node *cmd_node)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	cmd_node->cmd_no = 0;
968c2ecf20Sopenharmony_ci	cmd_node->cmd_flag = 0;
978c2ecf20Sopenharmony_ci	cmd_node->data_buf = NULL;
988c2ecf20Sopenharmony_ci	cmd_node->wait_q_enabled = false;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (cmd_node->cmd_skb)
1018c2ecf20Sopenharmony_ci		skb_trim(cmd_node->cmd_skb, 0);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (cmd_node->resp_skb) {
1048c2ecf20Sopenharmony_ci		adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb);
1058c2ecf20Sopenharmony_ci		cmd_node->resp_skb = NULL;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * This function returns a command to the command free queue.
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * The function also calls the completion callback if required, before
1138c2ecf20Sopenharmony_ci * cleaning the command node and re-inserting it into the free queue.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void
1168c2ecf20Sopenharmony_cimwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
1178c2ecf20Sopenharmony_ci			     struct cmd_ctrl_node *cmd_node)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	if (!cmd_node)
1208c2ecf20Sopenharmony_ci		return;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (cmd_node->wait_q_enabled)
1238c2ecf20Sopenharmony_ci		mwifiex_complete_cmd(adapter, cmd_node);
1248c2ecf20Sopenharmony_ci	/* Clean the node */
1258c2ecf20Sopenharmony_ci	mwifiex_clean_cmd_node(adapter, cmd_node);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Insert node into cmd_free_q */
1288c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->cmd_free_q_lock);
1298c2ecf20Sopenharmony_ci	list_add_tail(&cmd_node->list, &adapter->cmd_free_q);
1308c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_free_q_lock);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* This function reuses a command node. */
1348c2ecf20Sopenharmony_civoid mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
1358c2ecf20Sopenharmony_ci			      struct cmd_ctrl_node *cmd_node)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	atomic_dec(&adapter->cmd_pending);
1428c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, CMD,
1438c2ecf20Sopenharmony_ci		    "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n",
1448c2ecf20Sopenharmony_ci		le16_to_cpu(host_cmd->command),
1458c2ecf20Sopenharmony_ci		atomic_read(&adapter->cmd_pending));
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * This function sends a host command to the firmware.
1508c2ecf20Sopenharmony_ci *
1518c2ecf20Sopenharmony_ci * The function copies the host command into the driver command
1528c2ecf20Sopenharmony_ci * buffer, which will be transferred to the firmware later by the
1538c2ecf20Sopenharmony_ci * main thread.
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic int mwifiex_cmd_host_cmd(struct mwifiex_private *priv,
1568c2ecf20Sopenharmony_ci				struct host_cmd_ds_command *cmd,
1578c2ecf20Sopenharmony_ci				struct mwifiex_ds_misc_cmd *pcmd_ptr)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	/* Copy the HOST command to command buffer */
1608c2ecf20Sopenharmony_ci	memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len);
1618c2ecf20Sopenharmony_ci	mwifiex_dbg(priv->adapter, CMD,
1628c2ecf20Sopenharmony_ci		    "cmd: host cmd size = %d\n", pcmd_ptr->len);
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/*
1678c2ecf20Sopenharmony_ci * This function downloads a command to the firmware.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * The function performs sanity tests, sets the command sequence
1708c2ecf20Sopenharmony_ci * number and size, converts the header fields to CPU format before
1718c2ecf20Sopenharmony_ci * sending. Afterwards, it logs the command ID and action for debugging
1728c2ecf20Sopenharmony_ci * and sets up the command timeout timer.
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_cistatic int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
1758c2ecf20Sopenharmony_ci				  struct cmd_ctrl_node *cmd_node)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
1798c2ecf20Sopenharmony_ci	int ret;
1808c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *host_cmd;
1818c2ecf20Sopenharmony_ci	uint16_t cmd_code;
1828c2ecf20Sopenharmony_ci	uint16_t cmd_size;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (!adapter || !cmd_node)
1858c2ecf20Sopenharmony_ci		return -1;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* Sanity test */
1908c2ecf20Sopenharmony_ci	if (host_cmd == NULL || host_cmd->size == 0) {
1918c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
1928c2ecf20Sopenharmony_ci			    "DNLD_CMD: host_cmd is null\t"
1938c2ecf20Sopenharmony_ci			    "or cmd size is 0, not sending\n");
1948c2ecf20Sopenharmony_ci		if (cmd_node->wait_q_enabled)
1958c2ecf20Sopenharmony_ci			adapter->cmd_wait_q.status = -1;
1968c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, cmd_node);
1978c2ecf20Sopenharmony_ci		return -1;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	cmd_code = le16_to_cpu(host_cmd->command);
2018c2ecf20Sopenharmony_ci	cmd_node->cmd_no = cmd_code;
2028c2ecf20Sopenharmony_ci	cmd_size = le16_to_cpu(host_cmd->size);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET &&
2058c2ecf20Sopenharmony_ci	    cmd_code != HostCmd_CMD_FUNC_SHUTDOWN &&
2068c2ecf20Sopenharmony_ci	    cmd_code != HostCmd_CMD_FUNC_INIT) {
2078c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
2088c2ecf20Sopenharmony_ci			    "DNLD_CMD: FW in reset state, ignore cmd %#x\n",
2098c2ecf20Sopenharmony_ci			cmd_code);
2108c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, cmd_node);
2118c2ecf20Sopenharmony_ci		queue_work(adapter->workqueue, &adapter->main_work);
2128c2ecf20Sopenharmony_ci		return -1;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Set command sequence number */
2168c2ecf20Sopenharmony_ci	adapter->seq_num++;
2178c2ecf20Sopenharmony_ci	host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO
2188c2ecf20Sopenharmony_ci					(adapter->seq_num,
2198c2ecf20Sopenharmony_ci					 cmd_node->priv->bss_num,
2208c2ecf20Sopenharmony_ci					 cmd_node->priv->bss_type));
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->mwifiex_cmd_lock);
2238c2ecf20Sopenharmony_ci	adapter->curr_cmd = cmd_node;
2248c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->mwifiex_cmd_lock);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Adjust skb length */
2278c2ecf20Sopenharmony_ci	if (cmd_node->cmd_skb->len > cmd_size)
2288c2ecf20Sopenharmony_ci		/*
2298c2ecf20Sopenharmony_ci		 * cmd_size is less than sizeof(struct host_cmd_ds_command).
2308c2ecf20Sopenharmony_ci		 * Trim off the unused portion.
2318c2ecf20Sopenharmony_ci		 */
2328c2ecf20Sopenharmony_ci		skb_trim(cmd_node->cmd_skb, cmd_size);
2338c2ecf20Sopenharmony_ci	else if (cmd_node->cmd_skb->len < cmd_size)
2348c2ecf20Sopenharmony_ci		/*
2358c2ecf20Sopenharmony_ci		 * cmd_size is larger than sizeof(struct host_cmd_ds_command)
2368c2ecf20Sopenharmony_ci		 * because we have appended custom IE TLV. Increase skb length
2378c2ecf20Sopenharmony_ci		 * accordingly.
2388c2ecf20Sopenharmony_ci		 */
2398c2ecf20Sopenharmony_ci		skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, CMD,
2428c2ecf20Sopenharmony_ci		    "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
2438c2ecf20Sopenharmony_ci		    cmd_code,
2448c2ecf20Sopenharmony_ci		    get_unaligned_le16((u8 *)host_cmd + S_DS_GEN),
2458c2ecf20Sopenharmony_ci		    cmd_size, le16_to_cpu(host_cmd->seq_num));
2468c2ecf20Sopenharmony_ci	mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_USB) {
2498c2ecf20Sopenharmony_ci		skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);
2508c2ecf20Sopenharmony_ci		put_unaligned_le32(MWIFIEX_USB_TYPE_CMD,
2518c2ecf20Sopenharmony_ci				   cmd_node->cmd_skb->data);
2528c2ecf20Sopenharmony_ci		adapter->cmd_sent = true;
2538c2ecf20Sopenharmony_ci		ret = adapter->if_ops.host_to_card(adapter,
2548c2ecf20Sopenharmony_ci						   MWIFIEX_USB_EP_CMD_EVENT,
2558c2ecf20Sopenharmony_ci						   cmd_node->cmd_skb, NULL);
2568c2ecf20Sopenharmony_ci		skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);
2578c2ecf20Sopenharmony_ci		if (ret == -EBUSY)
2588c2ecf20Sopenharmony_ci			cmd_node->cmd_skb = NULL;
2598c2ecf20Sopenharmony_ci	} else {
2608c2ecf20Sopenharmony_ci		skb_push(cmd_node->cmd_skb, adapter->intf_hdr_len);
2618c2ecf20Sopenharmony_ci		ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
2628c2ecf20Sopenharmony_ci						   cmd_node->cmd_skb, NULL);
2638c2ecf20Sopenharmony_ci		skb_pull(cmd_node->cmd_skb, adapter->intf_hdr_len);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (ret == -1) {
2678c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
2688c2ecf20Sopenharmony_ci			    "DNLD_CMD: host to card failed\n");
2698c2ecf20Sopenharmony_ci		if (adapter->iface_type == MWIFIEX_USB)
2708c2ecf20Sopenharmony_ci			adapter->cmd_sent = false;
2718c2ecf20Sopenharmony_ci		if (cmd_node->wait_q_enabled)
2728c2ecf20Sopenharmony_ci			adapter->cmd_wait_q.status = -1;
2738c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
2768c2ecf20Sopenharmony_ci		adapter->curr_cmd = NULL;
2778c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		adapter->dbg.num_cmd_host_to_card_failure++;
2808c2ecf20Sopenharmony_ci		return -1;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* Save the last command id and action to debug log */
2848c2ecf20Sopenharmony_ci	adapter->dbg.last_cmd_index =
2858c2ecf20Sopenharmony_ci			(adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM;
2868c2ecf20Sopenharmony_ci	adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code;
2878c2ecf20Sopenharmony_ci	adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] =
2888c2ecf20Sopenharmony_ci			get_unaligned_le16((u8 *)host_cmd + S_DS_GEN);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Setup the timer after transmit command, except that specific
2918c2ecf20Sopenharmony_ci	 * command might not have command response.
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	if (cmd_code != HostCmd_CMD_FW_DUMP_EVENT)
2948c2ecf20Sopenharmony_ci		mod_timer(&adapter->cmd_timer,
2958c2ecf20Sopenharmony_ci			  jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* Clear BSS_NO_BITS from HostCmd */
2988c2ecf20Sopenharmony_ci	cmd_code &= HostCmd_CMD_ID_MASK;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * This function downloads a sleep confirm command to the firmware.
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * The function performs sanity tests, sets the command sequence
3078c2ecf20Sopenharmony_ci * number and size, converts the header fields to CPU format before
3088c2ecf20Sopenharmony_ci * sending.
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * No responses are needed for sleep confirm command.
3118c2ecf20Sopenharmony_ci */
3128c2ecf20Sopenharmony_cistatic int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int ret;
3158c2ecf20Sopenharmony_ci	struct mwifiex_private *priv;
3168c2ecf20Sopenharmony_ci	struct mwifiex_opt_sleep_confirm *sleep_cfm_buf =
3178c2ecf20Sopenharmony_ci				(struct mwifiex_opt_sleep_confirm *)
3188c2ecf20Sopenharmony_ci						adapter->sleep_cfm->data;
3198c2ecf20Sopenharmony_ci	struct sk_buff *sleep_cfm_tmp;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	adapter->seq_num++;
3248c2ecf20Sopenharmony_ci	sleep_cfm_buf->seq_num =
3258c2ecf20Sopenharmony_ci		cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO
3268c2ecf20Sopenharmony_ci					(adapter->seq_num, priv->bss_num,
3278c2ecf20Sopenharmony_ci					 priv->bss_type));
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, CMD,
3308c2ecf20Sopenharmony_ci		    "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
3318c2ecf20Sopenharmony_ci		le16_to_cpu(sleep_cfm_buf->command),
3328c2ecf20Sopenharmony_ci		le16_to_cpu(sleep_cfm_buf->action),
3338c2ecf20Sopenharmony_ci		le16_to_cpu(sleep_cfm_buf->size),
3348c2ecf20Sopenharmony_ci		le16_to_cpu(sleep_cfm_buf->seq_num));
3358c2ecf20Sopenharmony_ci	mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf,
3368c2ecf20Sopenharmony_ci			 le16_to_cpu(sleep_cfm_buf->size));
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (adapter->iface_type == MWIFIEX_USB) {
3398c2ecf20Sopenharmony_ci		sleep_cfm_tmp =
3408c2ecf20Sopenharmony_ci			dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
3418c2ecf20Sopenharmony_ci				      + MWIFIEX_TYPE_LEN);
3428c2ecf20Sopenharmony_ci		if (!sleep_cfm_tmp) {
3438c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
3448c2ecf20Sopenharmony_ci				    "SLEEP_CFM: dev_alloc_skb failed\n");
3458c2ecf20Sopenharmony_ci			return -ENOMEM;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm)
3498c2ecf20Sopenharmony_ci			+ MWIFIEX_TYPE_LEN);
3508c2ecf20Sopenharmony_ci		put_unaligned_le32(MWIFIEX_USB_TYPE_CMD, sleep_cfm_tmp->data);
3518c2ecf20Sopenharmony_ci		memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN,
3528c2ecf20Sopenharmony_ci		       adapter->sleep_cfm->data,
3538c2ecf20Sopenharmony_ci		       sizeof(struct mwifiex_opt_sleep_confirm));
3548c2ecf20Sopenharmony_ci		ret = adapter->if_ops.host_to_card(adapter,
3558c2ecf20Sopenharmony_ci						   MWIFIEX_USB_EP_CMD_EVENT,
3568c2ecf20Sopenharmony_ci						   sleep_cfm_tmp, NULL);
3578c2ecf20Sopenharmony_ci		if (ret != -EBUSY)
3588c2ecf20Sopenharmony_ci			dev_kfree_skb_any(sleep_cfm_tmp);
3598c2ecf20Sopenharmony_ci	} else {
3608c2ecf20Sopenharmony_ci		skb_push(adapter->sleep_cfm, adapter->intf_hdr_len);
3618c2ecf20Sopenharmony_ci		ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
3628c2ecf20Sopenharmony_ci						   adapter->sleep_cfm, NULL);
3638c2ecf20Sopenharmony_ci		skb_pull(adapter->sleep_cfm, adapter->intf_hdr_len);
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (ret == -1) {
3678c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n");
3688c2ecf20Sopenharmony_ci		adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;
3698c2ecf20Sopenharmony_ci		return -1;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))
3738c2ecf20Sopenharmony_ci		/* Response is not needed for sleep confirm command */
3748c2ecf20Sopenharmony_ci		adapter->ps_state = PS_STATE_SLEEP;
3758c2ecf20Sopenharmony_ci	else
3768c2ecf20Sopenharmony_ci		adapter->ps_state = PS_STATE_SLEEP_CFM;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&
3798c2ecf20Sopenharmony_ci	    (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags) &&
3808c2ecf20Sopenharmony_ci	     !adapter->sleep_period.period)) {
3818c2ecf20Sopenharmony_ci		adapter->pm_wakeup_card_req = true;
3828c2ecf20Sopenharmony_ci		mwifiex_hs_activated_event(mwifiex_get_priv
3838c2ecf20Sopenharmony_ci				(adapter, MWIFIEX_BSS_ROLE_ANY), true);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return ret;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/*
3908c2ecf20Sopenharmony_ci * This function allocates the command buffers and links them to
3918c2ecf20Sopenharmony_ci * the command free queue.
3928c2ecf20Sopenharmony_ci *
3938c2ecf20Sopenharmony_ci * The driver uses a pre allocated number of command buffers, which
3948c2ecf20Sopenharmony_ci * are created at driver initializations and freed at driver cleanup.
3958c2ecf20Sopenharmony_ci * Every command needs to obtain a command buffer from this pool before
3968c2ecf20Sopenharmony_ci * it can be issued. The command free queue lists the command buffers
3978c2ecf20Sopenharmony_ci * currently free to use, while the command pending queue lists the
3988c2ecf20Sopenharmony_ci * command buffers already in use and awaiting handling. Command buffers
3998c2ecf20Sopenharmony_ci * are returned to the free queue after use.
4008c2ecf20Sopenharmony_ci */
4018c2ecf20Sopenharmony_ciint mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_array;
4048c2ecf20Sopenharmony_ci	u32 i;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* Allocate and initialize struct cmd_ctrl_node */
4078c2ecf20Sopenharmony_ci	cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER,
4088c2ecf20Sopenharmony_ci			    sizeof(struct cmd_ctrl_node), GFP_KERNEL);
4098c2ecf20Sopenharmony_ci	if (!cmd_array)
4108c2ecf20Sopenharmony_ci		return -ENOMEM;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	adapter->cmd_pool = cmd_array;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* Allocate and initialize command buffers */
4158c2ecf20Sopenharmony_ci	for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
4168c2ecf20Sopenharmony_ci		cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);
4178c2ecf20Sopenharmony_ci		if (!cmd_array[i].skb) {
4188c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
4198c2ecf20Sopenharmony_ci				    "unable to allocate command buffer\n");
4208c2ecf20Sopenharmony_ci			return -ENOMEM;
4218c2ecf20Sopenharmony_ci		}
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++)
4258c2ecf20Sopenharmony_ci		mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return 0;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci/*
4318c2ecf20Sopenharmony_ci * This function frees the command buffers.
4328c2ecf20Sopenharmony_ci *
4338c2ecf20Sopenharmony_ci * The function calls the completion callback for all the command
4348c2ecf20Sopenharmony_ci * buffers that still have response buffers associated with them.
4358c2ecf20Sopenharmony_ci */
4368c2ecf20Sopenharmony_civoid mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_array;
4398c2ecf20Sopenharmony_ci	u32 i;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Need to check if cmd pool is allocated or not */
4428c2ecf20Sopenharmony_ci	if (!adapter->cmd_pool) {
4438c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, FATAL,
4448c2ecf20Sopenharmony_ci			    "info: FREE_CMD_BUF: cmd_pool is null\n");
4458c2ecf20Sopenharmony_ci		return;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	cmd_array = adapter->cmd_pool;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* Release shared memory buffers */
4518c2ecf20Sopenharmony_ci	for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
4528c2ecf20Sopenharmony_ci		if (cmd_array[i].skb) {
4538c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, CMD,
4548c2ecf20Sopenharmony_ci				    "cmd: free cmd buffer %d\n", i);
4558c2ecf20Sopenharmony_ci			dev_kfree_skb_any(cmd_array[i].skb);
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci		if (!cmd_array[i].resp_skb)
4588c2ecf20Sopenharmony_ci			continue;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (adapter->iface_type == MWIFIEX_USB)
4618c2ecf20Sopenharmony_ci			adapter->if_ops.cmdrsp_complete(adapter,
4628c2ecf20Sopenharmony_ci							cmd_array[i].resp_skb);
4638c2ecf20Sopenharmony_ci		else
4648c2ecf20Sopenharmony_ci			dev_kfree_skb_any(cmd_array[i].resp_skb);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci	/* Release struct cmd_ctrl_node */
4678c2ecf20Sopenharmony_ci	if (adapter->cmd_pool) {
4688c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, CMD,
4698c2ecf20Sopenharmony_ci			    "cmd: free cmd pool\n");
4708c2ecf20Sopenharmony_ci		kfree(adapter->cmd_pool);
4718c2ecf20Sopenharmony_ci		adapter->cmd_pool = NULL;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/*
4768c2ecf20Sopenharmony_ci * This function handles events generated by firmware.
4778c2ecf20Sopenharmony_ci *
4788c2ecf20Sopenharmony_ci * Event body of events received from firmware are not used (though they are
4798c2ecf20Sopenharmony_ci * saved), only the event ID is used. Some events are re-invoked by
4808c2ecf20Sopenharmony_ci * the driver, with a new event body.
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * After processing, the function calls the completion callback
4838c2ecf20Sopenharmony_ci * for cleanup.
4848c2ecf20Sopenharmony_ci */
4858c2ecf20Sopenharmony_ciint mwifiex_process_event(struct mwifiex_adapter *adapter)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	int ret, i;
4888c2ecf20Sopenharmony_ci	struct mwifiex_private *priv =
4898c2ecf20Sopenharmony_ci		mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
4908c2ecf20Sopenharmony_ci	struct sk_buff *skb = adapter->event_skb;
4918c2ecf20Sopenharmony_ci	u32 eventcause;
4928c2ecf20Sopenharmony_ci	struct mwifiex_rxinfo *rx_info;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if ((adapter->event_cause & EVENT_ID_MASK) == EVENT_RADAR_DETECTED) {
4958c2ecf20Sopenharmony_ci		for (i = 0; i < adapter->priv_num; i++) {
4968c2ecf20Sopenharmony_ci			priv = adapter->priv[i];
4978c2ecf20Sopenharmony_ci			if (priv && mwifiex_is_11h_active(priv)) {
4988c2ecf20Sopenharmony_ci				adapter->event_cause |=
4998c2ecf20Sopenharmony_ci					((priv->bss_num & 0xff) << 16) |
5008c2ecf20Sopenharmony_ci					((priv->bss_type & 0xff) << 24);
5018c2ecf20Sopenharmony_ci				break;
5028c2ecf20Sopenharmony_ci			}
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	eventcause = adapter->event_cause;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* Save the last event to debug log */
5098c2ecf20Sopenharmony_ci	adapter->dbg.last_event_index =
5108c2ecf20Sopenharmony_ci			(adapter->dbg.last_event_index + 1) % DBG_CMD_NUM;
5118c2ecf20Sopenharmony_ci	adapter->dbg.last_event[adapter->dbg.last_event_index] =
5128c2ecf20Sopenharmony_ci							(u16) eventcause;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* Get BSS number and corresponding priv */
5158c2ecf20Sopenharmony_ci	priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause),
5168c2ecf20Sopenharmony_ci				      EVENT_GET_BSS_TYPE(eventcause));
5178c2ecf20Sopenharmony_ci	if (!priv)
5188c2ecf20Sopenharmony_ci		priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* Clear BSS_NO_BITS from event */
5218c2ecf20Sopenharmony_ci	eventcause &= EVENT_ID_MASK;
5228c2ecf20Sopenharmony_ci	adapter->event_cause = eventcause;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (skb) {
5258c2ecf20Sopenharmony_ci		rx_info = MWIFIEX_SKB_RXCB(skb);
5268c2ecf20Sopenharmony_ci		memset(rx_info, 0, sizeof(*rx_info));
5278c2ecf20Sopenharmony_ci		rx_info->bss_num = priv->bss_num;
5288c2ecf20Sopenharmony_ci		rx_info->bss_type = priv->bss_type;
5298c2ecf20Sopenharmony_ci		mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:",
5308c2ecf20Sopenharmony_ci				 skb->data, skb->len);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
5368c2ecf20Sopenharmony_ci		ret = mwifiex_process_uap_event(priv);
5378c2ecf20Sopenharmony_ci	else
5388c2ecf20Sopenharmony_ci		ret = mwifiex_process_sta_event(priv);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	adapter->event_cause = 0;
5418c2ecf20Sopenharmony_ci	adapter->event_skb = NULL;
5428c2ecf20Sopenharmony_ci	adapter->if_ops.event_complete(adapter, skb);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return ret;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/*
5488c2ecf20Sopenharmony_ci * This function prepares a command and send it to the firmware.
5498c2ecf20Sopenharmony_ci *
5508c2ecf20Sopenharmony_ci * Preparation includes -
5518c2ecf20Sopenharmony_ci *      - Sanity tests to make sure the card is still present or the FW
5528c2ecf20Sopenharmony_ci *        is not reset
5538c2ecf20Sopenharmony_ci *      - Getting a new command node from the command free queue
5548c2ecf20Sopenharmony_ci *      - Initializing the command node for default parameters
5558c2ecf20Sopenharmony_ci *      - Fill up the non-default parameters and buffer pointers
5568c2ecf20Sopenharmony_ci *      - Add the command to pending queue
5578c2ecf20Sopenharmony_ci */
5588c2ecf20Sopenharmony_ciint mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,
5598c2ecf20Sopenharmony_ci		     u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	int ret;
5628c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
5638c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
5648c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *cmd_ptr;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (!adapter) {
5678c2ecf20Sopenharmony_ci		pr_err("PREP_CMD: adapter is NULL\n");
5688c2ecf20Sopenharmony_ci		return -1;
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) {
5728c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
5738c2ecf20Sopenharmony_ci			    "PREP_CMD: device in suspended state\n");
5748c2ecf20Sopenharmony_ci		return -1;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (test_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags) &&
5788c2ecf20Sopenharmony_ci	    cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) {
5798c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
5808c2ecf20Sopenharmony_ci			    "PREP_CMD: host entering sleep state\n");
5818c2ecf20Sopenharmony_ci		return -1;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) {
5858c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
5868c2ecf20Sopenharmony_ci			    "PREP_CMD: card is removed\n");
5878c2ecf20Sopenharmony_ci		return -1;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
5918c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
5928c2ecf20Sopenharmony_ci			    "PREP_CMD: FW is in bad state\n");
5938c2ecf20Sopenharmony_ci		return -1;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) {
5978c2ecf20Sopenharmony_ci		if (cmd_no != HostCmd_CMD_FUNC_INIT) {
5988c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
5998c2ecf20Sopenharmony_ci				    "PREP_CMD: FW in reset state\n");
6008c2ecf20Sopenharmony_ci			return -1;
6018c2ecf20Sopenharmony_ci		}
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci	/* We don't expect commands in manufacturing mode. They are cooked
6048c2ecf20Sopenharmony_ci	 * in application and ready to download buffer is passed to the driver
6058c2ecf20Sopenharmony_ci	 */
6068c2ecf20Sopenharmony_ci	if (adapter->mfg_mode && cmd_no) {
6078c2ecf20Sopenharmony_ci		dev_dbg(adapter->dev, "Ignoring commands in manufacturing mode\n");
6088c2ecf20Sopenharmony_ci		return -1;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* Get a new command node */
6138c2ecf20Sopenharmony_ci	cmd_node = mwifiex_get_cmd_node(adapter);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (!cmd_node) {
6168c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
6178c2ecf20Sopenharmony_ci			    "PREP_CMD: no free cmd node\n");
6188c2ecf20Sopenharmony_ci		return -1;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* Initialize the command node */
6228c2ecf20Sopenharmony_ci	mwifiex_init_cmd_node(priv, cmd_node, cmd_no, data_buf, sync);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (!cmd_node->cmd_skb) {
6258c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
6268c2ecf20Sopenharmony_ci			    "PREP_CMD: no free cmd buf\n");
6278c2ecf20Sopenharmony_ci		return -1;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	skb_put_zero(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command));
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
6338c2ecf20Sopenharmony_ci	cmd_ptr->command = cpu_to_le16(cmd_no);
6348c2ecf20Sopenharmony_ci	cmd_ptr->result = 0;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* Prepare command */
6378c2ecf20Sopenharmony_ci	if (cmd_no) {
6388c2ecf20Sopenharmony_ci		switch (cmd_no) {
6398c2ecf20Sopenharmony_ci		case HostCmd_CMD_UAP_SYS_CONFIG:
6408c2ecf20Sopenharmony_ci		case HostCmd_CMD_UAP_BSS_START:
6418c2ecf20Sopenharmony_ci		case HostCmd_CMD_UAP_BSS_STOP:
6428c2ecf20Sopenharmony_ci		case HostCmd_CMD_UAP_STA_DEAUTH:
6438c2ecf20Sopenharmony_ci		case HOST_CMD_APCMD_SYS_RESET:
6448c2ecf20Sopenharmony_ci		case HOST_CMD_APCMD_STA_LIST:
6458c2ecf20Sopenharmony_ci			ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
6468c2ecf20Sopenharmony_ci						      cmd_oid, data_buf,
6478c2ecf20Sopenharmony_ci						      cmd_ptr);
6488c2ecf20Sopenharmony_ci			break;
6498c2ecf20Sopenharmony_ci		default:
6508c2ecf20Sopenharmony_ci			ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action,
6518c2ecf20Sopenharmony_ci						      cmd_oid, data_buf,
6528c2ecf20Sopenharmony_ci						      cmd_ptr);
6538c2ecf20Sopenharmony_ci			break;
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	} else {
6568c2ecf20Sopenharmony_ci		ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf);
6578c2ecf20Sopenharmony_ci		cmd_node->cmd_flag |= CMD_F_HOSTCMD;
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/* Return error, since the command preparation failed */
6618c2ecf20Sopenharmony_ci	if (ret) {
6628c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
6638c2ecf20Sopenharmony_ci			    "PREP_CMD: cmd %#x preparation failed\n",
6648c2ecf20Sopenharmony_ci			cmd_no);
6658c2ecf20Sopenharmony_ci		mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
6668c2ecf20Sopenharmony_ci		return -1;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/* Send command */
6708c2ecf20Sopenharmony_ci	if (cmd_no == HostCmd_CMD_802_11_SCAN ||
6718c2ecf20Sopenharmony_ci	    cmd_no == HostCmd_CMD_802_11_SCAN_EXT) {
6728c2ecf20Sopenharmony_ci		mwifiex_queue_scan_cmd(priv, cmd_node);
6738c2ecf20Sopenharmony_ci	} else {
6748c2ecf20Sopenharmony_ci		mwifiex_insert_cmd_to_pending_q(adapter, cmd_node);
6758c2ecf20Sopenharmony_ci		queue_work(adapter->workqueue, &adapter->main_work);
6768c2ecf20Sopenharmony_ci		if (cmd_node->wait_q_enabled)
6778c2ecf20Sopenharmony_ci			ret = mwifiex_wait_queue_complete(adapter, cmd_node);
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return ret;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/*
6848c2ecf20Sopenharmony_ci * This function queues a command to the command pending queue.
6858c2ecf20Sopenharmony_ci *
6868c2ecf20Sopenharmony_ci * This in effect adds the command to the command list to be executed.
6878c2ecf20Sopenharmony_ci * Exit PS command is handled specially, by placing it always to the
6888c2ecf20Sopenharmony_ci * front of the command queue.
6898c2ecf20Sopenharmony_ci */
6908c2ecf20Sopenharmony_civoid
6918c2ecf20Sopenharmony_cimwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
6928c2ecf20Sopenharmony_ci				struct cmd_ctrl_node *cmd_node)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *host_cmd = NULL;
6958c2ecf20Sopenharmony_ci	u16 command;
6968c2ecf20Sopenharmony_ci	bool add_tail = true;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
6998c2ecf20Sopenharmony_ci	if (!host_cmd) {
7008c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n");
7018c2ecf20Sopenharmony_ci		return;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	command = le16_to_cpu(host_cmd->command);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/* Exit_PS command needs to be queued in the header always. */
7078c2ecf20Sopenharmony_ci	if (command == HostCmd_CMD_802_11_PS_MODE_ENH) {
7088c2ecf20Sopenharmony_ci		struct host_cmd_ds_802_11_ps_mode_enh *pm =
7098c2ecf20Sopenharmony_ci						&host_cmd->params.psmode_enh;
7108c2ecf20Sopenharmony_ci		if ((le16_to_cpu(pm->action) == DIS_PS) ||
7118c2ecf20Sopenharmony_ci		    (le16_to_cpu(pm->action) == DIS_AUTO_PS)) {
7128c2ecf20Sopenharmony_ci			if (adapter->ps_state != PS_STATE_AWAKE)
7138c2ecf20Sopenharmony_ci				add_tail = false;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->cmd_pending_q_lock);
7188c2ecf20Sopenharmony_ci	if (add_tail)
7198c2ecf20Sopenharmony_ci		list_add_tail(&cmd_node->list, &adapter->cmd_pending_q);
7208c2ecf20Sopenharmony_ci	else
7218c2ecf20Sopenharmony_ci		list_add(&cmd_node->list, &adapter->cmd_pending_q);
7228c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_pending_q_lock);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	atomic_inc(&adapter->cmd_pending);
7258c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, CMD,
7268c2ecf20Sopenharmony_ci		    "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",
7278c2ecf20Sopenharmony_ci		command, atomic_read(&adapter->cmd_pending));
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci/*
7318c2ecf20Sopenharmony_ci * This function executes the next command in command pending queue.
7328c2ecf20Sopenharmony_ci *
7338c2ecf20Sopenharmony_ci * This function will fail if a command is already in processing stage,
7348c2ecf20Sopenharmony_ci * otherwise it will dequeue the first command from the command pending
7358c2ecf20Sopenharmony_ci * queue and send to the firmware.
7368c2ecf20Sopenharmony_ci *
7378c2ecf20Sopenharmony_ci * If the device is currently in host sleep mode, any commands, except the
7388c2ecf20Sopenharmony_ci * host sleep configuration command will de-activate the host sleep. For PS
7398c2ecf20Sopenharmony_ci * mode, the function will put the firmware back to sleep if applicable.
7408c2ecf20Sopenharmony_ci */
7418c2ecf20Sopenharmony_ciint mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	struct mwifiex_private *priv;
7448c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
7458c2ecf20Sopenharmony_ci	int ret = 0;
7468c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *host_cmd;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* Check if already in processing */
7498c2ecf20Sopenharmony_ci	if (adapter->curr_cmd) {
7508c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, FATAL,
7518c2ecf20Sopenharmony_ci			    "EXEC_NEXT_CMD: cmd in processing\n");
7528c2ecf20Sopenharmony_ci		return -1;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->mwifiex_cmd_lock);
7568c2ecf20Sopenharmony_ci	/* Check if any command is pending */
7578c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->cmd_pending_q_lock);
7588c2ecf20Sopenharmony_ci	if (list_empty(&adapter->cmd_pending_q)) {
7598c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->cmd_pending_q_lock);
7608c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
7618c2ecf20Sopenharmony_ci		return 0;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci	cmd_node = list_first_entry(&adapter->cmd_pending_q,
7648c2ecf20Sopenharmony_ci				    struct cmd_ctrl_node, list);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
7678c2ecf20Sopenharmony_ci	priv = cmd_node->priv;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (adapter->ps_state != PS_STATE_AWAKE) {
7708c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
7718c2ecf20Sopenharmony_ci			    "%s: cannot send cmd in sleep state,\t"
7728c2ecf20Sopenharmony_ci			    "this should not happen\n", __func__);
7738c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->cmd_pending_q_lock);
7748c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
7758c2ecf20Sopenharmony_ci		return ret;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	list_del(&cmd_node->list);
7798c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_pending_q_lock);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->mwifiex_cmd_lock);
7828c2ecf20Sopenharmony_ci	ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node);
7838c2ecf20Sopenharmony_ci	priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
7848c2ecf20Sopenharmony_ci	/* Any command sent to the firmware when host is in sleep
7858c2ecf20Sopenharmony_ci	 * mode should de-configure host sleep. We should skip the
7868c2ecf20Sopenharmony_ci	 * host sleep configuration command itself though
7878c2ecf20Sopenharmony_ci	 */
7888c2ecf20Sopenharmony_ci	if (priv && (host_cmd->command !=
7898c2ecf20Sopenharmony_ci	     cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) {
7908c2ecf20Sopenharmony_ci		if (adapter->hs_activated) {
7918c2ecf20Sopenharmony_ci			clear_bit(MWIFIEX_IS_HS_CONFIGURED,
7928c2ecf20Sopenharmony_ci				  &adapter->work_flags);
7938c2ecf20Sopenharmony_ci			mwifiex_hs_activated_event(priv, false);
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return ret;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci/*
8018c2ecf20Sopenharmony_ci * This function handles the command response.
8028c2ecf20Sopenharmony_ci *
8038c2ecf20Sopenharmony_ci * After processing, the function cleans the command node and puts
8048c2ecf20Sopenharmony_ci * it back to the command free queue.
8058c2ecf20Sopenharmony_ci */
8068c2ecf20Sopenharmony_ciint mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *resp;
8098c2ecf20Sopenharmony_ci	struct mwifiex_private *priv =
8108c2ecf20Sopenharmony_ci		mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
8118c2ecf20Sopenharmony_ci	int ret = 0;
8128c2ecf20Sopenharmony_ci	uint16_t orig_cmdresp_no;
8138c2ecf20Sopenharmony_ci	uint16_t cmdresp_no;
8148c2ecf20Sopenharmony_ci	uint16_t cmdresp_result;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {
8178c2ecf20Sopenharmony_ci		resp = (struct host_cmd_ds_command *) adapter->upld_buf;
8188c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
8198c2ecf20Sopenharmony_ci			    "CMD_RESP: NULL curr_cmd, %#x\n",
8208c2ecf20Sopenharmony_ci			    le16_to_cpu(resp->command));
8218c2ecf20Sopenharmony_ci		return -1;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	resp = (struct host_cmd_ds_command *)adapter->curr_cmd->resp_skb->data;
8258c2ecf20Sopenharmony_ci	orig_cmdresp_no = le16_to_cpu(resp->command);
8268c2ecf20Sopenharmony_ci	cmdresp_no = (orig_cmdresp_no & HostCmd_CMD_ID_MASK);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (adapter->curr_cmd->cmd_no != cmdresp_no) {
8298c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
8308c2ecf20Sopenharmony_ci			    "cmdresp error: cmd=0x%x cmd_resp=0x%x\n",
8318c2ecf20Sopenharmony_ci			    adapter->curr_cmd->cmd_no, cmdresp_no);
8328c2ecf20Sopenharmony_ci		return -1;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci	/* Now we got response from FW, cancel the command timer */
8358c2ecf20Sopenharmony_ci	del_timer_sync(&adapter->cmd_timer);
8368c2ecf20Sopenharmony_ci	clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
8398c2ecf20Sopenharmony_ci		/* Copy original response back to response buffer */
8408c2ecf20Sopenharmony_ci		struct mwifiex_ds_misc_cmd *hostcmd;
8418c2ecf20Sopenharmony_ci		uint16_t size = le16_to_cpu(resp->size);
8428c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, INFO,
8438c2ecf20Sopenharmony_ci			    "info: host cmd resp size = %d\n", size);
8448c2ecf20Sopenharmony_ci		size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER);
8458c2ecf20Sopenharmony_ci		if (adapter->curr_cmd->data_buf) {
8468c2ecf20Sopenharmony_ci			hostcmd = adapter->curr_cmd->data_buf;
8478c2ecf20Sopenharmony_ci			hostcmd->len = size;
8488c2ecf20Sopenharmony_ci			memcpy(hostcmd->cmd, resp, size);
8498c2ecf20Sopenharmony_ci		}
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/* Get BSS number and corresponding priv */
8538c2ecf20Sopenharmony_ci	priv = mwifiex_get_priv_by_id(adapter,
8548c2ecf20Sopenharmony_ci			     HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)),
8558c2ecf20Sopenharmony_ci			     HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num)));
8568c2ecf20Sopenharmony_ci	if (!priv)
8578c2ecf20Sopenharmony_ci		priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
8588c2ecf20Sopenharmony_ci	/* Clear RET_BIT from HostCmd */
8598c2ecf20Sopenharmony_ci	resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	cmdresp_no = le16_to_cpu(resp->command);
8628c2ecf20Sopenharmony_ci	cmdresp_result = le16_to_cpu(resp->result);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* Save the last command response to debug log */
8658c2ecf20Sopenharmony_ci	adapter->dbg.last_cmd_resp_index =
8668c2ecf20Sopenharmony_ci			(adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM;
8678c2ecf20Sopenharmony_ci	adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =
8688c2ecf20Sopenharmony_ci								orig_cmdresp_no;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, CMD,
8718c2ecf20Sopenharmony_ci		    "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
8728c2ecf20Sopenharmony_ci		    orig_cmdresp_no, cmdresp_result,
8738c2ecf20Sopenharmony_ci		    le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
8748c2ecf20Sopenharmony_ci	mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp,
8758c2ecf20Sopenharmony_ci			 le16_to_cpu(resp->size));
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {
8788c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n");
8798c2ecf20Sopenharmony_ci		if (adapter->curr_cmd->wait_q_enabled)
8808c2ecf20Sopenharmony_ci			adapter->cmd_wait_q.status = -1;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
8838c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
8848c2ecf20Sopenharmony_ci		adapter->curr_cmd = NULL;
8858c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
8868c2ecf20Sopenharmony_ci		return -1;
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
8908c2ecf20Sopenharmony_ci		adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD;
8918c2ecf20Sopenharmony_ci		if ((cmdresp_result == HostCmd_RESULT_OK) &&
8928c2ecf20Sopenharmony_ci		    (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH))
8938c2ecf20Sopenharmony_ci			ret = mwifiex_ret_802_11_hs_cfg(priv, resp);
8948c2ecf20Sopenharmony_ci	} else {
8958c2ecf20Sopenharmony_ci		/* handle response */
8968c2ecf20Sopenharmony_ci		ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp);
8978c2ecf20Sopenharmony_ci	}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* Check init command response */
9008c2ecf20Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
9018c2ecf20Sopenharmony_ci		if (ret) {
9028c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, ERROR,
9038c2ecf20Sopenharmony_ci				    "%s: cmd %#x failed during\t"
9048c2ecf20Sopenharmony_ci				    "initialization\n", __func__, cmdresp_no);
9058c2ecf20Sopenharmony_ci			mwifiex_init_fw_complete(adapter);
9068c2ecf20Sopenharmony_ci			return -1;
9078c2ecf20Sopenharmony_ci		} else if (adapter->last_init_cmd == cmdresp_no)
9088c2ecf20Sopenharmony_ci			adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE;
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	if (adapter->curr_cmd) {
9128c2ecf20Sopenharmony_ci		if (adapter->curr_cmd->wait_q_enabled)
9138c2ecf20Sopenharmony_ci			adapter->cmd_wait_q.status = ret;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
9188c2ecf20Sopenharmony_ci		adapter->curr_cmd = NULL;
9198c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	return ret;
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci/*
9268c2ecf20Sopenharmony_ci * This function handles the timeout of command sending.
9278c2ecf20Sopenharmony_ci *
9288c2ecf20Sopenharmony_ci * It will re-send the same command again.
9298c2ecf20Sopenharmony_ci */
9308c2ecf20Sopenharmony_civoid
9318c2ecf20Sopenharmony_cimwifiex_cmd_timeout_func(struct timer_list *t)
9328c2ecf20Sopenharmony_ci{
9338c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = from_timer(adapter, t, cmd_timer);
9348c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	set_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);
9378c2ecf20Sopenharmony_ci	if (!adapter->curr_cmd) {
9388c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
9398c2ecf20Sopenharmony_ci			    "cmd: empty curr_cmd\n");
9408c2ecf20Sopenharmony_ci		return;
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci	cmd_node = adapter->curr_cmd;
9438c2ecf20Sopenharmony_ci	if (cmd_node) {
9448c2ecf20Sopenharmony_ci		adapter->dbg.timeout_cmd_id =
9458c2ecf20Sopenharmony_ci			adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];
9468c2ecf20Sopenharmony_ci		adapter->dbg.timeout_cmd_act =
9478c2ecf20Sopenharmony_ci			adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];
9488c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9498c2ecf20Sopenharmony_ci			    "%s: Timeout cmd id = %#x, act = %#x\n", __func__,
9508c2ecf20Sopenharmony_ci			    adapter->dbg.timeout_cmd_id,
9518c2ecf20Sopenharmony_ci			    adapter->dbg.timeout_cmd_act);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9548c2ecf20Sopenharmony_ci			    "num_data_h2c_failure = %d\n",
9558c2ecf20Sopenharmony_ci			    adapter->dbg.num_tx_host_to_card_failure);
9568c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9578c2ecf20Sopenharmony_ci			    "num_cmd_h2c_failure = %d\n",
9588c2ecf20Sopenharmony_ci			    adapter->dbg.num_cmd_host_to_card_failure);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9618c2ecf20Sopenharmony_ci			    "is_cmd_timedout = %d\n",
9628c2ecf20Sopenharmony_ci			    test_bit(MWIFIEX_IS_CMD_TIMEDOUT,
9638c2ecf20Sopenharmony_ci				     &adapter->work_flags));
9648c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9658c2ecf20Sopenharmony_ci			    "num_tx_timeout = %d\n",
9668c2ecf20Sopenharmony_ci			    adapter->dbg.num_tx_timeout);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9698c2ecf20Sopenharmony_ci			    "last_cmd_index = %d\n",
9708c2ecf20Sopenharmony_ci			    adapter->dbg.last_cmd_index);
9718c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9728c2ecf20Sopenharmony_ci			    "last_cmd_id: %*ph\n",
9738c2ecf20Sopenharmony_ci			    (int)sizeof(adapter->dbg.last_cmd_id),
9748c2ecf20Sopenharmony_ci			    adapter->dbg.last_cmd_id);
9758c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9768c2ecf20Sopenharmony_ci			    "last_cmd_act: %*ph\n",
9778c2ecf20Sopenharmony_ci			    (int)sizeof(adapter->dbg.last_cmd_act),
9788c2ecf20Sopenharmony_ci			    adapter->dbg.last_cmd_act);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9818c2ecf20Sopenharmony_ci			    "last_cmd_resp_index = %d\n",
9828c2ecf20Sopenharmony_ci			    adapter->dbg.last_cmd_resp_index);
9838c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9848c2ecf20Sopenharmony_ci			    "last_cmd_resp_id: %*ph\n",
9858c2ecf20Sopenharmony_ci			    (int)sizeof(adapter->dbg.last_cmd_resp_id),
9868c2ecf20Sopenharmony_ci			    adapter->dbg.last_cmd_resp_id);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9898c2ecf20Sopenharmony_ci			    "last_event_index = %d\n",
9908c2ecf20Sopenharmony_ci			    adapter->dbg.last_event_index);
9918c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9928c2ecf20Sopenharmony_ci			    "last_event: %*ph\n",
9938c2ecf20Sopenharmony_ci			    (int)sizeof(adapter->dbg.last_event),
9948c2ecf20Sopenharmony_ci			    adapter->dbg.last_event);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
9978c2ecf20Sopenharmony_ci			    "data_sent=%d cmd_sent=%d\n",
9988c2ecf20Sopenharmony_ci			    adapter->data_sent, adapter->cmd_sent);
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, MSG,
10018c2ecf20Sopenharmony_ci			    "ps_mode=%d ps_state=%d\n",
10028c2ecf20Sopenharmony_ci			    adapter->ps_mode, adapter->ps_state);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		if (cmd_node->wait_q_enabled) {
10058c2ecf20Sopenharmony_ci			adapter->cmd_wait_q.status = -ETIMEDOUT;
10068c2ecf20Sopenharmony_ci			mwifiex_cancel_pending_ioctl(adapter);
10078c2ecf20Sopenharmony_ci		}
10088c2ecf20Sopenharmony_ci	}
10098c2ecf20Sopenharmony_ci	if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
10108c2ecf20Sopenharmony_ci		mwifiex_init_fw_complete(adapter);
10118c2ecf20Sopenharmony_ci		return;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (adapter->if_ops.device_dump)
10158c2ecf20Sopenharmony_ci		adapter->if_ops.device_dump(adapter);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (adapter->if_ops.card_reset)
10188c2ecf20Sopenharmony_ci		adapter->if_ops.card_reset(adapter);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_civoid
10228c2ecf20Sopenharmony_cimwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* Cancel all pending scan command */
10278c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->scan_pending_q_lock);
10288c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd_node, tmp_node,
10298c2ecf20Sopenharmony_ci				 &adapter->scan_pending_q, list) {
10308c2ecf20Sopenharmony_ci		list_del(&cmd_node->list);
10318c2ecf20Sopenharmony_ci		cmd_node->wait_q_enabled = false;
10328c2ecf20Sopenharmony_ci		mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->scan_pending_q_lock);
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/*
10388c2ecf20Sopenharmony_ci * This function cancels all the pending commands.
10398c2ecf20Sopenharmony_ci *
10408c2ecf20Sopenharmony_ci * The current command, all commands in command pending queue and all scan
10418c2ecf20Sopenharmony_ci * commands in scan pending queue are cancelled. All the completion callbacks
10428c2ecf20Sopenharmony_ci * are called with failure status to ensure cleanup.
10438c2ecf20Sopenharmony_ci */
10448c2ecf20Sopenharmony_civoid
10458c2ecf20Sopenharmony_cimwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->mwifiex_cmd_lock);
10508c2ecf20Sopenharmony_ci	/* Cancel current cmd */
10518c2ecf20Sopenharmony_ci	if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) {
10528c2ecf20Sopenharmony_ci		adapter->cmd_wait_q.status = -1;
10538c2ecf20Sopenharmony_ci		mwifiex_complete_cmd(adapter, adapter->curr_cmd);
10548c2ecf20Sopenharmony_ci		adapter->curr_cmd->wait_q_enabled = false;
10558c2ecf20Sopenharmony_ci		/* no recycle probably wait for response */
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci	/* Cancel all pending command */
10588c2ecf20Sopenharmony_ci	spin_lock_bh(&adapter->cmd_pending_q_lock);
10598c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd_node, tmp_node,
10608c2ecf20Sopenharmony_ci				 &adapter->cmd_pending_q, list) {
10618c2ecf20Sopenharmony_ci		list_del(&cmd_node->list);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci		if (cmd_node->wait_q_enabled)
10648c2ecf20Sopenharmony_ci			adapter->cmd_wait_q.status = -1;
10658c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, cmd_node);
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->cmd_pending_q_lock);
10688c2ecf20Sopenharmony_ci	spin_unlock_bh(&adapter->mwifiex_cmd_lock);
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	mwifiex_cancel_scan(adapter);
10718c2ecf20Sopenharmony_ci}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci/*
10748c2ecf20Sopenharmony_ci * This function cancels all pending commands that matches with
10758c2ecf20Sopenharmony_ci * the given IOCTL request.
10768c2ecf20Sopenharmony_ci *
10778c2ecf20Sopenharmony_ci * Both the current command buffer and the pending command queue are
10788c2ecf20Sopenharmony_ci * searched for matching IOCTL request. The completion callback of
10798c2ecf20Sopenharmony_ci * the matched command is called with failure status to ensure cleanup.
10808c2ecf20Sopenharmony_ci * In case of scan commands, all pending commands in scan pending queue
10818c2ecf20Sopenharmony_ci * are cancelled.
10828c2ecf20Sopenharmony_ci */
10838c2ecf20Sopenharmony_cistatic void
10848c2ecf20Sopenharmony_cimwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct cmd_ctrl_node *cmd_node = NULL;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if ((adapter->curr_cmd) &&
10898c2ecf20Sopenharmony_ci	    (adapter->curr_cmd->wait_q_enabled)) {
10908c2ecf20Sopenharmony_ci		spin_lock_bh(&adapter->mwifiex_cmd_lock);
10918c2ecf20Sopenharmony_ci		cmd_node = adapter->curr_cmd;
10928c2ecf20Sopenharmony_ci		/* setting curr_cmd to NULL is quite dangerous, because
10938c2ecf20Sopenharmony_ci		 * mwifiex_process_cmdresp checks curr_cmd to be != NULL
10948c2ecf20Sopenharmony_ci		 * at the beginning then relies on it and dereferences
10958c2ecf20Sopenharmony_ci		 * it at will
10968c2ecf20Sopenharmony_ci		 * this probably works since mwifiex_cmd_timeout_func
10978c2ecf20Sopenharmony_ci		 * is the only caller of this function and responses
10988c2ecf20Sopenharmony_ci		 * at that point
10998c2ecf20Sopenharmony_ci		 */
11008c2ecf20Sopenharmony_ci		adapter->curr_cmd = NULL;
11018c2ecf20Sopenharmony_ci		spin_unlock_bh(&adapter->mwifiex_cmd_lock);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci		mwifiex_recycle_cmd_node(adapter, cmd_node);
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	mwifiex_cancel_scan(adapter);
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci/*
11108c2ecf20Sopenharmony_ci * This function sends the sleep confirm command to firmware, if
11118c2ecf20Sopenharmony_ci * possible.
11128c2ecf20Sopenharmony_ci *
11138c2ecf20Sopenharmony_ci * The sleep confirm command cannot be issued if command response,
11148c2ecf20Sopenharmony_ci * data response or event response is awaiting handling, or if we
11158c2ecf20Sopenharmony_ci * are in the middle of sending a command, or expecting a command
11168c2ecf20Sopenharmony_ci * response.
11178c2ecf20Sopenharmony_ci */
11188c2ecf20Sopenharmony_civoid
11198c2ecf20Sopenharmony_cimwifiex_check_ps_cond(struct mwifiex_adapter *adapter)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	if (!adapter->cmd_sent && !atomic_read(&adapter->tx_hw_pending) &&
11228c2ecf20Sopenharmony_ci	    !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter))
11238c2ecf20Sopenharmony_ci		mwifiex_dnld_sleep_confirm_cmd(adapter);
11248c2ecf20Sopenharmony_ci	else
11258c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, CMD,
11268c2ecf20Sopenharmony_ci			    "cmd: Delay Sleep Confirm (%s%s%s%s)\n",
11278c2ecf20Sopenharmony_ci			    (adapter->cmd_sent) ? "D" : "",
11288c2ecf20Sopenharmony_ci			    atomic_read(&adapter->tx_hw_pending) ? "T" : "",
11298c2ecf20Sopenharmony_ci			    (adapter->curr_cmd) ? "C" : "",
11308c2ecf20Sopenharmony_ci			    (IS_CARD_RX_RCVD(adapter)) ? "R" : "");
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci/*
11348c2ecf20Sopenharmony_ci * This function sends a Host Sleep activated event to applications.
11358c2ecf20Sopenharmony_ci *
11368c2ecf20Sopenharmony_ci * This event is generated by the driver, with a blank event body.
11378c2ecf20Sopenharmony_ci */
11388c2ecf20Sopenharmony_civoid
11398c2ecf20Sopenharmony_cimwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated)
11408c2ecf20Sopenharmony_ci{
11418c2ecf20Sopenharmony_ci	if (activated) {
11428c2ecf20Sopenharmony_ci		if (test_bit(MWIFIEX_IS_HS_CONFIGURED,
11438c2ecf20Sopenharmony_ci			     &priv->adapter->work_flags)) {
11448c2ecf20Sopenharmony_ci			priv->adapter->hs_activated = true;
11458c2ecf20Sopenharmony_ci			mwifiex_update_rxreor_flags(priv->adapter,
11468c2ecf20Sopenharmony_ci						    RXREOR_FORCE_NO_DROP);
11478c2ecf20Sopenharmony_ci			mwifiex_dbg(priv->adapter, EVENT,
11488c2ecf20Sopenharmony_ci				    "event: hs_activated\n");
11498c2ecf20Sopenharmony_ci			priv->adapter->hs_activate_wait_q_woken = true;
11508c2ecf20Sopenharmony_ci			wake_up_interruptible(
11518c2ecf20Sopenharmony_ci				&priv->adapter->hs_activate_wait_q);
11528c2ecf20Sopenharmony_ci		} else {
11538c2ecf20Sopenharmony_ci			mwifiex_dbg(priv->adapter, EVENT,
11548c2ecf20Sopenharmony_ci				    "event: HS not configured\n");
11558c2ecf20Sopenharmony_ci		}
11568c2ecf20Sopenharmony_ci	} else {
11578c2ecf20Sopenharmony_ci		mwifiex_dbg(priv->adapter, EVENT,
11588c2ecf20Sopenharmony_ci			    "event: hs_deactivated\n");
11598c2ecf20Sopenharmony_ci		priv->adapter->hs_activated = false;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci/*
11648c2ecf20Sopenharmony_ci * This function handles the command response of a Host Sleep configuration
11658c2ecf20Sopenharmony_ci * command.
11668c2ecf20Sopenharmony_ci *
11678c2ecf20Sopenharmony_ci * Handling includes changing the header fields into CPU format
11688c2ecf20Sopenharmony_ci * and setting the current host sleep activation status in driver.
11698c2ecf20Sopenharmony_ci *
11708c2ecf20Sopenharmony_ci * In case host sleep status change, the function generates an event to
11718c2ecf20Sopenharmony_ci * notify the applications.
11728c2ecf20Sopenharmony_ci */
11738c2ecf20Sopenharmony_ciint mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
11748c2ecf20Sopenharmony_ci			      struct host_cmd_ds_command *resp)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
11778c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =
11788c2ecf20Sopenharmony_ci		&resp->params.opt_hs_cfg;
11798c2ecf20Sopenharmony_ci	uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&
11828c2ecf20Sopenharmony_ci	    adapter->iface_type != MWIFIEX_USB) {
11838c2ecf20Sopenharmony_ci		mwifiex_hs_activated_event(priv, true);
11848c2ecf20Sopenharmony_ci		return 0;
11858c2ecf20Sopenharmony_ci	} else {
11868c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, CMD,
11878c2ecf20Sopenharmony_ci			    "cmd: CMD_RESP: HS_CFG cmd reply\t"
11888c2ecf20Sopenharmony_ci			    " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n",
11898c2ecf20Sopenharmony_ci			    resp->result, conditions,
11908c2ecf20Sopenharmony_ci			    phs_cfg->params.hs_config.gpio,
11918c2ecf20Sopenharmony_ci			    phs_cfg->params.hs_config.gap);
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci	if (conditions != HS_CFG_CANCEL) {
11948c2ecf20Sopenharmony_ci		set_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
11958c2ecf20Sopenharmony_ci		if (adapter->iface_type == MWIFIEX_USB)
11968c2ecf20Sopenharmony_ci			mwifiex_hs_activated_event(priv, true);
11978c2ecf20Sopenharmony_ci	} else {
11988c2ecf20Sopenharmony_ci		clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
11998c2ecf20Sopenharmony_ci		if (adapter->hs_activated)
12008c2ecf20Sopenharmony_ci			mwifiex_hs_activated_event(priv, false);
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	return 0;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci/*
12078c2ecf20Sopenharmony_ci * This function wakes up the adapter and generates a Host Sleep
12088c2ecf20Sopenharmony_ci * cancel event on receiving the power up interrupt.
12098c2ecf20Sopenharmony_ci */
12108c2ecf20Sopenharmony_civoid
12118c2ecf20Sopenharmony_cimwifiex_process_hs_config(struct mwifiex_adapter *adapter)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
12148c2ecf20Sopenharmony_ci		    "info: %s: auto cancelling host sleep\t"
12158c2ecf20Sopenharmony_ci		    "since there is interrupt from the firmware\n",
12168c2ecf20Sopenharmony_ci		    __func__);
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	adapter->if_ops.wakeup(adapter);
12198c2ecf20Sopenharmony_ci	adapter->hs_activated = false;
12208c2ecf20Sopenharmony_ci	clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);
12218c2ecf20Sopenharmony_ci	clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
12228c2ecf20Sopenharmony_ci	mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
12238c2ecf20Sopenharmony_ci						    MWIFIEX_BSS_ROLE_ANY),
12248c2ecf20Sopenharmony_ci				   false);
12258c2ecf20Sopenharmony_ci}
12268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_process_hs_config);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci/*
12298c2ecf20Sopenharmony_ci * This function handles the command response of a sleep confirm command.
12308c2ecf20Sopenharmony_ci *
12318c2ecf20Sopenharmony_ci * The function sets the card state to SLEEP if the response indicates success.
12328c2ecf20Sopenharmony_ci */
12338c2ecf20Sopenharmony_civoid
12348c2ecf20Sopenharmony_cimwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
12358c2ecf20Sopenharmony_ci				   u8 *pbuf, u32 upld_len)
12368c2ecf20Sopenharmony_ci{
12378c2ecf20Sopenharmony_ci	struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf;
12388c2ecf20Sopenharmony_ci	struct mwifiex_private *priv =
12398c2ecf20Sopenharmony_ci		mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
12408c2ecf20Sopenharmony_ci	uint16_t result = le16_to_cpu(cmd->result);
12418c2ecf20Sopenharmony_ci	uint16_t command = le16_to_cpu(cmd->command);
12428c2ecf20Sopenharmony_ci	uint16_t seq_num = le16_to_cpu(cmd->seq_num);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	if (!upld_len) {
12458c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
12468c2ecf20Sopenharmony_ci			    "%s: cmd size is 0\n", __func__);
12478c2ecf20Sopenharmony_ci		return;
12488c2ecf20Sopenharmony_ci	}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, CMD,
12518c2ecf20Sopenharmony_ci		    "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
12528c2ecf20Sopenharmony_ci		    command, result, le16_to_cpu(cmd->size), seq_num);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	/* Get BSS number and corresponding priv */
12558c2ecf20Sopenharmony_ci	priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
12568c2ecf20Sopenharmony_ci				      HostCmd_GET_BSS_TYPE(seq_num));
12578c2ecf20Sopenharmony_ci	if (!priv)
12588c2ecf20Sopenharmony_ci		priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/* Update sequence number */
12618c2ecf20Sopenharmony_ci	seq_num = HostCmd_GET_SEQ_NO(seq_num);
12628c2ecf20Sopenharmony_ci	/* Clear RET_BIT from HostCmd */
12638c2ecf20Sopenharmony_ci	command &= HostCmd_CMD_ID_MASK;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {
12668c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
12678c2ecf20Sopenharmony_ci			    "%s: rcvd unexpected resp for cmd %#x, result = %x\n",
12688c2ecf20Sopenharmony_ci			    __func__, command, result);
12698c2ecf20Sopenharmony_ci		return;
12708c2ecf20Sopenharmony_ci	}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	if (result) {
12738c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, ERROR,
12748c2ecf20Sopenharmony_ci			    "%s: sleep confirm cmd failed\n",
12758c2ecf20Sopenharmony_ci			    __func__);
12768c2ecf20Sopenharmony_ci		adapter->pm_wakeup_card_req = false;
12778c2ecf20Sopenharmony_ci		adapter->ps_state = PS_STATE_AWAKE;
12788c2ecf20Sopenharmony_ci		return;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci	adapter->pm_wakeup_card_req = true;
12818c2ecf20Sopenharmony_ci	if (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags))
12828c2ecf20Sopenharmony_ci		mwifiex_hs_activated_event(mwifiex_get_priv
12838c2ecf20Sopenharmony_ci						(adapter, MWIFIEX_BSS_ROLE_ANY),
12848c2ecf20Sopenharmony_ci					   true);
12858c2ecf20Sopenharmony_ci	adapter->ps_state = PS_STATE_SLEEP;
12868c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(command);
12878c2ecf20Sopenharmony_ci	cmd->seq_num = cpu_to_le16(seq_num);
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci/*
12928c2ecf20Sopenharmony_ci * This function prepares an enhanced power mode command.
12938c2ecf20Sopenharmony_ci *
12948c2ecf20Sopenharmony_ci * This function can be used to disable power save or to configure
12958c2ecf20Sopenharmony_ci * power save with auto PS or STA PS or auto deep sleep.
12968c2ecf20Sopenharmony_ci *
12978c2ecf20Sopenharmony_ci * Preparation includes -
12988c2ecf20Sopenharmony_ci *      - Setting command ID, action and proper size
12998c2ecf20Sopenharmony_ci *      - Setting Power Save bitmap, PS parameters TLV, PS mode TLV,
13008c2ecf20Sopenharmony_ci *        auto deep sleep TLV (as required)
13018c2ecf20Sopenharmony_ci *      - Ensuring correct endian-ness
13028c2ecf20Sopenharmony_ci */
13038c2ecf20Sopenharmony_ciint mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
13048c2ecf20Sopenharmony_ci			       struct host_cmd_ds_command *cmd,
13058c2ecf20Sopenharmony_ci			       u16 cmd_action, uint16_t ps_bitmap,
13068c2ecf20Sopenharmony_ci			       struct mwifiex_ds_auto_ds *auto_ds)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =
13098c2ecf20Sopenharmony_ci		&cmd->params.psmode_enh;
13108c2ecf20Sopenharmony_ci	u8 *tlv;
13118c2ecf20Sopenharmony_ci	u16 cmd_size = 0;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
13148c2ecf20Sopenharmony_ci	if (cmd_action == DIS_AUTO_PS) {
13158c2ecf20Sopenharmony_ci		psmode_enh->action = cpu_to_le16(DIS_AUTO_PS);
13168c2ecf20Sopenharmony_ci		psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
13178c2ecf20Sopenharmony_ci		cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
13188c2ecf20Sopenharmony_ci					sizeof(psmode_enh->params.ps_bitmap));
13198c2ecf20Sopenharmony_ci	} else if (cmd_action == GET_PS) {
13208c2ecf20Sopenharmony_ci		psmode_enh->action = cpu_to_le16(GET_PS);
13218c2ecf20Sopenharmony_ci		psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
13228c2ecf20Sopenharmony_ci		cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
13238c2ecf20Sopenharmony_ci					sizeof(psmode_enh->params.ps_bitmap));
13248c2ecf20Sopenharmony_ci	} else if (cmd_action == EN_AUTO_PS) {
13258c2ecf20Sopenharmony_ci		psmode_enh->action = cpu_to_le16(EN_AUTO_PS);
13268c2ecf20Sopenharmony_ci		psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
13278c2ecf20Sopenharmony_ci		cmd_size = S_DS_GEN + sizeof(psmode_enh->action) +
13288c2ecf20Sopenharmony_ci					sizeof(psmode_enh->params.ps_bitmap);
13298c2ecf20Sopenharmony_ci		tlv = (u8 *) cmd + cmd_size;
13308c2ecf20Sopenharmony_ci		if (ps_bitmap & BITMAP_STA_PS) {
13318c2ecf20Sopenharmony_ci			struct mwifiex_adapter *adapter = priv->adapter;
13328c2ecf20Sopenharmony_ci			struct mwifiex_ie_types_ps_param *ps_tlv =
13338c2ecf20Sopenharmony_ci				(struct mwifiex_ie_types_ps_param *) tlv;
13348c2ecf20Sopenharmony_ci			struct mwifiex_ps_param *ps_mode = &ps_tlv->param;
13358c2ecf20Sopenharmony_ci			ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM);
13368c2ecf20Sopenharmony_ci			ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) -
13378c2ecf20Sopenharmony_ci					sizeof(struct mwifiex_ie_types_header));
13388c2ecf20Sopenharmony_ci			cmd_size += sizeof(*ps_tlv);
13398c2ecf20Sopenharmony_ci			tlv += sizeof(*ps_tlv);
13408c2ecf20Sopenharmony_ci			mwifiex_dbg(priv->adapter, CMD,
13418c2ecf20Sopenharmony_ci				    "cmd: PS Command: Enter PS\n");
13428c2ecf20Sopenharmony_ci			ps_mode->null_pkt_interval =
13438c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->null_pkt_interval);
13448c2ecf20Sopenharmony_ci			ps_mode->multiple_dtims =
13458c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->multiple_dtim);
13468c2ecf20Sopenharmony_ci			ps_mode->bcn_miss_timeout =
13478c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->bcn_miss_time_out);
13488c2ecf20Sopenharmony_ci			ps_mode->local_listen_interval =
13498c2ecf20Sopenharmony_ci				cpu_to_le16(adapter->local_listen_interval);
13508c2ecf20Sopenharmony_ci			ps_mode->adhoc_wake_period =
13518c2ecf20Sopenharmony_ci				cpu_to_le16(adapter->adhoc_awake_period);
13528c2ecf20Sopenharmony_ci			ps_mode->delay_to_ps =
13538c2ecf20Sopenharmony_ci					cpu_to_le16(adapter->delay_to_ps);
13548c2ecf20Sopenharmony_ci			ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode);
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci		}
13578c2ecf20Sopenharmony_ci		if (ps_bitmap & BITMAP_AUTO_DS) {
13588c2ecf20Sopenharmony_ci			struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv =
13598c2ecf20Sopenharmony_ci				(struct mwifiex_ie_types_auto_ds_param *) tlv;
13608c2ecf20Sopenharmony_ci			u16 idletime = 0;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci			auto_ds_tlv->header.type =
13638c2ecf20Sopenharmony_ci				cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM);
13648c2ecf20Sopenharmony_ci			auto_ds_tlv->header.len =
13658c2ecf20Sopenharmony_ci				cpu_to_le16(sizeof(*auto_ds_tlv) -
13668c2ecf20Sopenharmony_ci					sizeof(struct mwifiex_ie_types_header));
13678c2ecf20Sopenharmony_ci			cmd_size += sizeof(*auto_ds_tlv);
13688c2ecf20Sopenharmony_ci			tlv += sizeof(*auto_ds_tlv);
13698c2ecf20Sopenharmony_ci			if (auto_ds)
13708c2ecf20Sopenharmony_ci				idletime = auto_ds->idle_time;
13718c2ecf20Sopenharmony_ci			mwifiex_dbg(priv->adapter, CMD,
13728c2ecf20Sopenharmony_ci				    "cmd: PS Command: Enter Auto Deep Sleep\n");
13738c2ecf20Sopenharmony_ci			auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime);
13748c2ecf20Sopenharmony_ci		}
13758c2ecf20Sopenharmony_ci		cmd->size = cpu_to_le16(cmd_size);
13768c2ecf20Sopenharmony_ci	}
13778c2ecf20Sopenharmony_ci	return 0;
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci/*
13818c2ecf20Sopenharmony_ci * This function handles the command response of an enhanced power mode
13828c2ecf20Sopenharmony_ci * command.
13838c2ecf20Sopenharmony_ci *
13848c2ecf20Sopenharmony_ci * Handling includes changing the header fields into CPU format
13858c2ecf20Sopenharmony_ci * and setting the current enhanced power mode in driver.
13868c2ecf20Sopenharmony_ci */
13878c2ecf20Sopenharmony_ciint mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
13888c2ecf20Sopenharmony_ci			       struct host_cmd_ds_command *resp,
13898c2ecf20Sopenharmony_ci			       struct mwifiex_ds_pm_cfg *pm_cfg)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
13928c2ecf20Sopenharmony_ci	struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =
13938c2ecf20Sopenharmony_ci		&resp->params.psmode_enh;
13948c2ecf20Sopenharmony_ci	uint16_t action = le16_to_cpu(ps_mode->action);
13958c2ecf20Sopenharmony_ci	uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap);
13968c2ecf20Sopenharmony_ci	uint16_t auto_ps_bitmap =
13978c2ecf20Sopenharmony_ci		le16_to_cpu(ps_mode->params.ps_bitmap);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
14008c2ecf20Sopenharmony_ci		    "info: %s: PS_MODE cmd reply result=%#x action=%#X\n",
14018c2ecf20Sopenharmony_ci		    __func__, resp->result, action);
14028c2ecf20Sopenharmony_ci	if (action == EN_AUTO_PS) {
14038c2ecf20Sopenharmony_ci		if (auto_ps_bitmap & BITMAP_AUTO_DS) {
14048c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, CMD,
14058c2ecf20Sopenharmony_ci				    "cmd: Enabled auto deep sleep\n");
14068c2ecf20Sopenharmony_ci			priv->adapter->is_deep_sleep = true;
14078c2ecf20Sopenharmony_ci		}
14088c2ecf20Sopenharmony_ci		if (auto_ps_bitmap & BITMAP_STA_PS) {
14098c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, CMD,
14108c2ecf20Sopenharmony_ci				    "cmd: Enabled STA power save\n");
14118c2ecf20Sopenharmony_ci			if (adapter->sleep_period.period)
14128c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, CMD,
14138c2ecf20Sopenharmony_ci					    "cmd: set to uapsd/pps mode\n");
14148c2ecf20Sopenharmony_ci		}
14158c2ecf20Sopenharmony_ci	} else if (action == DIS_AUTO_PS) {
14168c2ecf20Sopenharmony_ci		if (ps_bitmap & BITMAP_AUTO_DS) {
14178c2ecf20Sopenharmony_ci			priv->adapter->is_deep_sleep = false;
14188c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, CMD,
14198c2ecf20Sopenharmony_ci				    "cmd: Disabled auto deep sleep\n");
14208c2ecf20Sopenharmony_ci		}
14218c2ecf20Sopenharmony_ci		if (ps_bitmap & BITMAP_STA_PS) {
14228c2ecf20Sopenharmony_ci			mwifiex_dbg(adapter, CMD,
14238c2ecf20Sopenharmony_ci				    "cmd: Disabled STA power save\n");
14248c2ecf20Sopenharmony_ci			if (adapter->sleep_period.period) {
14258c2ecf20Sopenharmony_ci				adapter->delay_null_pkt = false;
14268c2ecf20Sopenharmony_ci				adapter->tx_lock_flag = false;
14278c2ecf20Sopenharmony_ci				adapter->pps_uapsd_mode = false;
14288c2ecf20Sopenharmony_ci			}
14298c2ecf20Sopenharmony_ci		}
14308c2ecf20Sopenharmony_ci	} else if (action == GET_PS) {
14318c2ecf20Sopenharmony_ci		if (ps_bitmap & BITMAP_STA_PS)
14328c2ecf20Sopenharmony_ci			adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
14338c2ecf20Sopenharmony_ci		else
14348c2ecf20Sopenharmony_ci			adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, CMD,
14378c2ecf20Sopenharmony_ci			    "cmd: ps_bitmap=%#x\n", ps_bitmap);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci		if (pm_cfg) {
14408c2ecf20Sopenharmony_ci			/* This section is for get power save mode */
14418c2ecf20Sopenharmony_ci			if (ps_bitmap & BITMAP_STA_PS)
14428c2ecf20Sopenharmony_ci				pm_cfg->param.ps_mode = 1;
14438c2ecf20Sopenharmony_ci			else
14448c2ecf20Sopenharmony_ci				pm_cfg->param.ps_mode = 0;
14458c2ecf20Sopenharmony_ci		}
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci	return 0;
14488c2ecf20Sopenharmony_ci}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci/*
14518c2ecf20Sopenharmony_ci * This function prepares command to get hardware specifications.
14528c2ecf20Sopenharmony_ci *
14538c2ecf20Sopenharmony_ci * Preparation includes -
14548c2ecf20Sopenharmony_ci *      - Setting command ID, action and proper size
14558c2ecf20Sopenharmony_ci *      - Setting permanent address parameter
14568c2ecf20Sopenharmony_ci *      - Ensuring correct endian-ness
14578c2ecf20Sopenharmony_ci */
14588c2ecf20Sopenharmony_ciint mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
14598c2ecf20Sopenharmony_ci			    struct host_cmd_ds_command *cmd)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC);
14648c2ecf20Sopenharmony_ci	cmd->size =
14658c2ecf20Sopenharmony_ci		cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN);
14668c2ecf20Sopenharmony_ci	memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	return 0;
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci/*
14728c2ecf20Sopenharmony_ci * This function handles the command response of get hardware
14738c2ecf20Sopenharmony_ci * specifications.
14748c2ecf20Sopenharmony_ci *
14758c2ecf20Sopenharmony_ci * Handling includes changing the header fields into CPU format
14768c2ecf20Sopenharmony_ci * and saving/updating the following parameters in driver -
14778c2ecf20Sopenharmony_ci *      - Firmware capability information
14788c2ecf20Sopenharmony_ci *      - Firmware band settings
14798c2ecf20Sopenharmony_ci *      - Ad-hoc start band and channel
14808c2ecf20Sopenharmony_ci *      - Ad-hoc 11n activation status
14818c2ecf20Sopenharmony_ci *      - Firmware release number
14828c2ecf20Sopenharmony_ci *      - Number of antennas
14838c2ecf20Sopenharmony_ci *      - Hardware address
14848c2ecf20Sopenharmony_ci *      - Hardware interface version
14858c2ecf20Sopenharmony_ci *      - Firmware version
14868c2ecf20Sopenharmony_ci *      - Region code
14878c2ecf20Sopenharmony_ci *      - 11n capabilities
14888c2ecf20Sopenharmony_ci *      - MCS support fields
14898c2ecf20Sopenharmony_ci *      - MP end port
14908c2ecf20Sopenharmony_ci */
14918c2ecf20Sopenharmony_ciint mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
14928c2ecf20Sopenharmony_ci			    struct host_cmd_ds_command *resp)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
14958c2ecf20Sopenharmony_ci	struct mwifiex_adapter *adapter = priv->adapter;
14968c2ecf20Sopenharmony_ci	struct mwifiex_ie_types_header *tlv;
14978c2ecf20Sopenharmony_ci	struct hw_spec_api_rev *api_rev;
14988c2ecf20Sopenharmony_ci	struct hw_spec_max_conn *max_conn;
14998c2ecf20Sopenharmony_ci	u16 resp_size, api_id;
15008c2ecf20Sopenharmony_ci	int i, left_len, parsed_len = 0;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	if (IS_SUPPORT_MULTI_BANDS(adapter))
15058c2ecf20Sopenharmony_ci		adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter);
15068c2ecf20Sopenharmony_ci	else
15078c2ecf20Sopenharmony_ci		adapter->fw_bands = BAND_B;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	adapter->config_bands = adapter->fw_bands;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	if (adapter->fw_bands & BAND_A) {
15128c2ecf20Sopenharmony_ci		if (adapter->fw_bands & BAND_GN) {
15138c2ecf20Sopenharmony_ci			adapter->config_bands |= BAND_AN;
15148c2ecf20Sopenharmony_ci			adapter->fw_bands |= BAND_AN;
15158c2ecf20Sopenharmony_ci		}
15168c2ecf20Sopenharmony_ci		if (adapter->fw_bands & BAND_AN) {
15178c2ecf20Sopenharmony_ci			adapter->adhoc_start_band = BAND_A | BAND_AN;
15188c2ecf20Sopenharmony_ci			adapter->adhoc_11n_enabled = true;
15198c2ecf20Sopenharmony_ci		} else {
15208c2ecf20Sopenharmony_ci			adapter->adhoc_start_band = BAND_A;
15218c2ecf20Sopenharmony_ci		}
15228c2ecf20Sopenharmony_ci		priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A;
15238c2ecf20Sopenharmony_ci	} else if (adapter->fw_bands & BAND_GN) {
15248c2ecf20Sopenharmony_ci		adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN;
15258c2ecf20Sopenharmony_ci		priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
15268c2ecf20Sopenharmony_ci		adapter->adhoc_11n_enabled = true;
15278c2ecf20Sopenharmony_ci	} else if (adapter->fw_bands & BAND_G) {
15288c2ecf20Sopenharmony_ci		adapter->adhoc_start_band = BAND_G | BAND_B;
15298c2ecf20Sopenharmony_ci		priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
15308c2ecf20Sopenharmony_ci	} else if (adapter->fw_bands & BAND_B) {
15318c2ecf20Sopenharmony_ci		adapter->adhoc_start_band = BAND_B;
15328c2ecf20Sopenharmony_ci		priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
15338c2ecf20Sopenharmony_ci	}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
15368c2ecf20Sopenharmony_ci	adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;
15378c2ecf20Sopenharmony_ci	adapter->number_of_antenna =
15388c2ecf20Sopenharmony_ci			le16_to_cpu(hw_spec->number_of_antenna) & 0xf;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
15418c2ecf20Sopenharmony_ci		adapter->is_hw_11ac_capable = true;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci		/* Copy 11AC cap */
15448c2ecf20Sopenharmony_ci		adapter->hw_dot_11ac_dev_cap =
15458c2ecf20Sopenharmony_ci					le32_to_cpu(hw_spec->dot_11ac_dev_cap);
15468c2ecf20Sopenharmony_ci		adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap
15478c2ecf20Sopenharmony_ci					& ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
15488c2ecf20Sopenharmony_ci		adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap
15498c2ecf20Sopenharmony_ci					& ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci		/* Copy 11AC mcs */
15528c2ecf20Sopenharmony_ci		adapter->hw_dot_11ac_mcs_support =
15538c2ecf20Sopenharmony_ci				le32_to_cpu(hw_spec->dot_11ac_mcs_support);
15548c2ecf20Sopenharmony_ci		adapter->usr_dot_11ac_mcs_support =
15558c2ecf20Sopenharmony_ci					adapter->hw_dot_11ac_mcs_support;
15568c2ecf20Sopenharmony_ci	} else {
15578c2ecf20Sopenharmony_ci		adapter->is_hw_11ac_capable = false;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	resp_size = le16_to_cpu(resp->size) - S_DS_GEN;
15618c2ecf20Sopenharmony_ci	if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {
15628c2ecf20Sopenharmony_ci		/* we have variable HW SPEC information */
15638c2ecf20Sopenharmony_ci		left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);
15648c2ecf20Sopenharmony_ci		while (left_len > sizeof(struct mwifiex_ie_types_header)) {
15658c2ecf20Sopenharmony_ci			tlv = (void *)&hw_spec->tlvs + parsed_len;
15668c2ecf20Sopenharmony_ci			switch (le16_to_cpu(tlv->type)) {
15678c2ecf20Sopenharmony_ci			case TLV_TYPE_API_REV:
15688c2ecf20Sopenharmony_ci				api_rev = (struct hw_spec_api_rev *)tlv;
15698c2ecf20Sopenharmony_ci				api_id = le16_to_cpu(api_rev->api_id);
15708c2ecf20Sopenharmony_ci				switch (api_id) {
15718c2ecf20Sopenharmony_ci				case KEY_API_VER_ID:
15728c2ecf20Sopenharmony_ci					adapter->key_api_major_ver =
15738c2ecf20Sopenharmony_ci							api_rev->major_ver;
15748c2ecf20Sopenharmony_ci					adapter->key_api_minor_ver =
15758c2ecf20Sopenharmony_ci							api_rev->minor_ver;
15768c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
15778c2ecf20Sopenharmony_ci						    "key_api v%d.%d\n",
15788c2ecf20Sopenharmony_ci						    adapter->key_api_major_ver,
15798c2ecf20Sopenharmony_ci						    adapter->key_api_minor_ver);
15808c2ecf20Sopenharmony_ci					break;
15818c2ecf20Sopenharmony_ci				case FW_API_VER_ID:
15828c2ecf20Sopenharmony_ci					adapter->fw_api_ver =
15838c2ecf20Sopenharmony_ci							api_rev->major_ver;
15848c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
15858c2ecf20Sopenharmony_ci						    "Firmware api version %d.%d\n",
15868c2ecf20Sopenharmony_ci						    adapter->fw_api_ver,
15878c2ecf20Sopenharmony_ci						    api_rev->minor_ver);
15888c2ecf20Sopenharmony_ci					break;
15898c2ecf20Sopenharmony_ci				case UAP_FW_API_VER_ID:
15908c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
15918c2ecf20Sopenharmony_ci						    "uAP api version %d.%d\n",
15928c2ecf20Sopenharmony_ci						    api_rev->major_ver,
15938c2ecf20Sopenharmony_ci						    api_rev->minor_ver);
15948c2ecf20Sopenharmony_ci					break;
15958c2ecf20Sopenharmony_ci				case CHANRPT_API_VER_ID:
15968c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, INFO,
15978c2ecf20Sopenharmony_ci						    "channel report api version %d.%d\n",
15988c2ecf20Sopenharmony_ci						    api_rev->major_ver,
15998c2ecf20Sopenharmony_ci						    api_rev->minor_ver);
16008c2ecf20Sopenharmony_ci					break;
16018c2ecf20Sopenharmony_ci				default:
16028c2ecf20Sopenharmony_ci					mwifiex_dbg(adapter, FATAL,
16038c2ecf20Sopenharmony_ci						    "Unknown api_id: %d\n",
16048c2ecf20Sopenharmony_ci						    api_id);
16058c2ecf20Sopenharmony_ci					break;
16068c2ecf20Sopenharmony_ci				}
16078c2ecf20Sopenharmony_ci				break;
16088c2ecf20Sopenharmony_ci			case TLV_TYPE_MAX_CONN:
16098c2ecf20Sopenharmony_ci				max_conn = (struct hw_spec_max_conn *)tlv;
16108c2ecf20Sopenharmony_ci				adapter->max_p2p_conn = max_conn->max_p2p_conn;
16118c2ecf20Sopenharmony_ci				adapter->max_sta_conn = max_conn->max_sta_conn;
16128c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
16138c2ecf20Sopenharmony_ci					    "max p2p connections: %u\n",
16148c2ecf20Sopenharmony_ci					    adapter->max_p2p_conn);
16158c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, INFO,
16168c2ecf20Sopenharmony_ci					    "max sta connections: %u\n",
16178c2ecf20Sopenharmony_ci					    adapter->max_sta_conn);
16188c2ecf20Sopenharmony_ci				break;
16198c2ecf20Sopenharmony_ci			default:
16208c2ecf20Sopenharmony_ci				mwifiex_dbg(adapter, FATAL,
16218c2ecf20Sopenharmony_ci					    "Unknown GET_HW_SPEC TLV type: %#x\n",
16228c2ecf20Sopenharmony_ci					    le16_to_cpu(tlv->type));
16238c2ecf20Sopenharmony_ci				break;
16248c2ecf20Sopenharmony_ci			}
16258c2ecf20Sopenharmony_ci			parsed_len += le16_to_cpu(tlv->len) +
16268c2ecf20Sopenharmony_ci				      sizeof(struct mwifiex_ie_types_header);
16278c2ecf20Sopenharmony_ci			left_len -= le16_to_cpu(tlv->len) +
16288c2ecf20Sopenharmony_ci				      sizeof(struct mwifiex_ie_types_header);
16298c2ecf20Sopenharmony_ci		}
16308c2ecf20Sopenharmony_ci	}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
16338c2ecf20Sopenharmony_ci		    "info: GET_HW_SPEC: fw_release_number- %#x\n",
16348c2ecf20Sopenharmony_ci		    adapter->fw_release_number);
16358c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
16368c2ecf20Sopenharmony_ci		    "info: GET_HW_SPEC: permanent addr: %pM\n",
16378c2ecf20Sopenharmony_ci		    hw_spec->permanent_addr);
16388c2ecf20Sopenharmony_ci	mwifiex_dbg(adapter, INFO,
16398c2ecf20Sopenharmony_ci		    "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n",
16408c2ecf20Sopenharmony_ci		    le16_to_cpu(hw_spec->hw_if_version),
16418c2ecf20Sopenharmony_ci		    le16_to_cpu(hw_spec->version));
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr);
16448c2ecf20Sopenharmony_ci	adapter->region_code = le16_to_cpu(hw_spec->region_code);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++)
16478c2ecf20Sopenharmony_ci		/* Use the region code to search for the index */
16488c2ecf20Sopenharmony_ci		if (adapter->region_code == region_code_index[i])
16498c2ecf20Sopenharmony_ci			break;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	/* If it's unidentified region code, use the default (world) */
16528c2ecf20Sopenharmony_ci	if (i >= MWIFIEX_MAX_REGION_CODE) {
16538c2ecf20Sopenharmony_ci		adapter->region_code = 0x00;
16548c2ecf20Sopenharmony_ci		mwifiex_dbg(adapter, WARN,
16558c2ecf20Sopenharmony_ci			    "cmd: unknown region code, use default (USA)\n");
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);
16598c2ecf20Sopenharmony_ci	adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;
16608c2ecf20Sopenharmony_ci	adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	if (adapter->if_ops.update_mp_end_port)
16638c2ecf20Sopenharmony_ci		adapter->if_ops.update_mp_end_port(adapter,
16648c2ecf20Sopenharmony_ci					le16_to_cpu(hw_spec->mp_end_port));
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	if (adapter->fw_api_ver == MWIFIEX_FW_V15)
16678c2ecf20Sopenharmony_ci		adapter->scan_chan_gap_enabled = true;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	return 0;
16708c2ecf20Sopenharmony_ci}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci/* This function handles the command response of hs wakeup reason
16738c2ecf20Sopenharmony_ci * command.
16748c2ecf20Sopenharmony_ci */
16758c2ecf20Sopenharmony_ciint mwifiex_ret_wakeup_reason(struct mwifiex_private *priv,
16768c2ecf20Sopenharmony_ci			      struct host_cmd_ds_command *resp,
16778c2ecf20Sopenharmony_ci			      struct host_cmd_ds_wakeup_reason *wakeup_reason)
16788c2ecf20Sopenharmony_ci{
16798c2ecf20Sopenharmony_ci	wakeup_reason->wakeup_reason =
16808c2ecf20Sopenharmony_ci		resp->params.hs_wakeup_reason.wakeup_reason;
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	return 0;
16838c2ecf20Sopenharmony_ci}
1684