| /** |
| * @file mlan_cmdevt.c |
| * |
| * @brief This file contains the handling of CMD/EVENT in MLAN |
| * |
| * |
| * Copyright (C) 2009, Marvell International Ltd. |
| * All Rights Reserved |
| * |
| */ |
| |
| /************************************************************* |
| Change Log: |
| 05/12/2009: initial version |
| ************************************************************/ |
| #include "mlan.h" |
| #include "mlan_join.h" |
| #include "mlan_util.h" |
| #include "mlan_fw.h" |
| #include "mlan_main.h" |
| #include "mlan_wmm.h" |
| #include "mlan_11n.h" |
| #include "mlan_11h.h" |
| #include "mlan_sdio.h" |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| /******************************************************* |
| Global Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| /** |
| * @brief This function initializes the command node. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pcmd_node A pointer to cmd_ctrl_node structure |
| * @param cmd_oid Cmd oid: treated as sub command |
| * @param pioctl_buf A pointer to MLAN IOCTl Request buffer |
| * @param pdata_buf A pointer to information buffer |
| * |
| * @return N/A |
| */ |
| void |
| wlan_init_cmd_node(IN pmlan_private pmpriv, |
| IN cmd_ctrl_node * pcmd_node, |
| IN t_u32 cmd_oid, |
| IN t_void * pioctl_buf, IN t_void * pdata_buf) |
| { |
| t_u8 *head_ptr = MNULL; |
| ENTER(); |
| |
| if (pcmd_node == MNULL) { |
| LEAVE(); |
| return; |
| } |
| pcmd_node->priv = pmpriv; |
| pcmd_node->cmd_oid = cmd_oid; |
| pcmd_node->pioctl_buf = pioctl_buf; |
| pcmd_node->pdata_buf = pdata_buf; |
| |
| pcmd_node->cmdbuf = pcmd_node->pmbuf; |
| |
| /* Make sure head_ptr for cmd buf is Align */ |
| head_ptr = (t_u8 *) ALIGN_ADDR(pcmd_node->cmdbuf->pbuf, HEADER_ALIGNMENT); |
| pcmd_node->cmdbuf->data_offset = |
| (t_u32) (head_ptr - pcmd_node->cmdbuf->pbuf); |
| memset(head_ptr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); |
| |
| /* Prepare mlan_buffer for command sending */ |
| pcmd_node->cmdbuf->buf_type = MLAN_BUF_TYPE_CMD; |
| pcmd_node->cmdbuf->data_offset += INTF_HEADER_LEN; |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function gets a free command node if available in |
| * command free queue. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or MNULL |
| */ |
| cmd_ctrl_node * |
| wlan_get_cmd_node(mlan_adapter * pmadapter) |
| { |
| cmd_ctrl_node *pcmd_node; |
| |
| ENTER(); |
| |
| if (pmadapter == MNULL) { |
| LEAVE(); |
| return MNULL; |
| } |
| |
| if (util_peek_list(&pmadapter->cmd_free_q, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock)) { |
| pcmd_node = (cmd_ctrl_node *) util_dequeue_list(&pmadapter->cmd_free_q, |
| pmadapter->callbacks. |
| moal_spin_lock, |
| pmadapter->callbacks. |
| moal_spin_unlock); |
| } else { |
| PRINTM(MERROR, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); |
| pcmd_node = MNULL; |
| } |
| |
| LEAVE(); |
| return pcmd_node; |
| } |
| |
| /** |
| * @brief This function cleans command node. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pcmd_node A pointer to cmd_ctrl_node structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_clean_cmd_node(pmlan_adapter pmadapter, cmd_ctrl_node * pcmd_node) |
| { |
| ENTER(); |
| |
| if (pcmd_node == MNULL) { |
| LEAVE(); |
| return; |
| } |
| pcmd_node->cmd_oid = 0; |
| pcmd_node->cmd_flag = 0; |
| pcmd_node->pioctl_buf = MNULL; |
| pcmd_node->pdata_buf = MNULL; |
| |
| if (pcmd_node->respbuf) { |
| pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, |
| pcmd_node->respbuf, 0, |
| MLAN_STATUS_SUCCESS); |
| pcmd_node->respbuf = MNULL; |
| } |
| |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function will return the pointer to the first entry in |
| * pending cmd which matches the given pioctl_req |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pioctl_req A pointer to mlan_ioctl_req buf |
| * |
| * @return A pointer to first entry match pioctl_req |
| */ |
| cmd_ctrl_node * |
| wlan_get_pending_ioctl_cmd(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) |
| { |
| cmd_ctrl_node *pcmd_node = MNULL; |
| |
| ENTER(); |
| |
| if (! |
| (pcmd_node = |
| (cmd_ctrl_node *) util_peek_list(&pmadapter->cmd_pending_q, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks. |
| moal_spin_unlock))) { |
| LEAVE(); |
| return MNULL; |
| } |
| while (pcmd_node != (cmd_ctrl_node *) & pmadapter->cmd_pending_q) { |
| if (pcmd_node->pioctl_buf == pioctl_req) { |
| LEAVE(); |
| return pcmd_node; |
| } |
| pcmd_node = pcmd_node->pnext; |
| } |
| LEAVE(); |
| return MNULL; |
| } |
| |
| /** |
| * @brief This function handles the command response of host_cmd |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| static mlan_status |
| wlan_ret_host_cmd(IN pmlan_private pmpriv, |
| IN HostCmd_DS_COMMAND * resp, IN mlan_ioctl_req * pioctl_buf) |
| { |
| mlan_ds_misc_cfg *misc; |
| t_u16 size = wlan_le16_to_cpu(resp->size); |
| |
| ENTER(); |
| |
| PRINTM(MINFO, "host command response size = %d\n", size); |
| size = MIN(size, MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (pioctl_buf) { |
| misc = (mlan_ds_misc_cfg *) pioctl_buf->pbuf; |
| misc->param.hostcmd.len = size; |
| memcpy(misc->param.hostcmd.cmd, (void *) resp, size); |
| } |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function sends host command to firmware. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param cmd A pointer to HostCmd_DS_COMMAND structure |
| * @param pdata_buf A pointer to data buffer |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_cmd_host_cmd(IN pmlan_private pmpriv, |
| IN HostCmd_DS_COMMAND * cmd, IN t_void * pdata_buf) |
| { |
| mlan_ds_misc_cmd *pcmd_ptr = (mlan_ds_misc_cmd *) pdata_buf; |
| |
| ENTER(); |
| |
| /* Copy the HOST command to command buffer */ |
| memcpy((void *) cmd, pcmd_ptr->cmd, pcmd_ptr->len); |
| PRINTM(MCMND, "Host command size = %d\n", pcmd_ptr->len); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function downloads a command to firmware. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param pcmd_node A pointer to cmd_ctrl_node structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_dnld_cmd_to_fw(IN mlan_private * pmpriv, IN cmd_ctrl_node * pcmd_node) |
| { |
| |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| HostCmd_DS_COMMAND *pcmd; |
| mlan_ioctl_req *pioctl_buf = MNULL; |
| t_u16 cmd_code; |
| t_u16 cmd_size; |
| t_u32 sec, usec; |
| |
| ENTER(); |
| |
| if (!pmadapter || !pcmd_node) { |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| pcmd = |
| (HostCmd_DS_COMMAND *) (pcmd_node->cmdbuf->pbuf + |
| pcmd_node->cmdbuf->data_offset); |
| if (pcmd_node->pioctl_buf != MNULL) |
| pioctl_buf = (mlan_ioctl_req *) pcmd_node->pioctl_buf; |
| |
| /* Sanity test */ |
| if (pcmd == MNULL || pcmd->size == 0) { |
| PRINTM(MERROR, "DNLD_CMD: pcmd is null or command size is zero, " |
| "Not sending\n"); |
| if (pioctl_buf != MNULL) |
| pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL, |
| wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Set command sequence number */ |
| pmadapter->seq_num++; |
| pcmd->seq_num = wlan_cpu_to_le16(pmadapter->seq_num); |
| |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->curr_cmd = pcmd_node; |
| wlan_release_cmd_lock(pmadapter); |
| |
| cmd_code = wlan_le16_to_cpu(pcmd->command); |
| cmd_size = wlan_le16_to_cpu(pcmd->size); |
| |
| /* Set BSS_NO_BITS to HostCmd */ |
| cmd_code = HostCmd_SET_BSS_NO(cmd_code, pcmd_node->priv->bss_num); |
| pcmd->command = wlan_cpu_to_le16(cmd_code); |
| |
| pcmd_node->cmdbuf->data_len = cmd_size; |
| |
| pmadapter->callbacks.moal_get_system_time(&sec, &usec); |
| PRINTM(MCMND, "DNLD_CMD (%lu.%lu): 0x%x, act 0x%x, len %d, seqno %d\n", |
| sec, usec, cmd_code, |
| wlan_le16_to_cpu(*(t_u16 *) ((t_u8 *) pcmd + S_DS_GEN)), cmd_size, |
| wlan_le16_to_cpu(pcmd->seq_num)); |
| DBG_HEXDUMP(MCMD_D, "DNLD_CMD", (t_u8 *) pcmd, cmd_size); |
| |
| /* Send the command to lower layer */ |
| |
| pcmd_node->cmdbuf->data_offset -= INTF_HEADER_LEN; |
| pcmd_node->cmdbuf->data_len += INTF_HEADER_LEN; |
| /* Extra header for SDIO is added here */ |
| ret = |
| wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_CMD, pcmd_node->cmdbuf, |
| MNULL); |
| |
| if (ret == MLAN_STATUS_FAILURE) { |
| PRINTM(MERROR, "DNLD_CMD: Host to Card Failed\n"); |
| if (pioctl_buf != MNULL) |
| pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL, |
| wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); |
| |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->curr_cmd = MNULL; |
| wlan_release_cmd_lock(pmadapter); |
| |
| pmadapter->dbg.num_cmd_host_to_card_failure++; |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Save the last command id and action to debug log */ |
| pmadapter->dbg.last_cmd_index = |
| (pmadapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; |
| pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index] = cmd_code; |
| pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index] = |
| wlan_le16_to_cpu(*(t_u16 *) ((t_u8 *) pcmd + S_DS_GEN)); |
| |
| /* Clear BSS_NO_BITS from HostCmd */ |
| cmd_code &= HostCmd_CMD_ID_MASK; |
| |
| /* Setup the timer after transmit command */ |
| pcb->moal_start_timer(pmadapter->pmlan_cmd_timer, MFALSE, MRVDRV_TIMER_10S); |
| |
| pmadapter->cmd_timer_is_set = MTRUE; |
| |
| ret = MLAN_STATUS_SUCCESS; |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function sends sleep confirm command to firmware. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_dnld_sleep_confirm_cmd(mlan_adapter * pmadapter) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| static t_u32 i = 0; |
| t_u16 cmd_len = 0; |
| HostCmd_DS_802_11_PS_MODE_ENH *pps_mode = MNULL; |
| opt_sleep_confirm_buffer *sleep_cfm_buf = |
| (opt_sleep_confirm_buffer *) (pmadapter->psleep_cfm->pbuf + |
| pmadapter->psleep_cfm->data_offset); |
| |
| ENTER(); |
| |
| cmd_len = sizeof(HostCmd_DS_COMMAND); |
| pps_mode = &sleep_cfm_buf->ps_cfm_sleep.params.psmode_enh; |
| sleep_cfm_buf->ps_cfm_sleep.seq_num = |
| wlan_cpu_to_le16(++pmadapter->seq_num); |
| pps_mode->params.sleep_cfm.resp_ctrl = wlan_cpu_to_le16(RESP_NEEDED); |
| DBG_HEXDUMP(MCMD_D, "SLEEP_CFM", &sleep_cfm_buf->ps_cfm_sleep, cmd_len); |
| |
| /* Send sleep confirm command to firmware */ |
| |
| pmadapter->psleep_cfm->data_len = cmd_len + INTF_HEADER_LEN; |
| ret = wlan_sdio_host_to_card(pmadapter, MLAN_TYPE_CMD, |
| pmadapter->psleep_cfm, MNULL); |
| |
| if (ret == MLAN_STATUS_FAILURE) { |
| PRINTM(MERROR, "SLEEP_CFM: failed\n"); |
| pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; |
| goto done; |
| } else { |
| if ((wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY))->bss_type == |
| MLAN_BSS_TYPE_STA) { |
| if (!pps_mode->params.sleep_cfm.resp_ctrl) { |
| /* Response is not needed for sleep confirm command */ |
| pmadapter->ps_state = PS_STATE_SLEEP; |
| } else { |
| pmadapter->ps_state = PS_STATE_SLEEP_CFM; |
| } |
| |
| if (pps_mode->params.sleep_cfm.resp_ctrl != RESP_NEEDED |
| && (pmadapter->is_hs_configured && |
| !pmadapter->sleep_period.period)) { |
| pmadapter->pm_wakeup_card_req = MTRUE; |
| wlan_host_sleep_activated_event(wlan_get_priv |
| (pmadapter, MLAN_BSS_TYPE_STA), |
| MTRUE); |
| } |
| } |
| #define NUM_SC_PER_LINE 16 |
| if (++i % NUM_SC_PER_LINE == 0) |
| PRINTM(MEVENT, "+\n"); |
| else |
| PRINTM(MEVENT, "+"); |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| |
| /** |
| * @brief Event handler |
| * |
| * @param priv A pointer to mlan_private structure |
| * @param event_id Event ID |
| * @param pmevent Event buffer |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_recv_event(pmlan_private priv, mlan_event_id event_id, t_void * pmevent) |
| { |
| pmlan_callbacks pcb = &priv->adapter->callbacks; |
| |
| ENTER(); |
| |
| if (pmevent) |
| /* The caller has provided the event. */ |
| pcb->moal_recv_event(priv->adapter->pmoal_handle, |
| (pmlan_event) pmevent); |
| else { |
| mlan_event mevent; |
| |
| memset(&mevent, 0, sizeof(mlan_event)); |
| mevent.bss_num = priv->bss_num; |
| mevent.event_id = event_id; |
| mevent.event_len = 0; |
| |
| pcb->moal_recv_event(priv->adapter->pmoal_handle, &mevent); |
| } |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function allocates the command buffer and links |
| * it to command free queue. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_alloc_cmd_buffer(IN mlan_adapter * pmadapter) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| cmd_ctrl_node *pcmd_array; |
| t_u32 buf_size; |
| t_u32 i; |
| |
| ENTER(); |
| |
| /* Allocate and initialize cmd_ctrl_node */ |
| buf_size = sizeof(cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; |
| ret = pcb->moal_malloc(buf_size, (t_u8 **) & pcmd_array); |
| if (ret != MLAN_STATUS_SUCCESS || !pcmd_array) { |
| PRINTM(MERROR, "ALLOC_CMD_BUF: Failed to allocate pcmd_array\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| pmadapter->cmd_pool = pcmd_array; |
| memset(pmadapter->cmd_pool, 0, buf_size); |
| |
| /* Allocate and initialize command buffers */ |
| for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { |
| if (! |
| (pcmd_array[i].pmbuf = |
| wlan_alloc_mlan_buffer(pcb, |
| (MRVDRV_SIZE_OF_CMD_BUFFER + |
| HEADER_ALIGNMENT)))) { |
| PRINTM(MERROR, "ALLOC_CMD_BUF: pcmd_buf: out of memory\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| } |
| |
| for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { |
| wlan_insert_cmd_to_free_q(pmadapter, &pcmd_array[i]); |
| } |
| ret = MLAN_STATUS_SUCCESS; |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function frees the command buffer. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_free_cmd_buffer(IN mlan_adapter * pmadapter) |
| { |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| cmd_ctrl_node *pcmd_array; |
| t_u32 i; |
| |
| ENTER(); |
| |
| /* Need to check if cmd pool is allocated or not */ |
| if (pmadapter->cmd_pool == MNULL) { |
| PRINTM(MINFO, "FREE_CMD_BUF: cmd_pool is Null\n"); |
| goto done; |
| } |
| |
| pcmd_array = pmadapter->cmd_pool; |
| |
| /* Release shared memory buffers */ |
| for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { |
| if (pcmd_array[i].pmbuf) { |
| PRINTM(MCMND, "Free all the command buffer.\n"); |
| wlan_free_mlan_buffer(pcb, pcmd_array[i].pmbuf); |
| pcmd_array[i].pmbuf = MNULL; |
| } |
| if (pcmd_array[i].respbuf) { |
| pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, |
| pcmd_array[i].respbuf, 0, |
| MLAN_STATUS_SUCCESS); |
| pcmd_array[i].respbuf = MNULL; |
| } |
| } |
| /* Release cmd_ctrl_node */ |
| if (pmadapter->cmd_pool) { |
| PRINTM(MCMND, "Free command pool.\n"); |
| pcb->moal_mfree((t_u8 *) pmadapter->cmd_pool); |
| pmadapter->cmd_pool = MNULL; |
| } |
| |
| done: |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handles events generated by firmware |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_process_event(pmlan_adapter pmadapter) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; |
| t_u32 eventcause = pmadapter->event_cause; |
| t_u32 bss_num = 0; |
| t_u32 in_ts_sec; |
| t_u32 in_ts_usec; |
| ENTER(); |
| |
| /* Save the last event to debug log */ |
| pmadapter->dbg.last_event_index = |
| (pmadapter->dbg.last_event_index + 1) % DBG_CMD_NUM; |
| pmadapter->dbg.last_event[pmadapter->dbg.last_event_index] = |
| (t_u16) eventcause; |
| |
| /* Get BSS number and corresponding priv */ |
| bss_num = EVENT_GET_BSS_NUM((t_u16) eventcause); |
| priv = pmadapter->priv[bss_num]; |
| if (!priv) { |
| bss_num = 0; |
| priv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| } |
| /* Clear BSS_NO_BITS from event */ |
| eventcause &= EVENT_ID_MASK; |
| if (pmbuf) |
| pmbuf->bss_num = bss_num; |
| |
| if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) { |
| pmadapter->callbacks.moal_get_system_time(&in_ts_sec, &in_ts_usec); |
| PRINTM(MEVENT, "%lu.%lu : Event: 0x%x\n", in_ts_sec, in_ts_usec, |
| eventcause); |
| } |
| |
| ret = priv->ops.process_event(priv); |
| |
| pmadapter->event_cause = 0; |
| pmadapter->pmlan_buffer_event = MNULL; |
| |
| if (pmbuf) { |
| pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, |
| 0, MLAN_STATUS_SUCCESS); |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function requests a lock on command queue. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_request_cmd_lock(IN mlan_adapter * pmadapter) |
| { |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| |
| ENTER(); |
| |
| /* Call MOAL spin lock callback function */ |
| pcb->moal_spin_lock(pmadapter->pmlan_cmd_lock); |
| |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function releases a lock on command queue. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_release_cmd_lock(IN mlan_adapter * pmadapter) |
| { |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| |
| ENTER(); |
| |
| /* Call MOAL spin unlock callback function */ |
| pcb->moal_spin_unlock(pmadapter->pmlan_cmd_lock); |
| |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function prepare the command before sending to firmware. |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param cmd_no Command number |
| * @param cmd_action Command action: GET or SET |
| * @param cmd_oid Cmd oid: treated as sub command |
| * @param pioctl_buf A pointer to MLAN IOCTl Request buffer |
| * @param pdata_buf A pointer to information buffer |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_prepare_cmd(IN mlan_private * pmpriv, |
| IN t_u16 cmd_no, |
| IN t_u16 cmd_action, |
| IN t_u32 cmd_oid, |
| IN t_void * pioctl_buf, IN t_void * pdata_buf) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| mlan_adapter *pmadapter = pmpriv->adapter; |
| cmd_ctrl_node *pcmd_node = MNULL; |
| HostCmd_DS_COMMAND *cmd_ptr = MNULL; |
| |
| ENTER(); |
| |
| /* Sanity test */ |
| if (pmadapter == MNULL) { |
| PRINTM(MERROR, "PREP_CMD: pmadapter is MNULL\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| if (pmadapter->surprise_removed) { |
| PRINTM(MERROR, "PREP_CMD: Card is Removed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| if (pmadapter->hw_status == WlanHardwareStatusReset) { |
| if (cmd_no != HostCmd_CMD_FUNC_INIT) { |
| PRINTM(MERROR, "PREP_CMD: FW is in reset state\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| } |
| |
| /* Get a new command node */ |
| pcmd_node = wlan_get_cmd_node(pmadapter); |
| |
| if (pcmd_node == MNULL) { |
| PRINTM(MERROR, "PREP_CMD: No free cmd node\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Initialize the command node */ |
| wlan_init_cmd_node(pmpriv, pcmd_node, cmd_oid, pioctl_buf, pdata_buf); |
| |
| if (pcmd_node->cmdbuf == MNULL) { |
| PRINTM(MERROR, "PREP_CMD: No free cmd buf\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| cmd_ptr = |
| (HostCmd_DS_COMMAND *) (pcmd_node->cmdbuf->pbuf + |
| pcmd_node->cmdbuf->data_offset); |
| cmd_ptr->command = cmd_no; |
| cmd_ptr->result = 0; |
| |
| /* Prepare command */ |
| if (cmd_no) |
| ret = |
| pmpriv->ops.prepare_cmd(pmpriv, cmd_no, cmd_action, cmd_oid, |
| pioctl_buf, pdata_buf, cmd_ptr); |
| else { |
| ret = wlan_cmd_host_cmd(pmpriv, cmd_ptr, pdata_buf); |
| pcmd_node->cmd_flag |= CMD_F_HOSTCMD; |
| } |
| |
| /* Return error, since the command preparation failed */ |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "PREP_CMD: Command 0x%x preparation failed\n", cmd_no); |
| wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Send command */ |
| if (cmd_no == HostCmd_CMD_802_11_SCAN) |
| wlan_queue_scan_cmd(pmpriv, pcmd_node); |
| else |
| wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, MTRUE); |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function inserts command node to cmd_free_q |
| * after cleaning it. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pcmd_node A pointer to cmd_ctrl_node structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_insert_cmd_to_free_q(IN mlan_adapter * pmadapter, |
| IN cmd_ctrl_node * pcmd_node) |
| { |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| mlan_ioctl_req *pioctl_req = MNULL; |
| ENTER(); |
| |
| if (pcmd_node == MNULL) |
| goto done; |
| if (pcmd_node->pioctl_buf) { |
| pioctl_req = (mlan_ioctl_req *) pcmd_node->pioctl_buf; |
| if (pioctl_req->status_code != MLAN_ERROR_NO_ERROR) |
| pcb->moal_ioctl_complete(pmadapter->pmoal_handle, |
| pioctl_req, MLAN_STATUS_FAILURE); |
| else |
| pcb->moal_ioctl_complete(pmadapter->pmoal_handle, |
| pioctl_req, MLAN_STATUS_SUCCESS); |
| } |
| /* Clean the node */ |
| wlan_clean_cmd_node(pmadapter, pcmd_node); |
| |
| /* Insert node into cmd_free_q */ |
| util_enqueue_list_tail(&pmadapter->cmd_free_q, |
| (pmlan_linked_list) pcmd_node, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| done: |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function queues the command to cmd list. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pcmd_node A pointer to cmd_ctrl_node structure |
| * @param add_tail Specify if the cmd needs to be queued in the header or tail |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_insert_cmd_to_pending_q(IN mlan_adapter * pmadapter, |
| IN cmd_ctrl_node * pcmd_node, IN t_u32 add_tail) |
| { |
| HostCmd_DS_COMMAND *pcmd = MNULL; |
| t_u16 command; |
| |
| ENTER(); |
| |
| if (pcmd_node == MNULL) { |
| PRINTM(MERROR, "QUEUE_CMD: pcmd_node is MNULL\n"); |
| goto done; |
| } |
| |
| pcmd = |
| (HostCmd_DS_COMMAND *) (pcmd_node->cmdbuf->pbuf + |
| pcmd_node->cmdbuf->data_offset); |
| if (pcmd == MNULL) { |
| PRINTM(MERROR, "QUEUE_CMD: pcmd is MNULL\n"); |
| goto done; |
| } |
| |
| command = wlan_le16_to_cpu(pcmd->command); |
| |
| /* Exit_PS command needs to be queued in the header always. */ |
| if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { |
| HostCmd_DS_802_11_PS_MODE_ENH *pm = &pcmd->params.psmode_enh; |
| if (wlan_le16_to_cpu(pm->action) == DIS_PS) { |
| if (pmadapter->ps_state != PS_STATE_AWAKE) |
| add_tail = MFALSE; |
| } |
| } |
| |
| if (add_tail) { |
| util_enqueue_list_tail(&pmadapter->cmd_pending_q, |
| (pmlan_linked_list) pcmd_node, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| } else { |
| util_enqueue_list_head(&pmadapter->cmd_pending_q, |
| (pmlan_linked_list) pcmd_node, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| } |
| |
| PRINTM(MCMND, "QUEUE_CMD: cmd=0x%x is queued\n", command); |
| |
| done: |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function executes next command in command |
| * pending queue. It will put firmware back to PS mode |
| * if applicable. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_exec_next_cmd(mlan_adapter * pmadapter) |
| { |
| mlan_private *priv = MNULL; |
| cmd_ctrl_node *pcmd_node = MNULL; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| HostCmd_DS_COMMAND *pcmd; |
| |
| ENTER(); |
| |
| /* Sanity test */ |
| if (pmadapter == MNULL) { |
| PRINTM(MERROR, "EXEC_NEXT_CMD: pmadapter is MNULL\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| /* Check if already in processing */ |
| if (pmadapter->curr_cmd) { |
| PRINTM(MERROR, "EXEC_NEXT_CMD: there is command in processing!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| wlan_request_cmd_lock(pmadapter); |
| /* Check if any command is pending */ |
| pcmd_node = (cmd_ctrl_node *) util_peek_list(&pmadapter->cmd_pending_q, |
| pmadapter->callbacks. |
| moal_spin_lock, |
| pmadapter->callbacks. |
| moal_spin_unlock); |
| |
| if (pcmd_node) { |
| pcmd = |
| (HostCmd_DS_COMMAND *) (pcmd_node->cmdbuf->pbuf + |
| pcmd_node->cmdbuf->data_offset); |
| priv = pcmd_node->priv; |
| |
| if (pmadapter->ps_state != PS_STATE_AWAKE) { |
| PRINTM(MERROR, |
| "Cannot send command in sleep state, this should not happen\n"); |
| wlan_release_cmd_lock(pmadapter); |
| goto done; |
| } |
| |
| util_unlink_list(&pmadapter->cmd_pending_q, |
| (pmlan_linked_list) pcmd_node, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| wlan_release_cmd_lock(pmadapter); |
| ret = wlan_dnld_cmd_to_fw(priv, pcmd_node); |
| priv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| /* Any command sent to the firmware when host is in sleep mode, should |
| de-configure host sleep */ |
| /* We should skip the host sleep configuration command itself though */ |
| if (priv && |
| (pcmd->command != |
| wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { |
| if (pmadapter->hs_activated == MTRUE) { |
| pmadapter->is_hs_configured = MFALSE; |
| wlan_host_sleep_activated_event(priv, MFALSE); |
| } |
| } |
| goto done; |
| } else { |
| wlan_release_cmd_lock(pmadapter); |
| } |
| ret = MLAN_STATUS_SUCCESS; |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function handles the command response |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_process_cmdresp(mlan_adapter * pmadapter) |
| { |
| HostCmd_DS_COMMAND *resp = MNULL; |
| mlan_private *pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u16 orig_cmdresp_no; |
| t_u16 cmdresp_no; |
| t_u16 cmdresp_result; |
| mlan_ioctl_req *pioctl_buf = MNULL; |
| mlan_callbacks *pcb = (mlan_callbacks *) & pmadapter->callbacks; |
| t_u32 sec, usec; |
| |
| ENTER(); |
| |
| /* Now we got response from FW, cancel the command timer */ |
| if (pmadapter->cmd_timer_is_set) { |
| /* Cancel command timeout timer */ |
| pcb->moal_stop_timer(pmadapter->pmlan_cmd_timer); |
| /* Cancel command timeout timer */ |
| pmadapter->cmd_timer_is_set = MFALSE; |
| } |
| |
| if (!pmadapter->curr_cmd || !pmadapter->curr_cmd->respbuf) { |
| resp = (HostCmd_DS_COMMAND *) pmadapter->upld_buf; |
| resp->command = wlan_le16_to_cpu(resp->command); |
| PRINTM(MERROR, "CMD_RESP: MNULL curr_cmd, 0x%x\n", resp->command); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| if (pmadapter->curr_cmd->pioctl_buf != MNULL) { |
| pioctl_buf = (mlan_ioctl_req *) pmadapter->curr_cmd->pioctl_buf; |
| } |
| |
| pmadapter->num_cmd_timeout = 0; |
| |
| DBG_HEXDUMP(MCMD_D, "CMD_RESP", |
| pmadapter->curr_cmd->respbuf->pbuf + |
| pmadapter->curr_cmd->respbuf->data_offset, pmadapter->upld_len); |
| |
| resp = |
| (HostCmd_DS_COMMAND *) (pmadapter->curr_cmd->respbuf->pbuf + |
| pmadapter->curr_cmd->respbuf->data_offset); |
| if (pmadapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { |
| /* Copy original response back to response buffer */ |
| wlan_ret_host_cmd(pmpriv, resp, pioctl_buf); |
| } |
| resp->command = wlan_le16_to_cpu(resp->command); |
| resp->size = wlan_le16_to_cpu(resp->size); |
| resp->seq_num = wlan_le16_to_cpu(resp->seq_num); |
| resp->result = wlan_le16_to_cpu(resp->result); |
| |
| orig_cmdresp_no = resp->command; |
| cmdresp_result = resp->result; |
| |
| /* Save the last command response to debug log */ |
| pmadapter->dbg.last_cmd_resp_index = |
| (pmadapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; |
| pmadapter->dbg.last_cmd_resp_id[pmadapter->dbg.last_cmd_resp_index] = |
| orig_cmdresp_no; |
| |
| pmadapter->callbacks.moal_get_system_time(&sec, &usec); |
| PRINTM(MCMND, "CMD_RESP (%lu.%lu): 0x%x, result %d, len %d, seqno %d\n", |
| sec, usec, orig_cmdresp_no, cmdresp_result, resp->size, |
| resp->seq_num); |
| |
| if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { |
| PRINTM(MERROR, "CMD_RESP: Invalid response to command!"); |
| if (pioctl_buf) { |
| pioctl_buf->status_code = MLAN_ERROR_FW_CMDRESP; |
| } |
| wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->curr_cmd = MNULL; |
| wlan_release_cmd_lock(pmadapter); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Get BSS number and corresponding priv */ |
| pmpriv = pmadapter->priv[HostCmd_GET_BSS_NO(resp->command)]; |
| if (!pmpriv) |
| pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| /* Clear RET_BIT & BSS_NO_BITS from HostCmd */ |
| resp->command &= HostCmd_CMD_ID_MASK; |
| cmdresp_no = resp->command; |
| |
| if (pmadapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { |
| pmadapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; |
| if ((cmdresp_result == HostCmd_RESULT_OK) |
| && (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) |
| ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); |
| } else { |
| /* handle response */ |
| ret = pmpriv->ops.process_cmdresp(pmpriv, cmdresp_no, resp, pioctl_buf); |
| } |
| |
| if ((pmadapter->hw_status == WlanHardwareStatusInitializing) && |
| (pmadapter->last_init_cmd == cmdresp_no)) { |
| if (cmdresp_result == HostCmd_RESULT_OK) |
| pmadapter->hw_status = WlanHardwareStatusInitdone; |
| else |
| wlan_init_fw_complete(pmadapter); |
| } |
| |
| if (pmadapter->curr_cmd) { |
| pioctl_buf = (mlan_ioctl_req *) pmadapter->curr_cmd->pioctl_buf; |
| if (pioctl_buf && (ret == MLAN_STATUS_SUCCESS)) |
| pioctl_buf->status_code = MLAN_ERROR_NO_ERROR; |
| |
| /* Clean up and put current command back to cmd_free_q */ |
| wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); |
| |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->curr_cmd = MNULL; |
| wlan_release_cmd_lock(pmadapter); |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function handles the timeout of command sending. |
| * It will re-send the same command again. |
| * |
| * @param function_context A pointer to function_context |
| * @return n/a |
| */ |
| t_void |
| wlan_cmd_timeout_func(t_void * function_context) |
| { |
| mlan_adapter *pmadapter = (mlan_adapter *) function_context; |
| cmd_ctrl_node *pcmd_node = MNULL; |
| mlan_ioctl_req *pioctl_buf = MNULL; |
| t_u32 sec, usec; |
| t_u8 i; |
| ENTER(); |
| |
| pmadapter->cmd_timer_is_set = MFALSE; |
| pmadapter->num_cmd_timeout++; |
| pmadapter->dbg.num_cmd_timeout++; |
| if (!pmadapter->curr_cmd) { |
| PRINTM(MWARN, "CurCmd Empty\n"); |
| goto exit; |
| } |
| pcmd_node = pmadapter->curr_cmd; |
| if (pcmd_node->pioctl_buf != MNULL) { |
| pioctl_buf = (mlan_ioctl_req *) pcmd_node->pioctl_buf; |
| pioctl_buf->status_code = MLAN_ERROR_CMD_TIMEOUT; |
| } |
| |
| if (pcmd_node) { |
| pmadapter->dbg.timeout_cmd_id = |
| pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index]; |
| pmadapter->dbg.timeout_cmd_act = |
| pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index]; |
| pmadapter->callbacks.moal_get_system_time(&sec, &usec); |
| PRINTM(MERROR, "Timeout cmd id (%lu.%lu) = 0x%x, act = 0x%x \n", sec, |
| usec, pmadapter->dbg.timeout_cmd_id, |
| pmadapter->dbg.timeout_cmd_act); |
| |
| PRINTM(MERROR, "num_data_h2c_failure = %d\n", |
| pmadapter->dbg.num_tx_host_to_card_failure); |
| PRINTM(MERROR, "num_cmd_h2c_failure = %d\n", |
| pmadapter->dbg.num_cmd_host_to_card_failure); |
| |
| PRINTM(MERROR, "num_cmd_timeout = %d\n", |
| pmadapter->dbg.num_cmd_timeout); |
| PRINTM(MERROR, "num_tx_timeout = %d\n", pmadapter->dbg.num_tx_timeout); |
| |
| PRINTM(MERROR, "last_cmd_index = %d\n", pmadapter->dbg.last_cmd_index); |
| PRINTM(MERROR, "last_cmd_id = "); |
| for (i = 0; i < DBG_CMD_NUM; i++) { |
| PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_cmd_id[i]); |
| } |
| PRINTM(MMSG, "\n"); |
| PRINTM(MERROR, "last_cmd_act = "); |
| for (i = 0; i < DBG_CMD_NUM; i++) { |
| PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_cmd_act[i]); |
| } |
| PRINTM(MMSG, "\n"); |
| PRINTM(MERROR, "last_cmd_resp_index = %d\n", |
| pmadapter->dbg.last_cmd_resp_index); |
| PRINTM(MERROR, "last_cmd_resp_id = "); |
| for (i = 0; i < DBG_CMD_NUM; i++) { |
| PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_cmd_resp_id[i]); |
| } |
| PRINTM(MMSG, "\n"); |
| |
| PRINTM(MERROR, "last_event_index = %d\n", |
| pmadapter->dbg.last_event_index); |
| PRINTM(MERROR, "last_event = "); |
| for (i = 0; i < DBG_CMD_NUM; i++) { |
| PRINTM(MERROR, "0x%x ", pmadapter->dbg.last_event[i]); |
| } |
| PRINTM(MMSG, "\n"); |
| |
| PRINTM(MERROR, "data_sent=%d cmd_sent=%d\n", pmadapter->data_sent, |
| pmadapter->cmd_sent); |
| |
| PRINTM(MERROR, "ps_mode=%d ps_state=%d\n", pmadapter->ps_mode, |
| pmadapter->ps_state); |
| } |
| if (pmadapter->hw_status == WlanHardwareStatusInitializing) |
| wlan_init_fw_complete(pmadapter); |
| exit: |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief Cancel all pending cmd. |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * |
| * @return N/A |
| */ |
| void |
| wlan_cancel_all_pending_cmd(pmlan_adapter pmadapter) |
| { |
| cmd_ctrl_node *pcmd_node = MNULL; |
| pmlan_callbacks pcb = &pmadapter->callbacks; |
| mlan_ioctl_req *pioctl_buf = MNULL; |
| |
| /* Cancel current cmd */ |
| if ((pmadapter->curr_cmd) && (pmadapter->curr_cmd->pioctl_buf)) { |
| pioctl_buf = (mlan_ioctl_req *) pmadapter->curr_cmd->pioctl_buf; |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->curr_cmd->pioctl_buf = MNULL; |
| wlan_release_cmd_lock(pmadapter); |
| pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; |
| pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, |
| MLAN_STATUS_FAILURE); |
| } |
| /* Cancel all pending command */ |
| while ((pcmd_node = |
| (cmd_ctrl_node *) util_peek_list(&pmadapter->cmd_pending_q, |
| pcb->moal_spin_lock, |
| pcb->moal_spin_unlock))) { |
| util_unlink_list(&pmadapter->cmd_pending_q, |
| (pmlan_linked_list) pcmd_node, pcb->moal_spin_lock, |
| pcb->moal_spin_unlock); |
| if (pcmd_node->pioctl_buf) { |
| pioctl_buf = (mlan_ioctl_req *) pcmd_node->pioctl_buf; |
| pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; |
| pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, |
| MLAN_STATUS_FAILURE); |
| pcmd_node->pioctl_buf = MNULL; |
| } |
| wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); |
| } |
| /* Cancel all pending scan command */ |
| while ((pcmd_node = |
| (cmd_ctrl_node *) util_peek_list(&pmadapter->scan_pending_q, |
| pcb->moal_spin_lock, |
| pcb->moal_spin_unlock))) { |
| util_unlink_list(&pmadapter->scan_pending_q, |
| (pmlan_linked_list) pcmd_node, pcb->moal_spin_lock, |
| pcb->moal_spin_unlock); |
| pcmd_node->pioctl_buf = MNULL; |
| wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); |
| } |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->scan_processing = MFALSE; |
| wlan_release_cmd_lock(pmadapter); |
| } |
| |
| /** |
| * @brief Cancel pending ioctl cmd. |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pioctl_req A pointer to mlan_ioctl_req buf |
| * |
| * @return N/A |
| */ |
| void |
| wlan_cancel_pending_ioctl(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) |
| { |
| pmlan_callbacks pcb = &pmadapter->callbacks; |
| cmd_ctrl_node *pcmd_node = MNULL; |
| |
| ENTER(); |
| |
| PRINTM(MCMND, "MOAL Cancel IOCTL: 0x%x sub_id=0x%x action=%d\n", |
| pioctl_req->req_id, *((t_u32 *) pioctl_req->pbuf), |
| (int) pioctl_req->action); |
| |
| if ((pmadapter->curr_cmd) && |
| (pmadapter->curr_cmd->pioctl_buf == pioctl_req)) { |
| wlan_request_cmd_lock(pmadapter); |
| pcmd_node = pmadapter->curr_cmd; |
| pcmd_node->pioctl_buf = MNULL; |
| wlan_release_cmd_lock(pmadapter); |
| } |
| |
| wlan_request_cmd_lock(pmadapter); |
| while ((pcmd_node = |
| wlan_get_pending_ioctl_cmd(pmadapter, pioctl_req)) != MNULL) { |
| util_unlink_list(&pmadapter->cmd_pending_q, |
| (pmlan_linked_list) pcmd_node, |
| pmadapter->callbacks.moal_spin_lock, |
| pmadapter->callbacks.moal_spin_unlock); |
| pcmd_node->pioctl_buf = MNULL; |
| wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); |
| } |
| wlan_release_cmd_lock(pmadapter); |
| if (pioctl_req->req_id == MLAN_IOCTL_SCAN) { |
| /* Cancel all pending scan command */ |
| while ((pcmd_node = |
| (cmd_ctrl_node *) util_peek_list(&pmadapter->scan_pending_q, |
| pcb->moal_spin_lock, |
| pcb->moal_spin_unlock))) { |
| util_unlink_list(&pmadapter->scan_pending_q, |
| (pmlan_linked_list) pcmd_node, pcb->moal_spin_lock, |
| pcb->moal_spin_unlock); |
| pcmd_node->pioctl_buf = MNULL; |
| wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); |
| } |
| wlan_request_cmd_lock(pmadapter); |
| pmadapter->scan_processing = MFALSE; |
| wlan_release_cmd_lock(pmadapter); |
| } |
| pioctl_req->status_code = MLAN_ERROR_CMD_CANCEL; |
| pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_req, |
| MLAN_STATUS_FAILURE); |
| |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief This function checks conditions and prepares to |
| * send sleep confirm command to firmware if OK. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * |
| * @return N/A |
| */ |
| t_void |
| wlan_check_ps_cond(mlan_adapter * pmadapter) |
| { |
| ENTER(); |
| |
| if (!pmadapter->cmd_sent && |
| !pmadapter->curr_cmd && !IS_CARD_RX_RCVD(pmadapter)) { |
| wlan_dnld_sleep_confirm_cmd(pmadapter); |
| } else { |
| PRINTM(MCMND, "Delay Sleep Confirm (%s%s%s)\n", |
| (pmadapter->cmd_sent) ? "D" : "", |
| (pmadapter->curr_cmd) ? "C" : "", |
| (IS_CARD_RX_RCVD(pmadapter)) ? "R" : ""); |
| } |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function sends the HS_ACTIVATED event to the application |
| * |
| * @param priv A pointer to mlan_private structure |
| * @param activated MTRUE if activated, MFALSE if de-activated |
| * |
| * @return None |
| */ |
| t_void |
| wlan_host_sleep_activated_event(pmlan_private priv, t_u8 activated) |
| { |
| ENTER(); |
| |
| if (activated) { |
| if (priv->adapter->is_hs_configured) { |
| priv->adapter->hs_activated = MTRUE; |
| PRINTM(MEVENT, "hs_actived\n"); |
| wlan_recv_event(priv, MLAN_EVENT_ID_DRV_HS_ACTIVATED, MNULL); |
| } else |
| PRINTM(MWARN, "hs_activated: HS not configured !!!\n"); |
| } else { |
| PRINTM(MEVENT, "hs_deactived\n"); |
| priv->adapter->hs_activated = MFALSE; |
| wlan_recv_event(priv, MLAN_EVENT_ID_DRV_HS_DEACTIVATED, MNULL); |
| } |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function sends the HS_WAKEUP event to the application |
| * |
| * @param priv A pointer to mlan_private structure |
| * |
| * @return None |
| */ |
| t_void |
| wlan_host_sleep_wakeup_event(pmlan_private priv) |
| { |
| ENTER(); |
| |
| if (priv->adapter->is_hs_configured) { |
| wlan_recv_event(priv, MLAN_EVENT_ID_FW_HS_WAKEUP, MNULL); |
| } else { |
| PRINTM(MWARN, "hs_wakeup: Host Sleep not configured !!!\n"); |
| } |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function handles the command response of hs_cfg |
| * |
| * @param pmpriv A pointer to mlan_private structure |
| * @param resp A pointer to HostCmd_DS_COMMAND |
| * @param pioctl_buf A pointer to mlan_ioctl_req structure |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| mlan_status |
| wlan_ret_802_11_hs_cfg(IN pmlan_private pmpriv, |
| IN HostCmd_DS_COMMAND * resp, |
| IN mlan_ioctl_req * pioctl_buf) |
| { |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = &resp->params.opt_hs_cfg; |
| ENTER(); |
| |
| if (phs_cfg->action == HS_ACTIVATE) { |
| wlan_host_sleep_activated_event(pmpriv, MTRUE); |
| goto done; |
| } else { |
| phs_cfg->params.hs_config.conditions = |
| wlan_le32_to_cpu(phs_cfg->params.hs_config.conditions); |
| PRINTM(MCMND, |
| "CMD_RESP: HS_CFG cmd reply result=%#x," |
| " conditions=0x%x gpio=0x%x gap=0x%x\n", resp->result, |
| phs_cfg->params.hs_config.conditions, |
| phs_cfg->params.hs_config.gpio, phs_cfg->params.hs_config.gap); |
| } |
| if (phs_cfg->params.hs_config.conditions != HOST_SLEEP_CFG_CANCEL) { |
| pmadapter->is_hs_configured = MTRUE; |
| } else { |
| pmadapter->is_hs_configured = MFALSE; |
| if (pmadapter->hs_activated) |
| wlan_host_sleep_activated_event(pmpriv, MFALSE); |
| } |
| done: |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Perform hs related activities on receving the power up interrupt |
| * |
| * @param pmadapter A pointer to the adapter structure |
| * @return N/A |
| */ |
| t_void |
| wlan_process_hs_config(pmlan_adapter pmadapter) |
| { |
| PRINTM(MINFO, |
| "Auto Cancelling host sleep since there is some interrupt from the firmware\n"); |
| wlan_pm_wakeup_card(pmadapter); |
| pmadapter->hs_activated = MFALSE; |
| pmadapter->is_hs_configured = MFALSE; |
| wlan_host_sleep_activated_event(wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY), |
| MFALSE); |
| return; |
| } |
| |
| /** |
| * @brief Check sleep confirm command response and set the state to ASLEEP |
| * |
| * @param pmadapter A pointer to the adapter structure |
| * @param pbuf A pointer to the command response buffer |
| * @param upld_len Command response buffer length |
| * @return N/A |
| */ |
| void |
| wlan_process_sleep_confirm_resp(pmlan_adapter pmadapter, t_u8 * pbuf, |
| t_u32 upld_len) |
| { |
| HostCmd_DS_COMMAND *cmd; |
| pmlan_private pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| |
| ENTER(); |
| |
| if (!upld_len) { |
| PRINTM(MERROR, "Command size is 0\n"); |
| LEAVE(); |
| return; |
| } |
| cmd = (HostCmd_DS_COMMAND *) pbuf; |
| cmd->result = wlan_le16_to_cpu(cmd->result); |
| cmd->command = wlan_le16_to_cpu(cmd->command); |
| |
| /* Get BSS number and corresponding priv */ |
| pmpriv = pmadapter->priv[HostCmd_GET_BSS_NO(cmd->command)]; |
| if (!pmpriv) |
| pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_TYPE_ANY); |
| /* Clear RET_BIT & BSS_NO_BITS from HostCmd */ |
| cmd->command &= HostCmd_CMD_ID_MASK; |
| |
| if (cmd->command != HostCmd_CMD_802_11_PS_MODE_ENH) { |
| PRINTM(MERROR, |
| "Received unexpected response for command %x, result = %x\n", |
| cmd->command, cmd->result); |
| return; |
| } |
| PRINTM(MEVENT, "#\n"); |
| if ((pmpriv->media_connected != MTRUE) || !pmadapter->sleep_period.period) |
| pmadapter->pm_wakeup_card_req = MTRUE; |
| |
| if (cmd->result != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "Sleep confirm command failed\n"); |
| LEAVE(); |
| return; |
| } |
| if (pmadapter->is_hs_configured && !pmadapter->sleep_period.period) { |
| wlan_host_sleep_activated_event(wlan_get_priv |
| (pmadapter, MLAN_BSS_TYPE_ANY), MTRUE); |
| } |
| pmadapter->ps_state = PS_STATE_SLEEP; |
| LEAVE(); |
| } |