1717 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * NXP Wireless LAN device driver: commands and events4 *5 * Copyright 2011-2020 NXP6 */7 8#include <linux/unaligned.h>9#include "decl.h"10#include "ioctl.h"11#include "util.h"12#include "fw.h"13#include "main.h"14#include "wmm.h"15#include "11n.h"16 17static void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);18 19/*20 * This function initializes a command node.21 *22 * The actual allocation of the node is not done by this function. It only23 * initiates a node by filling it with default parameters. Similarly,24 * allocation of the different buffers used (IOCTL buffer, data buffer) are25 * not done by this function either.26 */27static void28mwifiex_init_cmd_node(struct mwifiex_private *priv,29 struct cmd_ctrl_node *cmd_node,30 u32 cmd_no, void *data_buf, bool sync)31{32 cmd_node->priv = priv;33 cmd_node->cmd_no = cmd_no;34 35 if (sync) {36 cmd_node->wait_q_enabled = true;37 cmd_node->cmd_wait_q_woken = false;38 cmd_node->condition = &cmd_node->cmd_wait_q_woken;39 }40 cmd_node->data_buf = data_buf;41 cmd_node->cmd_skb = cmd_node->skb;42}43 44/*45 * This function returns a command node from the free queue depending upon46 * availability.47 */48static struct cmd_ctrl_node *49mwifiex_get_cmd_node(struct mwifiex_adapter *adapter)50{51 struct cmd_ctrl_node *cmd_node;52 53 spin_lock_bh(&adapter->cmd_free_q_lock);54 if (list_empty(&adapter->cmd_free_q)) {55 mwifiex_dbg(adapter, ERROR,56 "GET_CMD_NODE: cmd node not available\n");57 spin_unlock_bh(&adapter->cmd_free_q_lock);58 return NULL;59 }60 cmd_node = list_first_entry(&adapter->cmd_free_q,61 struct cmd_ctrl_node, list);62 list_del(&cmd_node->list);63 spin_unlock_bh(&adapter->cmd_free_q_lock);64 65 return cmd_node;66}67 68/*69 * This function cleans up a command node.70 *71 * The function resets the fields including the buffer pointers.72 * This function does not try to free the buffers. They must be73 * freed before calling this function.74 *75 * This function will however call the receive completion callback76 * in case a response buffer is still available before resetting77 * the pointer.78 */79static void80mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,81 struct cmd_ctrl_node *cmd_node)82{83 cmd_node->cmd_no = 0;84 cmd_node->cmd_flag = 0;85 cmd_node->data_buf = NULL;86 cmd_node->wait_q_enabled = false;87 88 if (cmd_node->cmd_skb)89 skb_trim(cmd_node->cmd_skb, 0);90 91 if (cmd_node->resp_skb) {92 adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb);93 cmd_node->resp_skb = NULL;94 }95}96 97/*98 * This function returns a command to the command free queue.99 *100 * The function also calls the completion callback if required, before101 * cleaning the command node and re-inserting it into the free queue.102 */103static void104mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,105 struct cmd_ctrl_node *cmd_node)106{107 if (!cmd_node)108 return;109 110 if (cmd_node->wait_q_enabled)111 mwifiex_complete_cmd(adapter, cmd_node);112 /* Clean the node */113 mwifiex_clean_cmd_node(adapter, cmd_node);114 115 /* Insert node into cmd_free_q */116 spin_lock_bh(&adapter->cmd_free_q_lock);117 list_add_tail(&cmd_node->list, &adapter->cmd_free_q);118 spin_unlock_bh(&adapter->cmd_free_q_lock);119}120 121/* This function reuses a command node. */122void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,123 struct cmd_ctrl_node *cmd_node)124{125 struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data;126 127 mwifiex_insert_cmd_to_free_q(adapter, cmd_node);128 129 atomic_dec(&adapter->cmd_pending);130 mwifiex_dbg(adapter, CMD,131 "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n",132 le16_to_cpu(host_cmd->command),133 atomic_read(&adapter->cmd_pending));134}135 136/*137 * This function sends a host command to the firmware.138 *139 * The function copies the host command into the driver command140 * buffer, which will be transferred to the firmware later by the141 * main thread.142 */143static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv,144 struct host_cmd_ds_command *cmd,145 struct mwifiex_ds_misc_cmd *pcmd_ptr)146{147 /* Copy the HOST command to command buffer */148 memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len);149 mwifiex_dbg(priv->adapter, CMD,150 "cmd: host cmd size = %d\n", pcmd_ptr->len);151 return 0;152}153 154/*155 * This function downloads a command to the firmware.156 *157 * The function performs sanity tests, sets the command sequence158 * number and size, converts the header fields to CPU format before159 * sending. Afterwards, it logs the command ID and action for debugging160 * and sets up the command timeout timer.161 */162static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,163 struct cmd_ctrl_node *cmd_node)164{165 166 struct mwifiex_adapter *adapter = priv->adapter;167 int ret;168 struct host_cmd_ds_command *host_cmd;169 uint16_t cmd_code;170 uint16_t cmd_size;171 172 if (!adapter || !cmd_node)173 return -1;174 175 host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);176 177 /* Sanity test */178 if (host_cmd->size == 0) {179 mwifiex_dbg(adapter, ERROR,180 "DNLD_CMD: host_cmd is null\t"181 "or cmd size is 0, not sending\n");182 if (cmd_node->wait_q_enabled)183 adapter->cmd_wait_q.status = -1;184 mwifiex_recycle_cmd_node(adapter, cmd_node);185 return -1;186 }187 188 cmd_code = le16_to_cpu(host_cmd->command);189 cmd_node->cmd_no = cmd_code;190 cmd_size = le16_to_cpu(host_cmd->size);191 192 if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET &&193 cmd_code != HostCmd_CMD_FUNC_SHUTDOWN &&194 cmd_code != HostCmd_CMD_FUNC_INIT) {195 mwifiex_dbg(adapter, ERROR,196 "DNLD_CMD: FW in reset state, ignore cmd %#x\n",197 cmd_code);198 mwifiex_recycle_cmd_node(adapter, cmd_node);199 queue_work(adapter->workqueue, &adapter->main_work);200 return -1;201 }202 203 /* Set command sequence number */204 adapter->seq_num++;205 host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO206 (adapter->seq_num,207 cmd_node->priv->bss_num,208 cmd_node->priv->bss_type));209 210 spin_lock_bh(&adapter->mwifiex_cmd_lock);211 adapter->curr_cmd = cmd_node;212 spin_unlock_bh(&adapter->mwifiex_cmd_lock);213 214 /* Adjust skb length */215 if (cmd_node->cmd_skb->len > cmd_size)216 /*217 * cmd_size is less than sizeof(struct host_cmd_ds_command).218 * Trim off the unused portion.219 */220 skb_trim(cmd_node->cmd_skb, cmd_size);221 else if (cmd_node->cmd_skb->len < cmd_size)222 /*223 * cmd_size is larger than sizeof(struct host_cmd_ds_command)224 * because we have appended custom IE TLV. Increase skb length225 * accordingly.226 */227 skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);228 229 mwifiex_dbg(adapter, CMD,230 "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",231 cmd_code,232 get_unaligned_le16((u8 *)host_cmd + S_DS_GEN),233 cmd_size, le16_to_cpu(host_cmd->seq_num));234 mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size);235 236 if (adapter->iface_type == MWIFIEX_USB) {237 skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);238 put_unaligned_le32(MWIFIEX_USB_TYPE_CMD,239 cmd_node->cmd_skb->data);240 adapter->cmd_sent = true;241 ret = adapter->if_ops.host_to_card(adapter,242 MWIFIEX_USB_EP_CMD_EVENT,243 cmd_node->cmd_skb, NULL);244 skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN);245 if (ret == -EBUSY)246 cmd_node->cmd_skb = NULL;247 } else {248 skb_push(cmd_node->cmd_skb, adapter->intf_hdr_len);249 ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,250 cmd_node->cmd_skb, NULL);251 skb_pull(cmd_node->cmd_skb, adapter->intf_hdr_len);252 }253 254 if (ret == -1) {255 mwifiex_dbg(adapter, ERROR,256 "DNLD_CMD: host to card failed\n");257 if (adapter->iface_type == MWIFIEX_USB)258 adapter->cmd_sent = false;259 if (cmd_node->wait_q_enabled)260 adapter->cmd_wait_q.status = -1;261 mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);262 263 spin_lock_bh(&adapter->mwifiex_cmd_lock);264 adapter->curr_cmd = NULL;265 spin_unlock_bh(&adapter->mwifiex_cmd_lock);266 267 adapter->dbg.num_cmd_host_to_card_failure++;268 return -1;269 }270 271 /* Save the last command id and action to debug log */272 adapter->dbg.last_cmd_index =273 (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM;274 adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code;275 adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] =276 get_unaligned_le16((u8 *)host_cmd + S_DS_GEN);277 278 /* Setup the timer after transmit command, except that specific279 * command might not have command response.280 */281 if (cmd_code != HostCmd_CMD_FW_DUMP_EVENT)282 mod_timer(&adapter->cmd_timer,283 jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));284 285 /* Clear BSS_NO_BITS from HostCmd */286 cmd_code &= HostCmd_CMD_ID_MASK;287 288 return 0;289}290 291/*292 * This function downloads a sleep confirm command to the firmware.293 *294 * The function performs sanity tests, sets the command sequence295 * number and size, converts the header fields to CPU format before296 * sending.297 *298 * No responses are needed for sleep confirm command.299 */300static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)301{302 int ret;303 struct mwifiex_private *priv;304 struct mwifiex_opt_sleep_confirm *sleep_cfm_buf =305 (struct mwifiex_opt_sleep_confirm *)306 adapter->sleep_cfm->data;307 struct sk_buff *sleep_cfm_tmp;308 309 priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);310 311 adapter->seq_num++;312 sleep_cfm_buf->seq_num =313 cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO314 (adapter->seq_num, priv->bss_num,315 priv->bss_type));316 317 mwifiex_dbg(adapter, CMD,318 "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",319 le16_to_cpu(sleep_cfm_buf->command),320 le16_to_cpu(sleep_cfm_buf->action),321 le16_to_cpu(sleep_cfm_buf->size),322 le16_to_cpu(sleep_cfm_buf->seq_num));323 mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf,324 le16_to_cpu(sleep_cfm_buf->size));325 326 if (adapter->iface_type == MWIFIEX_USB) {327 sleep_cfm_tmp =328 dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)329 + MWIFIEX_TYPE_LEN);330 if (!sleep_cfm_tmp) {331 mwifiex_dbg(adapter, ERROR,332 "SLEEP_CFM: dev_alloc_skb failed\n");333 return -ENOMEM;334 }335 336 skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm)337 + MWIFIEX_TYPE_LEN);338 put_unaligned_le32(MWIFIEX_USB_TYPE_CMD, sleep_cfm_tmp->data);339 memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN,340 adapter->sleep_cfm->data,341 sizeof(struct mwifiex_opt_sleep_confirm));342 ret = adapter->if_ops.host_to_card(adapter,343 MWIFIEX_USB_EP_CMD_EVENT,344 sleep_cfm_tmp, NULL);345 if (ret != -EBUSY)346 dev_kfree_skb_any(sleep_cfm_tmp);347 } else {348 skb_push(adapter->sleep_cfm, adapter->intf_hdr_len);349 ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,350 adapter->sleep_cfm, NULL);351 skb_pull(adapter->sleep_cfm, adapter->intf_hdr_len);352 }353 354 if (ret == -1) {355 mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n");356 adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;357 return -1;358 }359 360 if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))361 /* Response is not needed for sleep confirm command */362 adapter->ps_state = PS_STATE_SLEEP;363 else364 adapter->ps_state = PS_STATE_SLEEP_CFM;365 366 if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&367 (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags) &&368 !adapter->sleep_period.period)) {369 adapter->pm_wakeup_card_req = true;370 mwifiex_hs_activated_event(mwifiex_get_priv371 (adapter, MWIFIEX_BSS_ROLE_ANY), true);372 }373 374 return ret;375}376 377/*378 * This function allocates the command buffers and links them to379 * the command free queue.380 *381 * The driver uses a pre allocated number of command buffers, which382 * are created at driver initializations and freed at driver cleanup.383 * Every command needs to obtain a command buffer from this pool before384 * it can be issued. The command free queue lists the command buffers385 * currently free to use, while the command pending queue lists the386 * command buffers already in use and awaiting handling. Command buffers387 * are returned to the free queue after use.388 */389int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter)390{391 struct cmd_ctrl_node *cmd_array;392 u32 i;393 394 /* Allocate and initialize struct cmd_ctrl_node */395 cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER,396 sizeof(struct cmd_ctrl_node), GFP_KERNEL);397 if (!cmd_array)398 return -ENOMEM;399 400 adapter->cmd_pool = cmd_array;401 402 /* Allocate and initialize command buffers */403 for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {404 cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);405 if (!cmd_array[i].skb) {406 mwifiex_dbg(adapter, ERROR,407 "unable to allocate command buffer\n");408 return -ENOMEM;409 }410 }411 412 for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++)413 mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]);414 415 return 0;416}417 418/*419 * This function frees the command buffers.420 *421 * The function calls the completion callback for all the command422 * buffers that still have response buffers associated with them.423 */424void mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter)425{426 struct cmd_ctrl_node *cmd_array;427 u32 i;428 429 /* Need to check if cmd pool is allocated or not */430 if (!adapter->cmd_pool) {431 mwifiex_dbg(adapter, FATAL,432 "info: FREE_CMD_BUF: cmd_pool is null\n");433 return;434 }435 436 cmd_array = adapter->cmd_pool;437 438 /* Release shared memory buffers */439 for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {440 if (cmd_array[i].skb) {441 mwifiex_dbg(adapter, CMD,442 "cmd: free cmd buffer %d\n", i);443 dev_kfree_skb_any(cmd_array[i].skb);444 }445 if (!cmd_array[i].resp_skb)446 continue;447 448 if (adapter->iface_type == MWIFIEX_USB)449 adapter->if_ops.cmdrsp_complete(adapter,450 cmd_array[i].resp_skb);451 else452 dev_kfree_skb_any(cmd_array[i].resp_skb);453 }454 /* Release struct cmd_ctrl_node */455 if (adapter->cmd_pool) {456 mwifiex_dbg(adapter, CMD,457 "cmd: free cmd pool\n");458 kfree(adapter->cmd_pool);459 adapter->cmd_pool = NULL;460 }461}462 463/*464 * This function handles events generated by firmware.465 *466 * Event body of events received from firmware are not used (though they are467 * saved), only the event ID is used. Some events are re-invoked by468 * the driver, with a new event body.469 *470 * After processing, the function calls the completion callback471 * for cleanup.472 */473int mwifiex_process_event(struct mwifiex_adapter *adapter)474{475 int ret, i;476 struct mwifiex_private *priv =477 mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);478 struct sk_buff *skb = adapter->event_skb;479 u32 eventcause;480 struct mwifiex_rxinfo *rx_info;481 482 if ((adapter->event_cause & EVENT_ID_MASK) == EVENT_RADAR_DETECTED) {483 for (i = 0; i < adapter->priv_num; i++) {484 priv = adapter->priv[i];485 if (mwifiex_is_11h_active(priv)) {486 adapter->event_cause |=487 ((priv->bss_num & 0xff) << 16) |488 ((priv->bss_type & 0xff) << 24);489 break;490 }491 }492 }493 494 eventcause = adapter->event_cause;495 496 /* Save the last event to debug log */497 adapter->dbg.last_event_index =498 (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM;499 adapter->dbg.last_event[adapter->dbg.last_event_index] =500 (u16) eventcause;501 502 /* Get BSS number and corresponding priv */503 priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause),504 EVENT_GET_BSS_TYPE(eventcause));505 if (!priv)506 priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);507 508 /* Clear BSS_NO_BITS from event */509 eventcause &= EVENT_ID_MASK;510 adapter->event_cause = eventcause;511 512 if (skb) {513 rx_info = MWIFIEX_SKB_RXCB(skb);514 memset(rx_info, 0, sizeof(*rx_info));515 rx_info->bss_num = priv->bss_num;516 rx_info->bss_type = priv->bss_type;517 mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:",518 skb->data, skb->len);519 }520 521 mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause);522 523 if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)524 ret = mwifiex_process_uap_event(priv);525 else526 ret = mwifiex_process_sta_event(priv);527 528 adapter->event_cause = 0;529 adapter->event_skb = NULL;530 adapter->if_ops.event_complete(adapter, skb);531 532 return ret;533}534 535/*536 * This function prepares a command and send it to the firmware.537 *538 * Preparation includes -539 * - Sanity tests to make sure the card is still present or the FW540 * is not reset541 * - Getting a new command node from the command free queue542 * - Initializing the command node for default parameters543 * - Fill up the non-default parameters and buffer pointers544 * - Add the command to pending queue545 */546int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no,547 u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync)548{549 int ret;550 struct mwifiex_adapter *adapter = priv->adapter;551 struct cmd_ctrl_node *cmd_node;552 struct host_cmd_ds_command *cmd_ptr;553 554 if (!adapter) {555 pr_err("PREP_CMD: adapter is NULL\n");556 return -1;557 }558 559 if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) {560 mwifiex_dbg(adapter, ERROR,561 "PREP_CMD: device in suspended state\n");562 return -1;563 }564 565 if (test_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags) &&566 cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) {567 mwifiex_dbg(adapter, ERROR,568 "PREP_CMD: host entering sleep state\n");569 return -1;570 }571 572 if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) {573 mwifiex_dbg(adapter, ERROR,574 "PREP_CMD: card is removed\n");575 return -1;576 }577 578 if (test_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags)) {579 mwifiex_dbg(adapter, ERROR,580 "PREP_CMD: FW is in bad state\n");581 return -1;582 }583 584 if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) {585 if (cmd_no != HostCmd_CMD_FUNC_INIT) {586 mwifiex_dbg(adapter, ERROR,587 "PREP_CMD: FW in reset state\n");588 return -1;589 }590 }591 /* We don't expect commands in manufacturing mode. They are cooked592 * in application and ready to download buffer is passed to the driver593 */594 if (adapter->mfg_mode && cmd_no) {595 dev_dbg(adapter->dev, "Ignoring commands in manufacturing mode\n");596 return -1;597 }598 599 if (priv->adapter->hs_activated_manually &&600 cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) {601 mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);602 priv->adapter->hs_activated_manually = false;603 }604 605 /* Get a new command node */606 cmd_node = mwifiex_get_cmd_node(adapter);607 608 if (!cmd_node) {609 mwifiex_dbg(adapter, ERROR,610 "PREP_CMD: no free cmd node\n");611 return -1;612 }613 614 /* Initialize the command node */615 mwifiex_init_cmd_node(priv, cmd_node, cmd_no, data_buf, sync);616 617 if (!cmd_node->cmd_skb) {618 mwifiex_dbg(adapter, ERROR,619 "PREP_CMD: no free cmd buf\n");620 return -1;621 }622 623 skb_put_zero(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command));624 625 cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);626 cmd_ptr->command = cpu_to_le16(cmd_no);627 cmd_ptr->result = 0;628 629 /* Prepare command */630 if (cmd_no) {631 switch (cmd_no) {632 case HostCmd_CMD_UAP_SYS_CONFIG:633 case HostCmd_CMD_UAP_BSS_START:634 case HostCmd_CMD_UAP_BSS_STOP:635 case HostCmd_CMD_UAP_STA_DEAUTH:636 case HOST_CMD_APCMD_SYS_RESET:637 case HOST_CMD_APCMD_STA_LIST:638 case HostCmd_CMD_CHAN_REPORT_REQUEST:639 case HostCmd_CMD_ADD_NEW_STATION:640 ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,641 cmd_oid, data_buf,642 cmd_ptr);643 break;644 default:645 ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action,646 cmd_oid, data_buf,647 cmd_ptr);648 break;649 }650 } else {651 ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf);652 cmd_node->cmd_flag |= CMD_F_HOSTCMD;653 }654 655 /* Return error, since the command preparation failed */656 if (ret) {657 mwifiex_dbg(adapter, ERROR,658 "PREP_CMD: cmd %#x preparation failed\n",659 cmd_no);660 mwifiex_insert_cmd_to_free_q(adapter, cmd_node);661 return -1;662 }663 664 /* Send command */665 if (cmd_no == HostCmd_CMD_802_11_SCAN ||666 cmd_no == HostCmd_CMD_802_11_SCAN_EXT) {667 mwifiex_queue_scan_cmd(priv, cmd_node);668 } else {669 mwifiex_insert_cmd_to_pending_q(adapter, cmd_node);670 queue_work(adapter->workqueue, &adapter->main_work);671 if (cmd_node->wait_q_enabled)672 ret = mwifiex_wait_queue_complete(adapter, cmd_node);673 }674 675 return ret;676}677 678/*679 * This function queues a command to the command pending queue.680 *681 * This in effect adds the command to the command list to be executed.682 * Exit PS command is handled specially, by placing it always to the683 * front of the command queue.684 */685void686mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,687 struct cmd_ctrl_node *cmd_node)688{689 struct host_cmd_ds_command *host_cmd = NULL;690 u16 command;691 bool add_tail = true;692 693 host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);694 if (!host_cmd) {695 mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n");696 return;697 }698 699 command = le16_to_cpu(host_cmd->command);700 701 /* Exit_PS command needs to be queued in the header always. */702 if (command == HostCmd_CMD_802_11_PS_MODE_ENH) {703 struct host_cmd_ds_802_11_ps_mode_enh *pm =704 &host_cmd->params.psmode_enh;705 if ((le16_to_cpu(pm->action) == DIS_PS) ||706 (le16_to_cpu(pm->action) == DIS_AUTO_PS)) {707 if (adapter->ps_state != PS_STATE_AWAKE)708 add_tail = false;709 }710 }711 712 /* Same with exit host sleep cmd, luckily that can't happen at the same time as EXIT_PS */713 if (command == HostCmd_CMD_802_11_HS_CFG_ENH) {714 struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg =715 &host_cmd->params.opt_hs_cfg;716 717 if (le16_to_cpu(hs_cfg->action) == HS_ACTIVATE)718 add_tail = false;719 }720 721 spin_lock_bh(&adapter->cmd_pending_q_lock);722 if (add_tail)723 list_add_tail(&cmd_node->list, &adapter->cmd_pending_q);724 else725 list_add(&cmd_node->list, &adapter->cmd_pending_q);726 spin_unlock_bh(&adapter->cmd_pending_q_lock);727 728 atomic_inc(&adapter->cmd_pending);729 mwifiex_dbg(adapter, CMD,730 "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",731 command, atomic_read(&adapter->cmd_pending));732}733 734/*735 * This function executes the next command in command pending queue.736 *737 * This function will fail if a command is already in processing stage,738 * otherwise it will dequeue the first command from the command pending739 * queue and send to the firmware.740 *741 * If the device is currently in host sleep mode, any commands, except the742 * host sleep configuration command will de-activate the host sleep. For PS743 * mode, the function will put the firmware back to sleep if applicable.744 */745int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)746{747 struct mwifiex_private *priv;748 struct cmd_ctrl_node *cmd_node;749 int ret = 0;750 struct host_cmd_ds_command *host_cmd;751 752 /* Check if already in processing */753 if (adapter->curr_cmd) {754 mwifiex_dbg(adapter, FATAL,755 "EXEC_NEXT_CMD: cmd in processing\n");756 return -1;757 }758 759 spin_lock_bh(&adapter->mwifiex_cmd_lock);760 /* Check if any command is pending */761 spin_lock_bh(&adapter->cmd_pending_q_lock);762 if (list_empty(&adapter->cmd_pending_q)) {763 spin_unlock_bh(&adapter->cmd_pending_q_lock);764 spin_unlock_bh(&adapter->mwifiex_cmd_lock);765 return 0;766 }767 cmd_node = list_first_entry(&adapter->cmd_pending_q,768 struct cmd_ctrl_node, list);769 770 host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);771 priv = cmd_node->priv;772 773 if (adapter->ps_state != PS_STATE_AWAKE) {774 mwifiex_dbg(adapter, ERROR,775 "%s: cannot send cmd in sleep state,\t"776 "this should not happen\n", __func__);777 spin_unlock_bh(&adapter->cmd_pending_q_lock);778 spin_unlock_bh(&adapter->mwifiex_cmd_lock);779 return ret;780 }781 782 list_del(&cmd_node->list);783 spin_unlock_bh(&adapter->cmd_pending_q_lock);784 785 spin_unlock_bh(&adapter->mwifiex_cmd_lock);786 ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node);787 priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);788 /* Any command sent to the firmware when host is in sleep789 * mode should de-configure host sleep. We should skip the790 * host sleep configuration command itself though791 */792 if (priv && (host_cmd->command !=793 cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) {794 if (adapter->hs_activated) {795 clear_bit(MWIFIEX_IS_HS_CONFIGURED,796 &adapter->work_flags);797 mwifiex_hs_activated_event(priv, false);798 }799 }800 801 return ret;802}803 804/*805 * This function handles the command response.806 *807 * After processing, the function cleans the command node and puts808 * it back to the command free queue.809 */810int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)811{812 struct host_cmd_ds_command *resp;813 struct mwifiex_private *priv =814 mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);815 int ret = 0;816 uint16_t orig_cmdresp_no;817 uint16_t cmdresp_no;818 uint16_t cmdresp_result;819 820 if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {821 resp = (struct host_cmd_ds_command *) adapter->upld_buf;822 mwifiex_dbg(adapter, ERROR,823 "CMD_RESP: NULL curr_cmd, %#x\n",824 le16_to_cpu(resp->command));825 return -1;826 }827 828 resp = (struct host_cmd_ds_command *)adapter->curr_cmd->resp_skb->data;829 orig_cmdresp_no = le16_to_cpu(resp->command);830 cmdresp_no = (orig_cmdresp_no & HostCmd_CMD_ID_MASK);831 832 if (adapter->curr_cmd->cmd_no != cmdresp_no) {833 mwifiex_dbg(adapter, ERROR,834 "cmdresp error: cmd=0x%x cmd_resp=0x%x\n",835 adapter->curr_cmd->cmd_no, cmdresp_no);836 return -1;837 }838 /* Now we got response from FW, cancel the command timer */839 del_timer_sync(&adapter->cmd_timer);840 clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);841 842 if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {843 /* Copy original response back to response buffer */844 struct mwifiex_ds_misc_cmd *hostcmd;845 uint16_t size = le16_to_cpu(resp->size);846 mwifiex_dbg(adapter, INFO,847 "info: host cmd resp size = %d\n", size);848 size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER);849 if (adapter->curr_cmd->data_buf) {850 hostcmd = adapter->curr_cmd->data_buf;851 hostcmd->len = size;852 memcpy(hostcmd->cmd, resp, size);853 }854 }855 856 /* Get BSS number and corresponding priv */857 priv = mwifiex_get_priv_by_id(adapter,858 HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)),859 HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num)));860 if (!priv)861 priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);862 /* Clear RET_BIT from HostCmd */863 resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK);864 865 cmdresp_no = le16_to_cpu(resp->command);866 cmdresp_result = le16_to_cpu(resp->result);867 868 /* Save the last command response to debug log */869 adapter->dbg.last_cmd_resp_index =870 (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM;871 adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =872 orig_cmdresp_no;873 874 mwifiex_dbg(adapter, CMD,875 "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",876 orig_cmdresp_no, cmdresp_result,877 le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));878 mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp,879 le16_to_cpu(resp->size));880 881 if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {882 mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n");883 if (adapter->curr_cmd->wait_q_enabled)884 adapter->cmd_wait_q.status = -1;885 886 mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);887 spin_lock_bh(&adapter->mwifiex_cmd_lock);888 adapter->curr_cmd = NULL;889 spin_unlock_bh(&adapter->mwifiex_cmd_lock);890 return -1;891 }892 893 if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {894 adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD;895 if ((cmdresp_result == HostCmd_RESULT_OK) &&896 (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH))897 ret = mwifiex_ret_802_11_hs_cfg(priv, resp);898 } else {899 /* handle response */900 ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp);901 }902 903 /* Check init command response */904 if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {905 if (ret) {906 mwifiex_dbg(adapter, ERROR,907 "%s: cmd %#x failed during\t"908 "initialization\n", __func__, cmdresp_no);909 mwifiex_init_fw_complete(adapter);910 return -1;911 } else if (adapter->last_init_cmd == cmdresp_no)912 adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE;913 }914 915 if (adapter->curr_cmd) {916 if (adapter->curr_cmd->wait_q_enabled)917 adapter->cmd_wait_q.status = ret;918 919 mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);920 921 spin_lock_bh(&adapter->mwifiex_cmd_lock);922 adapter->curr_cmd = NULL;923 spin_unlock_bh(&adapter->mwifiex_cmd_lock);924 }925 926 return ret;927}928 929void mwifiex_process_assoc_resp(struct mwifiex_adapter *adapter)930{931 struct cfg80211_rx_assoc_resp_data assoc_resp = {932 .uapsd_queues = -1,933 };934 struct mwifiex_private *priv =935 mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);936 937 if (priv->assoc_rsp_size) {938 assoc_resp.links[0].bss = priv->req_bss;939 assoc_resp.buf = priv->assoc_rsp_buf;940 assoc_resp.len = priv->assoc_rsp_size;941 cfg80211_rx_assoc_resp(priv->netdev,942 &assoc_resp);943 priv->assoc_rsp_size = 0;944 }945}946 947/*948 * This function handles the timeout of command sending.949 *950 * It will re-send the same command again.951 */952void953mwifiex_cmd_timeout_func(struct timer_list *t)954{955 struct mwifiex_adapter *adapter = from_timer(adapter, t, cmd_timer);956 struct cmd_ctrl_node *cmd_node;957 958 set_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags);959 if (!adapter->curr_cmd) {960 mwifiex_dbg(adapter, ERROR,961 "cmd: empty curr_cmd\n");962 return;963 }964 cmd_node = adapter->curr_cmd;965 if (cmd_node) {966 adapter->dbg.timeout_cmd_id =967 adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];968 adapter->dbg.timeout_cmd_act =969 adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];970 mwifiex_dbg(adapter, MSG,971 "%s: Timeout cmd id = %#x, act = %#x\n", __func__,972 adapter->dbg.timeout_cmd_id,973 adapter->dbg.timeout_cmd_act);974 975 mwifiex_dbg(adapter, MSG,976 "num_data_h2c_failure = %d\n",977 adapter->dbg.num_tx_host_to_card_failure);978 mwifiex_dbg(adapter, MSG,979 "num_cmd_h2c_failure = %d\n",980 adapter->dbg.num_cmd_host_to_card_failure);981 982 mwifiex_dbg(adapter, MSG,983 "is_cmd_timedout = %d\n",984 test_bit(MWIFIEX_IS_CMD_TIMEDOUT,985 &adapter->work_flags));986 mwifiex_dbg(adapter, MSG,987 "num_tx_timeout = %d\n",988 adapter->dbg.num_tx_timeout);989 990 mwifiex_dbg(adapter, MSG,991 "last_cmd_index = %d\n",992 adapter->dbg.last_cmd_index);993 mwifiex_dbg(adapter, MSG,994 "last_cmd_id: %*ph\n",995 (int)sizeof(adapter->dbg.last_cmd_id),996 adapter->dbg.last_cmd_id);997 mwifiex_dbg(adapter, MSG,998 "last_cmd_act: %*ph\n",999 (int)sizeof(adapter->dbg.last_cmd_act),1000 adapter->dbg.last_cmd_act);1001 1002 mwifiex_dbg(adapter, MSG,1003 "last_cmd_resp_index = %d\n",1004 adapter->dbg.last_cmd_resp_index);1005 mwifiex_dbg(adapter, MSG,1006 "last_cmd_resp_id: %*ph\n",1007 (int)sizeof(adapter->dbg.last_cmd_resp_id),1008 adapter->dbg.last_cmd_resp_id);1009 1010 mwifiex_dbg(adapter, MSG,1011 "last_event_index = %d\n",1012 adapter->dbg.last_event_index);1013 mwifiex_dbg(adapter, MSG,1014 "last_event: %*ph\n",1015 (int)sizeof(adapter->dbg.last_event),1016 adapter->dbg.last_event);1017 1018 mwifiex_dbg(adapter, MSG,1019 "data_sent=%d cmd_sent=%d\n",1020 adapter->data_sent, adapter->cmd_sent);1021 1022 mwifiex_dbg(adapter, MSG,1023 "ps_mode=%d ps_state=%d\n",1024 adapter->ps_mode, adapter->ps_state);1025 1026 if (cmd_node->wait_q_enabled) {1027 adapter->cmd_wait_q.status = -ETIMEDOUT;1028 mwifiex_cancel_pending_ioctl(adapter);1029 }1030 }1031 if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {1032 mwifiex_init_fw_complete(adapter);1033 return;1034 }1035 1036 if (adapter->if_ops.device_dump)1037 adapter->if_ops.device_dump(adapter);1038 1039 if (adapter->if_ops.card_reset)1040 adapter->if_ops.card_reset(adapter);1041}1042 1043void1044mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter)1045{1046 struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;1047 1048 /* Cancel all pending scan command */1049 spin_lock_bh(&adapter->scan_pending_q_lock);1050 list_for_each_entry_safe(cmd_node, tmp_node,1051 &adapter->scan_pending_q, list) {1052 list_del(&cmd_node->list);1053 cmd_node->wait_q_enabled = false;1054 mwifiex_insert_cmd_to_free_q(adapter, cmd_node);1055 }1056 spin_unlock_bh(&adapter->scan_pending_q_lock);1057}1058 1059/*1060 * This function cancels all the pending commands.1061 *1062 * The current command, all commands in command pending queue and all scan1063 * commands in scan pending queue are cancelled. All the completion callbacks1064 * are called with failure status to ensure cleanup.1065 */1066void1067mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)1068{1069 struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;1070 1071 spin_lock_bh(&adapter->mwifiex_cmd_lock);1072 /* Cancel current cmd */1073 if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) {1074 adapter->cmd_wait_q.status = -1;1075 mwifiex_complete_cmd(adapter, adapter->curr_cmd);1076 adapter->curr_cmd->wait_q_enabled = false;1077 /* no recycle probably wait for response */1078 }1079 /* Cancel all pending command */1080 spin_lock_bh(&adapter->cmd_pending_q_lock);1081 list_for_each_entry_safe(cmd_node, tmp_node,1082 &adapter->cmd_pending_q, list) {1083 list_del(&cmd_node->list);1084 1085 if (cmd_node->wait_q_enabled)1086 adapter->cmd_wait_q.status = -1;1087 mwifiex_recycle_cmd_node(adapter, cmd_node);1088 }1089 spin_unlock_bh(&adapter->cmd_pending_q_lock);1090 spin_unlock_bh(&adapter->mwifiex_cmd_lock);1091 1092 mwifiex_cancel_scan(adapter);1093}1094 1095/*1096 * This function cancels all pending commands that matches with1097 * the given IOCTL request.1098 *1099 * Both the current command buffer and the pending command queue are1100 * searched for matching IOCTL request. The completion callback of1101 * the matched command is called with failure status to ensure cleanup.1102 * In case of scan commands, all pending commands in scan pending queue1103 * are cancelled.1104 */1105static void1106mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)1107{1108 struct cmd_ctrl_node *cmd_node = NULL;1109 1110 if ((adapter->curr_cmd) &&1111 (adapter->curr_cmd->wait_q_enabled)) {1112 spin_lock_bh(&adapter->mwifiex_cmd_lock);1113 cmd_node = adapter->curr_cmd;1114 /* setting curr_cmd to NULL is quite dangerous, because1115 * mwifiex_process_cmdresp checks curr_cmd to be != NULL1116 * at the beginning then relies on it and dereferences1117 * it at will1118 * this probably works since mwifiex_cmd_timeout_func1119 * is the only caller of this function and responses1120 * at that point1121 */1122 adapter->curr_cmd = NULL;1123 spin_unlock_bh(&adapter->mwifiex_cmd_lock);1124 1125 mwifiex_recycle_cmd_node(adapter, cmd_node);1126 }1127 1128 mwifiex_cancel_scan(adapter);1129}1130 1131/*1132 * This function sends the sleep confirm command to firmware, if1133 * possible.1134 *1135 * The sleep confirm command cannot be issued if command response,1136 * data response or event response is awaiting handling, or if we1137 * are in the middle of sending a command, or expecting a command1138 * response.1139 */1140void1141mwifiex_check_ps_cond(struct mwifiex_adapter *adapter)1142{1143 if (!adapter->cmd_sent && !atomic_read(&adapter->tx_hw_pending) &&1144 !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter))1145 mwifiex_dnld_sleep_confirm_cmd(adapter);1146 else1147 mwifiex_dbg(adapter, CMD,1148 "cmd: Delay Sleep Confirm (%s%s%s%s)\n",1149 (adapter->cmd_sent) ? "D" : "",1150 atomic_read(&adapter->tx_hw_pending) ? "T" : "",1151 (adapter->curr_cmd) ? "C" : "",1152 (IS_CARD_RX_RCVD(adapter)) ? "R" : "");1153}1154 1155/*1156 * This function sends a Host Sleep activated event to applications.1157 *1158 * This event is generated by the driver, with a blank event body.1159 */1160void1161mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated)1162{1163 if (activated) {1164 if (test_bit(MWIFIEX_IS_HS_CONFIGURED,1165 &priv->adapter->work_flags)) {1166 priv->adapter->hs_activated = true;1167 mwifiex_update_rxreor_flags(priv->adapter,1168 RXREOR_FORCE_NO_DROP);1169 mwifiex_dbg(priv->adapter, EVENT,1170 "event: hs_activated\n");1171 priv->adapter->hs_activate_wait_q_woken = true;1172 wake_up_interruptible(1173 &priv->adapter->hs_activate_wait_q);1174 } else {1175 mwifiex_dbg(priv->adapter, EVENT,1176 "event: HS not configured\n");1177 }1178 } else {1179 mwifiex_dbg(priv->adapter, EVENT,1180 "event: hs_deactivated\n");1181 priv->adapter->hs_activated = false;1182 }1183}1184 1185/*1186 * This function handles the command response of a Host Sleep configuration1187 * command.1188 *1189 * Handling includes changing the header fields into CPU format1190 * and setting the current host sleep activation status in driver.1191 *1192 * In case host sleep status change, the function generates an event to1193 * notify the applications.1194 */1195int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,1196 struct host_cmd_ds_command *resp)1197{1198 struct mwifiex_adapter *adapter = priv->adapter;1199 struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =1200 &resp->params.opt_hs_cfg;1201 uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);1202 1203 if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) &&1204 adapter->iface_type != MWIFIEX_USB) {1205 mwifiex_hs_activated_event(priv, true);1206 return 0;1207 } else {1208 mwifiex_dbg(adapter, CMD,1209 "cmd: CMD_RESP: HS_CFG cmd reply\t"1210 " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n",1211 resp->result, conditions,1212 phs_cfg->params.hs_config.gpio,1213 phs_cfg->params.hs_config.gap);1214 }1215 if (conditions != HS_CFG_CANCEL) {1216 set_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);1217 if (adapter->iface_type == MWIFIEX_USB)1218 mwifiex_hs_activated_event(priv, true);1219 } else {1220 clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);1221 if (adapter->hs_activated)1222 mwifiex_hs_activated_event(priv, false);1223 }1224 1225 return 0;1226}1227 1228/*1229 * This function wakes up the adapter and generates a Host Sleep1230 * cancel event on receiving the power up interrupt.1231 */1232void1233mwifiex_process_hs_config(struct mwifiex_adapter *adapter)1234{1235 mwifiex_dbg(adapter, INFO,1236 "info: %s: auto cancelling host sleep\t"1237 "since there is interrupt from the firmware\n",1238 __func__);1239 1240 adapter->if_ops.wakeup(adapter);1241 1242 if (adapter->hs_activated_manually) {1243 mwifiex_cancel_hs(mwifiex_get_priv (adapter, MWIFIEX_BSS_ROLE_ANY),1244 MWIFIEX_ASYNC_CMD);1245 adapter->hs_activated_manually = false;1246 }1247 1248 adapter->hs_activated = false;1249 clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags);1250 clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);1251 mwifiex_hs_activated_event(mwifiex_get_priv(adapter,1252 MWIFIEX_BSS_ROLE_ANY),1253 false);1254}1255EXPORT_SYMBOL_GPL(mwifiex_process_hs_config);1256 1257/*1258 * This function handles the command response of a sleep confirm command.1259 *1260 * The function sets the card state to SLEEP if the response indicates success.1261 */1262void1263mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,1264 u8 *pbuf, u32 upld_len)1265{1266 struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf;1267 uint16_t result = le16_to_cpu(cmd->result);1268 uint16_t command = le16_to_cpu(cmd->command);1269 uint16_t seq_num = le16_to_cpu(cmd->seq_num);1270 1271 if (!upld_len) {1272 mwifiex_dbg(adapter, ERROR,1273 "%s: cmd size is 0\n", __func__);1274 return;1275 }1276 1277 mwifiex_dbg(adapter, CMD,1278 "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",1279 command, result, le16_to_cpu(cmd->size), seq_num);1280 1281 /* Update sequence number */1282 seq_num = HostCmd_GET_SEQ_NO(seq_num);1283 /* Clear RET_BIT from HostCmd */1284 command &= HostCmd_CMD_ID_MASK;1285 1286 if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {1287 mwifiex_dbg(adapter, ERROR,1288 "%s: rcvd unexpected resp for cmd %#x, result = %x\n",1289 __func__, command, result);1290 return;1291 }1292 1293 if (result) {1294 mwifiex_dbg(adapter, ERROR,1295 "%s: sleep confirm cmd failed\n",1296 __func__);1297 adapter->pm_wakeup_card_req = false;1298 adapter->ps_state = PS_STATE_AWAKE;1299 return;1300 }1301 adapter->pm_wakeup_card_req = true;1302 if (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags))1303 mwifiex_hs_activated_event(mwifiex_get_priv1304 (adapter, MWIFIEX_BSS_ROLE_ANY),1305 true);1306 adapter->ps_state = PS_STATE_SLEEP;1307 cmd->command = cpu_to_le16(command);1308 cmd->seq_num = cpu_to_le16(seq_num);1309}1310EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp);1311 1312/*1313 * This function prepares an enhanced power mode command.1314 *1315 * This function can be used to disable power save or to configure1316 * power save with auto PS or STA PS or auto deep sleep.1317 *1318 * Preparation includes -1319 * - Setting command ID, action and proper size1320 * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV,1321 * auto deep sleep TLV (as required)1322 * - Ensuring correct endian-ness1323 */1324int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,1325 struct host_cmd_ds_command *cmd,1326 u16 cmd_action, uint16_t ps_bitmap,1327 struct mwifiex_ds_auto_ds *auto_ds)1328{1329 struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =1330 &cmd->params.psmode_enh;1331 u8 *tlv;1332 u16 cmd_size = 0;1333 1334 cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);1335 if (cmd_action == DIS_AUTO_PS) {1336 psmode_enh->action = cpu_to_le16(DIS_AUTO_PS);1337 psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);1338 cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +1339 sizeof(psmode_enh->params.ps_bitmap));1340 } else if (cmd_action == GET_PS) {1341 psmode_enh->action = cpu_to_le16(GET_PS);1342 psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);1343 cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +1344 sizeof(psmode_enh->params.ps_bitmap));1345 } else if (cmd_action == EN_AUTO_PS) {1346 psmode_enh->action = cpu_to_le16(EN_AUTO_PS);1347 psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);1348 cmd_size = S_DS_GEN + sizeof(psmode_enh->action) +1349 sizeof(psmode_enh->params.ps_bitmap);1350 tlv = (u8 *) cmd + cmd_size;1351 if (ps_bitmap & BITMAP_STA_PS) {1352 struct mwifiex_adapter *adapter = priv->adapter;1353 struct mwifiex_ie_types_ps_param *ps_tlv =1354 (struct mwifiex_ie_types_ps_param *) tlv;1355 struct mwifiex_ps_param *ps_mode = &ps_tlv->param;1356 ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM);1357 ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) -1358 sizeof(struct mwifiex_ie_types_header));1359 cmd_size += sizeof(*ps_tlv);1360 tlv += sizeof(*ps_tlv);1361 mwifiex_dbg(priv->adapter, CMD,1362 "cmd: PS Command: Enter PS\n");1363 ps_mode->null_pkt_interval =1364 cpu_to_le16(adapter->null_pkt_interval);1365 ps_mode->multiple_dtims =1366 cpu_to_le16(adapter->multiple_dtim);1367 ps_mode->bcn_miss_timeout =1368 cpu_to_le16(adapter->bcn_miss_time_out);1369 ps_mode->local_listen_interval =1370 cpu_to_le16(adapter->local_listen_interval);1371 ps_mode->adhoc_wake_period =1372 cpu_to_le16(adapter->adhoc_awake_period);1373 ps_mode->delay_to_ps =1374 cpu_to_le16(adapter->delay_to_ps);1375 ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode);1376 1377 }1378 if (ps_bitmap & BITMAP_AUTO_DS) {1379 struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv =1380 (struct mwifiex_ie_types_auto_ds_param *) tlv;1381 u16 idletime = 0;1382 1383 auto_ds_tlv->header.type =1384 cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM);1385 auto_ds_tlv->header.len =1386 cpu_to_le16(sizeof(*auto_ds_tlv) -1387 sizeof(struct mwifiex_ie_types_header));1388 cmd_size += sizeof(*auto_ds_tlv);1389 tlv += sizeof(*auto_ds_tlv);1390 if (auto_ds)1391 idletime = auto_ds->idle_time;1392 mwifiex_dbg(priv->adapter, CMD,1393 "cmd: PS Command: Enter Auto Deep Sleep\n");1394 auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime);1395 }1396 cmd->size = cpu_to_le16(cmd_size);1397 }1398 return 0;1399}1400 1401/*1402 * This function handles the command response of an enhanced power mode1403 * command.1404 *1405 * Handling includes changing the header fields into CPU format1406 * and setting the current enhanced power mode in driver.1407 */1408int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,1409 struct host_cmd_ds_command *resp,1410 struct mwifiex_ds_pm_cfg *pm_cfg)1411{1412 struct mwifiex_adapter *adapter = priv->adapter;1413 struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =1414 &resp->params.psmode_enh;1415 uint16_t action = le16_to_cpu(ps_mode->action);1416 uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap);1417 uint16_t auto_ps_bitmap =1418 le16_to_cpu(ps_mode->params.ps_bitmap);1419 1420 mwifiex_dbg(adapter, INFO,1421 "info: %s: PS_MODE cmd reply result=%#x action=%#X\n",1422 __func__, resp->result, action);1423 if (action == EN_AUTO_PS) {1424 if (auto_ps_bitmap & BITMAP_AUTO_DS) {1425 mwifiex_dbg(adapter, CMD,1426 "cmd: Enabled auto deep sleep\n");1427 priv->adapter->is_deep_sleep = true;1428 }1429 if (auto_ps_bitmap & BITMAP_STA_PS) {1430 mwifiex_dbg(adapter, CMD,1431 "cmd: Enabled STA power save\n");1432 if (adapter->sleep_period.period)1433 mwifiex_dbg(adapter, CMD,1434 "cmd: set to uapsd/pps mode\n");1435 }1436 } else if (action == DIS_AUTO_PS) {1437 if (ps_bitmap & BITMAP_AUTO_DS) {1438 priv->adapter->is_deep_sleep = false;1439 mwifiex_dbg(adapter, CMD,1440 "cmd: Disabled auto deep sleep\n");1441 }1442 if (ps_bitmap & BITMAP_STA_PS) {1443 mwifiex_dbg(adapter, CMD,1444 "cmd: Disabled STA power save\n");1445 if (adapter->sleep_period.period) {1446 adapter->delay_null_pkt = false;1447 adapter->tx_lock_flag = false;1448 adapter->pps_uapsd_mode = false;1449 }1450 }1451 } else if (action == GET_PS) {1452 if (ps_bitmap & BITMAP_STA_PS)1453 adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;1454 else1455 adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;1456 1457 mwifiex_dbg(adapter, CMD,1458 "cmd: ps_bitmap=%#x\n", ps_bitmap);1459 1460 if (pm_cfg) {1461 /* This section is for get power save mode */1462 if (ps_bitmap & BITMAP_STA_PS)1463 pm_cfg->param.ps_mode = 1;1464 else1465 pm_cfg->param.ps_mode = 0;1466 }1467 }1468 return 0;1469}1470 1471/*1472 * This function prepares command to get hardware specifications.1473 *1474 * Preparation includes -1475 * - Setting command ID, action and proper size1476 * - Setting permanent address parameter1477 * - Ensuring correct endian-ness1478 */1479int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,1480 struct host_cmd_ds_command *cmd)1481{1482 struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec;1483 1484 cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC);1485 cmd->size =1486 cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN);1487 memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN);1488 1489 return 0;1490}1491 1492/*1493 * This function handles the command response of get hardware1494 * specifications.1495 *1496 * Handling includes changing the header fields into CPU format1497 * and saving/updating the following parameters in driver -1498 * - Firmware capability information1499 * - Firmware band settings1500 * - Ad-hoc start band and channel1501 * - Ad-hoc 11n activation status1502 * - Firmware release number1503 * - Number of antennas1504 * - Hardware address1505 * - Hardware interface version1506 * - Firmware version1507 * - Region code1508 * - 11n capabilities1509 * - MCS support fields1510 * - MP end port1511 */1512int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,1513 struct host_cmd_ds_command *resp)1514{1515 struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;1516 struct mwifiex_adapter *adapter = priv->adapter;1517 struct mwifiex_ie_types_header *tlv;1518 struct hw_spec_api_rev *api_rev;1519 struct hw_spec_max_conn *max_conn;1520 u16 resp_size, api_id;1521 int i, left_len, parsed_len = 0;1522 1523 adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);1524 1525 if (IS_SUPPORT_MULTI_BANDS(adapter))1526 adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter);1527 else1528 adapter->fw_bands = BAND_B;1529 1530 adapter->config_bands = adapter->fw_bands;1531 1532 if (adapter->fw_bands & BAND_A) {1533 if (adapter->fw_bands & BAND_GN) {1534 adapter->config_bands |= BAND_AN;1535 adapter->fw_bands |= BAND_AN;1536 }1537 if (adapter->fw_bands & BAND_AN) {1538 adapter->adhoc_start_band = BAND_A | BAND_AN;1539 adapter->adhoc_11n_enabled = true;1540 } else {1541 adapter->adhoc_start_band = BAND_A;1542 }1543 priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A;1544 } else if (adapter->fw_bands & BAND_GN) {1545 adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN;1546 priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;1547 adapter->adhoc_11n_enabled = true;1548 } else if (adapter->fw_bands & BAND_G) {1549 adapter->adhoc_start_band = BAND_G | BAND_B;1550 priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;1551 } else if (adapter->fw_bands & BAND_B) {1552 adapter->adhoc_start_band = BAND_B;1553 priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;1554 }1555 1556 adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);1557 adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;1558 adapter->number_of_antenna =1559 le16_to_cpu(hw_spec->number_of_antenna) & 0xf;1560 1561 if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {1562 adapter->is_hw_11ac_capable = true;1563 1564 /* Copy 11AC cap */1565 adapter->hw_dot_11ac_dev_cap =1566 le32_to_cpu(hw_spec->dot_11ac_dev_cap);1567 adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap1568 & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;1569 adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap1570 & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK;1571 1572 /* Copy 11AC mcs */1573 adapter->hw_dot_11ac_mcs_support =1574 le32_to_cpu(hw_spec->dot_11ac_mcs_support);1575 adapter->usr_dot_11ac_mcs_support =1576 adapter->hw_dot_11ac_mcs_support;1577 } else {1578 adapter->is_hw_11ac_capable = false;1579 }1580 1581 resp_size = le16_to_cpu(resp->size) - S_DS_GEN;1582 if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {1583 /* we have variable HW SPEC information */1584 left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);1585 while (left_len > sizeof(struct mwifiex_ie_types_header)) {1586 tlv = (void *)&hw_spec->tlvs + parsed_len;1587 switch (le16_to_cpu(tlv->type)) {1588 case TLV_TYPE_API_REV:1589 api_rev = (struct hw_spec_api_rev *)tlv;1590 api_id = le16_to_cpu(api_rev->api_id);1591 switch (api_id) {1592 case KEY_API_VER_ID:1593 adapter->key_api_major_ver =1594 api_rev->major_ver;1595 adapter->key_api_minor_ver =1596 api_rev->minor_ver;1597 mwifiex_dbg(adapter, INFO,1598 "key_api v%d.%d\n",1599 adapter->key_api_major_ver,1600 adapter->key_api_minor_ver);1601 break;1602 case FW_API_VER_ID:1603 adapter->fw_api_ver =1604 api_rev->major_ver;1605 mwifiex_dbg(adapter, INFO,1606 "Firmware api version %d.%d\n",1607 adapter->fw_api_ver,1608 api_rev->minor_ver);1609 break;1610 case UAP_FW_API_VER_ID:1611 mwifiex_dbg(adapter, INFO,1612 "uAP api version %d.%d\n",1613 api_rev->major_ver,1614 api_rev->minor_ver);1615 break;1616 case CHANRPT_API_VER_ID:1617 mwifiex_dbg(adapter, INFO,1618 "channel report api version %d.%d\n",1619 api_rev->major_ver,1620 api_rev->minor_ver);1621 break;1622 case FW_HOTFIX_VER_ID:1623 mwifiex_dbg(adapter, INFO,1624 "Firmware hotfix version %d\n",1625 api_rev->major_ver);1626 break;1627 default:1628 mwifiex_dbg(adapter, FATAL,1629 "Unknown api_id: %d\n",1630 api_id);1631 break;1632 }1633 break;1634 case TLV_TYPE_MAX_CONN:1635 max_conn = (struct hw_spec_max_conn *)tlv;1636 adapter->max_p2p_conn = max_conn->max_p2p_conn;1637 adapter->max_sta_conn = max_conn->max_sta_conn;1638 mwifiex_dbg(adapter, INFO,1639 "max p2p connections: %u\n",1640 adapter->max_p2p_conn);1641 mwifiex_dbg(adapter, INFO,1642 "max sta connections: %u\n",1643 adapter->max_sta_conn);1644 break;1645 default:1646 mwifiex_dbg(adapter, FATAL,1647 "Unknown GET_HW_SPEC TLV type: %#x\n",1648 le16_to_cpu(tlv->type));1649 break;1650 }1651 parsed_len += le16_to_cpu(tlv->len) +1652 sizeof(struct mwifiex_ie_types_header);1653 left_len -= le16_to_cpu(tlv->len) +1654 sizeof(struct mwifiex_ie_types_header);1655 }1656 }1657 1658 mwifiex_dbg(adapter, INFO,1659 "info: GET_HW_SPEC: fw_release_number- %#x\n",1660 adapter->fw_release_number);1661 mwifiex_dbg(adapter, INFO,1662 "info: GET_HW_SPEC: permanent addr: %pM\n",1663 hw_spec->permanent_addr);1664 mwifiex_dbg(adapter, INFO,1665 "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n",1666 le16_to_cpu(hw_spec->hw_if_version),1667 le16_to_cpu(hw_spec->version));1668 1669 ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr);1670 adapter->region_code = le16_to_cpu(hw_spec->region_code);1671 1672 for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++)1673 /* Use the region code to search for the index */1674 if (adapter->region_code == region_code_index[i])1675 break;1676 1677 /* If it's unidentified region code, use the default (world) */1678 if (i >= MWIFIEX_MAX_REGION_CODE) {1679 adapter->region_code = 0x00;1680 mwifiex_dbg(adapter, WARN,1681 "cmd: unknown region code, use default (USA)\n");1682 }1683 1684 adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);1685 adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;1686 adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support;1687 1688 if (adapter->if_ops.update_mp_end_port)1689 adapter->if_ops.update_mp_end_port(adapter,1690 le16_to_cpu(hw_spec->mp_end_port));1691 1692 if (adapter->fw_api_ver == MWIFIEX_FW_V15)1693 adapter->scan_chan_gap_enabled = true;1694 1695 if (adapter->key_api_major_ver != KEY_API_VER_MAJOR_V2)1696 adapter->host_mlme_enabled = false;1697 1698 mwifiex_dbg(adapter, MSG, "host_mlme: %s, key_api: %d\n",1699 adapter->host_mlme_enabled ? "enable" : "disable",1700 adapter->key_api_major_ver);1701 1702 return 0;1703}1704 1705/* This function handles the command response of hs wakeup reason1706 * command.1707 */1708int mwifiex_ret_wakeup_reason(struct mwifiex_private *priv,1709 struct host_cmd_ds_command *resp,1710 struct host_cmd_ds_wakeup_reason *wakeup_reason)1711{1712 wakeup_reason->wakeup_reason =1713 resp->params.hs_wakeup_reason.wakeup_reason;1714 1715 return 0;1716}1717