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