| /** @file mlan_usb.c |
| * |
| * @brief This file contains USB specific code |
| * |
| * |
| * Copyright 2008-2021 NXP |
| * |
| * This software file (the File) is distributed by NXP |
| * under the terms of the GNU General Public License Version 2, June 1991 |
| * (the License). You may use, redistribute and/or modify the File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available by writing to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the |
| * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| * |
| */ |
| |
| /******************************************************** |
| Change log: |
| 04/21/2009: initial version |
| ********************************************************/ |
| |
| #include "mlan.h" |
| #ifdef STA_SUPPORT |
| #include "mlan_join.h" |
| #endif |
| #include "mlan_util.h" |
| #include "mlan_init.h" |
| #include "mlan_fw.h" |
| #include "mlan_main.h" |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| #ifdef USB8801 |
| static const struct _mlan_card_info mlan_card_info_usb8801 = { |
| .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, |
| .v14_fw_api = 1, |
| .v16_fw_api = 0, |
| .supp_ps_handshake = 1, |
| .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, |
| }; |
| #endif |
| #ifdef USB8897 |
| static const struct _mlan_card_info mlan_card_info_usb8897 = { |
| .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, |
| .v16_fw_api = 0, |
| .supp_ps_handshake = 1, |
| .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, |
| }; |
| #endif |
| |
| #ifdef USB8997 |
| static const struct _mlan_card_info mlan_card_info_usb8997 = { |
| .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, |
| .v16_fw_api = 1, |
| .supp_ps_handshake = 1, |
| .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, |
| }; |
| #endif |
| |
| #ifdef USB8978 |
| static const struct _mlan_card_info mlan_card_info_usb8978 = { |
| .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, |
| .v16_fw_api = 1, |
| .supp_ps_handshake = 1, |
| .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, |
| }; |
| #endif |
| |
| #ifdef USB9098 |
| static const struct _mlan_card_info mlan_card_info_usb9098 = { |
| .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, |
| .v16_fw_api = 1, |
| .v17_fw_api = 1, |
| .supp_ps_handshake = 1, |
| .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, |
| }; |
| #endif |
| |
| #ifdef USB9097 |
| static const struct _mlan_card_info mlan_card_info_usb9097 = { |
| .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, |
| .v16_fw_api = 1, |
| .v17_fw_api = 1, |
| .supp_ps_handshake = 1, |
| .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, |
| }; |
| #endif |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| #if defined(USB9098) |
| /** |
| * @This function checks the chip revision id |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param rev_id A pointer to chip revision id |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_usb_check_revision(mlan_adapter *pmadapter, t_u32 *rev_id) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| pmlan_callbacks pcb = &pmadapter->callbacks; |
| mlan_buffer mbuf; |
| t_u8 *tx_buff = MNULL; |
| t_u8 *recv_buff = MNULL; |
| t_u8 tx_size = 16; |
| FWSyncPkt syncpkt; |
| |
| ENTER(); |
| /* Allocate memory for transmit */ |
| ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_TX_BUF_SIZE, |
| MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&tx_buff); |
| if ((ret != MLAN_STATUS_SUCCESS) || !tx_buff) { |
| PRINTM(MERROR, "Could not allocate buffer for FW download\n"); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| /* Allocate memory for receive */ |
| ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_RX_BUF_SIZE, |
| MLAN_MEM_DEF | MLAN_MEM_DMA, &recv_buff); |
| if ((ret != MLAN_STATUS_SUCCESS) || !recv_buff) { |
| PRINTM(MERROR, |
| "Could not allocate buffer for FW download response\n"); |
| goto cleanup; |
| } |
| memset(pmadapter, &syncpkt, 0, sizeof(FWSyncPkt)); |
| memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); |
| mbuf.pbuf = (t_u8 *)tx_buff; |
| mbuf.data_len = tx_size; |
| ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, &mbuf, |
| pmadapter->tx_cmd_ep, |
| MLAN_USB_BULK_MSG_TIMEOUT); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "check revision: write_data failed, ret %d\n", |
| ret); |
| goto cleanup; |
| } |
| memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); |
| mbuf.pbuf = (t_u8 *)recv_buff; |
| mbuf.data_len = 2048; |
| ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf, |
| pmadapter->rx_cmd_ep, |
| MLAN_USB_BULK_MSG_TIMEOUT); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "check revision: read_data failed, ret %d\n", |
| ret); |
| goto cleanup; |
| } |
| memcpy_ext(pmadapter, &syncpkt, recv_buff, sizeof(syncpkt), |
| sizeof(syncpkt)); |
| syncpkt.chip_rev = wlan_le32_to_cpu(syncpkt.chip_rev); |
| *rev_id = syncpkt.chip_rev & 0x000000ff; |
| PRINTM(MERROR, "chip_revision_id = %d\n", syncpkt.chip_rev); |
| cleanup: |
| if (recv_buff) |
| pcb->moal_mfree(pmadapter->pmoal_handle, recv_buff); |
| if (tx_buff) |
| pcb->moal_mfree(pmadapter->pmoal_handle, tx_buff); |
| |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief This function downloads FW blocks to device |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pmfw A pointer to firmware image |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_usb_prog_fw_w_helper(pmlan_adapter pmadapter, pmlan_fw_image pmfw) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| pmlan_callbacks pcb = &pmadapter->callbacks; |
| t_u8 *firmware = pmfw->pfw_buf, *RecvBuff; |
| t_u32 retries = MAX_FW_RETRY, DataLength; |
| t_u32 FWSeqNum = 0, TotalBytes = 0, DnldCmd = 0; |
| t_u8 *TxBuff = MNULL; |
| FWData *fwdata = MNULL; |
| FWSyncHeader SyncFWHeader; |
| t_u8 check_winner = 1; |
| t_u8 check_fw_status = MFALSE; |
| t_u8 mic_retry = MAX_FW_RETRY; |
| #if defined(USB9098) |
| t_u32 revision_id = 0; |
| #endif |
| |
| ENTER(); |
| |
| if (!firmware && !pcb->moal_get_fw_data) { |
| PRINTM(MMSG, "No firmware image found! Terminating download\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto fw_exit; |
| } |
| |
| /* Allocate memory for transmit */ |
| ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_TX_BUF_SIZE, |
| MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&TxBuff); |
| if ((ret != MLAN_STATUS_SUCCESS) || !TxBuff) { |
| PRINTM(MERROR, "Could not allocate buffer for FW download\n"); |
| goto fw_exit; |
| } |
| fwdata = (FWData *)TxBuff; |
| |
| /* Allocate memory for receive */ |
| ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_RX_BUF_SIZE, |
| MLAN_MEM_DEF | MLAN_MEM_DMA, &RecvBuff); |
| if ((ret != MLAN_STATUS_SUCCESS) || !RecvBuff) { |
| PRINTM(MERROR, |
| "Could not allocate buffer for FW download response\n"); |
| goto cleanup; |
| } |
| |
| if (!IS_USB_NEW_INIT(pmadapter->feature_control)) |
| check_winner = 0; |
| #if defined(USB9098) |
| if (IS_USB9098(pmadapter->card_type)) { |
| ret = wlan_usb_check_revision(pmadapter, &revision_id); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "Failed to get USB chip revision ID\n"); |
| goto cleanup; |
| } |
| /* Skyhawk A0, need to check both CRC and MIC error */ |
| if (revision_id >= CHIP_9098_REV_A0) |
| check_fw_status = MTRUE; |
| } |
| #endif |
| #if defined(USB9097) |
| if (IS_USB9097(pmadapter->card_type)) |
| check_fw_status = MTRUE; |
| #endif |
| do { |
| /* Send pseudo data to check winner status first */ |
| if (check_winner) { |
| memset(pmadapter, &fwdata->fw_header, 0, |
| sizeof(FWHeader)); |
| DataLength = 0; |
| } else { |
| /* Copy the header of the firmware data to get the |
| * length */ |
| if (firmware) |
| memcpy_ext(pmadapter, &fwdata->fw_header, |
| &firmware[TotalBytes], |
| sizeof(FWHeader), |
| sizeof(fwdata->fw_header)); |
| else |
| pcb->moal_get_fw_data(pmadapter->pmoal_handle, |
| TotalBytes, |
| sizeof(FWHeader), |
| (t_u8 *)&fwdata-> |
| fw_header); |
| |
| DataLength = |
| wlan_le32_to_cpu(fwdata->fw_header.data_length); |
| DnldCmd = wlan_le32_to_cpu(fwdata->fw_header.dnld_cmd); |
| TotalBytes += sizeof(FWHeader); |
| |
| /** CMD 7 don't have data_length field */ |
| if (DnldCmd == FW_CMD_4 || DnldCmd == FW_CMD_6 || |
| DnldCmd == FW_CMD_7 || DnldCmd == FW_CMD_10) |
| DataLength = 0; |
| |
| if (DataLength > |
| (FW_DNLD_TX_BUF_SIZE - sizeof(FWHeader))) { |
| PRINTM(MERROR, |
| "Invalid Data Legth read from FW\n"); |
| ret = MLAN_STATUS_FAILURE; |
| break; |
| } |
| |
| /* Copy the firmware data */ |
| if (firmware) |
| memcpy_ext(pmadapter, fwdata->data, |
| &firmware[TotalBytes], DataLength, |
| DataLength); |
| else |
| pcb->moal_get_fw_data(pmadapter->pmoal_handle, |
| TotalBytes, DataLength, |
| (t_u8 *)fwdata->data); |
| |
| fwdata->seq_num = wlan_cpu_to_le32(FWSeqNum); |
| TotalBytes += DataLength; |
| } |
| /* If the send/receive fails or CRC occurs then retry */ |
| while (retries) { |
| mlan_buffer mbuf; |
| int length = FW_DATA_XMIT_SIZE; |
| retries--; |
| |
| memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); |
| mbuf.pbuf = (t_u8 *)fwdata; |
| mbuf.data_len = length; |
| /* Send the firmware block */ |
| ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, |
| &mbuf, |
| pmadapter->tx_cmd_ep, |
| MLAN_USB_BULK_MSG_TIMEOUT); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, |
| "fw_dnld: write_data failed, ret %d\n", |
| ret); |
| continue; |
| } |
| |
| memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); |
| mbuf.pbuf = RecvBuff; |
| mbuf.data_len = FW_DNLD_RX_BUF_SIZE; |
| |
| /* Receive the firmware block response */ |
| ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, |
| &mbuf, |
| pmadapter->rx_cmd_ep, |
| MLAN_USB_BULK_MSG_TIMEOUT); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, |
| "fw_dnld: read_data failed, ret %d\n", |
| ret); |
| continue; |
| } |
| memcpy_ext(pmadapter, &SyncFWHeader, RecvBuff, |
| sizeof(FWSyncHeader), sizeof(SyncFWHeader)); |
| endian_convert_syncfwheader(&SyncFWHeader); |
| /* Check the first firmware block response for highest |
| * bit set */ |
| if (check_winner) { |
| if (SyncFWHeader.cmd & 0x80000000) { |
| PRINTM(MMSG, |
| "USB is not the winner 0x%x, returning success\n", |
| SyncFWHeader.cmd); |
| ret = MLAN_STATUS_SUCCESS; |
| goto cleanup; |
| } |
| PRINTM(MINFO, |
| "USB is the winner, start to download FW\n"); |
| check_winner = 0; |
| break; |
| } |
| |
| /* Check the firmware block response for CRC errors */ |
| if (SyncFWHeader.cmd) { |
| /* Check firmware block response for CRC and MIC |
| * errors */ |
| if (check_fw_status) { |
| if (SyncFWHeader.status & MBIT(0)) { |
| PRINTM(MERROR, |
| "FW received Blk with CRC error 0x%x offset=%d\n", |
| SyncFWHeader.status, |
| SyncFWHeader.offset); |
| ret = MLAN_STATUS_FAILURE; |
| continue; |
| } |
| if (SyncFWHeader.status & |
| (MBIT(6) | MBIT(7))) { |
| PRINTM(MERROR, |
| "FW received Blk with MIC error 0x%x offset\n", |
| SyncFWHeader.status, |
| SyncFWHeader.offset); |
| mic_retry--; |
| FWSeqNum = 0; |
| TotalBytes = 0; |
| ret = MLAN_STATUS_FAILURE; |
| continue; |
| } |
| } else { |
| PRINTM(MERROR, |
| "FW received Blk with CRC error 0x%x\n", |
| SyncFWHeader.cmd); |
| ret = MLAN_STATUS_FAILURE; |
| continue; |
| } |
| } |
| |
| retries = MAX_FW_RETRY; |
| break; |
| } |
| |
| FWSeqNum++; |
| PRINTM(MINFO, ".\n"); |
| |
| } while ((DnldCmd != FW_HAS_LAST_BLOCK) && retries && mic_retry); |
| |
| cleanup: |
| PRINTM(MMSG, "fw_dnld: %d bytes downloaded\n", TotalBytes); |
| |
| if (RecvBuff) |
| pcb->moal_mfree(pmadapter->pmoal_handle, RecvBuff); |
| if (TxBuff) |
| pcb->moal_mfree(pmadapter->pmoal_handle, TxBuff); |
| if (retries && mic_retry) { |
| ret = MLAN_STATUS_SUCCESS; |
| } |
| |
| fw_exit: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Get number of packets when deaggregated |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pdata A pointer to packet data |
| * @param aggr_pkt_len Aggregate packet length |
| * |
| * @return Number of packets |
| */ |
| static int |
| wlan_usb_deaggr_rx_num_pkts(pmlan_adapter pmadapter, t_u8 *pdata, |
| int aggr_pkt_len) |
| { |
| int pkt_count = 0, pkt_len; |
| RxPD *prx_pd; |
| |
| ENTER(); |
| while (aggr_pkt_len >= (int)sizeof(RxPD)) { |
| prx_pd = (RxPD *)pdata; |
| pkt_len = wlan_le16_to_cpu(prx_pd->rx_pkt_length) + |
| wlan_le16_to_cpu(prx_pd->rx_pkt_offset); |
| if (pkt_len == 0) /* blank RxPD can be at the end */ |
| break; |
| |
| ++pkt_count; |
| if (aggr_pkt_len == pkt_len) /* last packet has no padding */ |
| break; |
| |
| /* skip padding and goto next */ |
| if (pkt_len % |
| pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_align) |
| pkt_len += |
| (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl. |
| aggr_align - |
| (pkt_len % |
| pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl. |
| aggr_align)); |
| aggr_pkt_len -= pkt_len; |
| pdata += pkt_len; |
| } |
| LEAVE(); |
| return pkt_count; |
| } |
| |
| static inline t_u32 |
| usb_tx_aggr_pad_len(t_u32 len, usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| return (len % pusb_tx_aggr->aggr_ctrl.aggr_align) ? |
| (len + (pusb_tx_aggr->aggr_ctrl.aggr_align - |
| (len % pusb_tx_aggr->aggr_ctrl.aggr_align))) : len; |
| } |
| |
| /** |
| * @brief Copy pmbuf to aggregation buffer |
| * |
| * @param pmadapter Pointer to mlan_adapter structure |
| * @param pmbuf_aggr Pointer to aggregation buffer |
| * @param pmbuf Pointer to buffer to copy |
| * @param pusb_tx_aggr Pointer to usb_tx_aggr_params |
| * |
| * @return N/A |
| */ |
| static inline t_void |
| wlan_usb_tx_copy_buf_to_aggr(pmlan_adapter pmadapter, pmlan_buffer pmbuf_aggr, |
| pmlan_buffer pmbuf, |
| usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| ENTER(); |
| pmbuf_aggr->data_len = |
| usb_tx_aggr_pad_len(pmbuf_aggr->data_len, pusb_tx_aggr); |
| memcpy_ext(pmadapter, |
| pmbuf_aggr->pbuf + pmbuf_aggr->data_offset + |
| pmbuf_aggr->data_len, |
| pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len, |
| pmbuf->data_len); |
| pmbuf_aggr->data_len += pmbuf->data_len; |
| LEAVE(); |
| } |
| |
| #define MLAN_TYPE_AGGR_DATA_V2 11 |
| /** |
| * @brief Copy pmbuf to aggregation buffer |
| * |
| * @param pmadapter Pointer to mlan_adapter structure |
| * @param pmbuf_aggr Pointer to aggregation buffer |
| * @param pmbuf Pointer to buffer to copy |
| * @param last last packet flag |
| * @param pusb_tx_aggr Pointer to usb_tx_aggr_params |
| * |
| * @return N/A |
| */ |
| static inline t_void |
| wlan_usb_tx_copy_buf_to_aggr_v2(pmlan_adapter pmadapter, |
| pmlan_buffer pmbuf_aggr, pmlan_buffer pmbuf, |
| t_u8 last, usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| t_u8 *payload; |
| t_u16 offset; |
| |
| ENTER(); |
| pmbuf_aggr->data_len = |
| usb_tx_aggr_pad_len(pmbuf_aggr->data_len, pusb_tx_aggr); |
| memcpy_ext(pmadapter, |
| pmbuf_aggr->pbuf + pmbuf_aggr->data_offset + |
| pmbuf_aggr->data_len, |
| pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len, |
| pmbuf->data_len); |
| payload = pmbuf_aggr->pbuf + pmbuf_aggr->data_offset + |
| pmbuf_aggr->data_len; |
| if (last) { |
| offset = pmbuf->data_len; |
| *(t_u16 *)&payload[2] = |
| wlan_cpu_to_le16(MLAN_TYPE_AGGR_DATA_V2 | 0x80); |
| } else { |
| offset = usb_tx_aggr_pad_len(pmbuf->data_len, pusb_tx_aggr); |
| *(t_u16 *)&payload[2] = |
| wlan_cpu_to_le16(MLAN_TYPE_AGGR_DATA_V2); |
| } |
| *(t_u16 *)&payload[0] = wlan_cpu_to_le16(offset); |
| pmbuf_aggr->data_len += pmbuf->data_len; |
| PRINTM(MIF_D, "offset=%d len=%d\n", offset, pmbuf->data_len); |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Allocate Aggregation buffer and copy pending buffers to it. |
| * |
| * @param pmadapter Pointer to mlan_adapter structure |
| * @param pusb_tx_aggr Pointer to usb_tx_aggr_params |
| * |
| * @return Aggregation buffer |
| */ |
| static inline pmlan_buffer |
| wlan_usb_copy_buf_to_aggr(pmlan_adapter pmadapter, |
| usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| pmlan_buffer pmbuf_aggr = MNULL; |
| t_u8 i, use_count; |
| pmlan_buffer pmbuf_curr, pmbuf_next; |
| pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter, pusb_tx_aggr->aggr_len, |
| 0, MOAL_MALLOC_BUFFER); |
| if (pmbuf_aggr) { |
| pmbuf_curr = pusb_tx_aggr->pmbuf_aggr; |
| pmbuf_aggr->bss_index = pmbuf_curr->bss_index; |
| pmbuf_aggr->buf_type = pmbuf_curr->buf_type; |
| pmbuf_aggr->priority = pmbuf_curr->priority; |
| pmbuf_aggr->data_len = 0; |
| PRINTM(MIF_D, "use_count=%d,aggr_len=%d\n", |
| pmbuf_curr->use_count, pusb_tx_aggr->aggr_len); |
| use_count = pmbuf_curr->use_count; |
| for (i = 0; i <= use_count; i++) { |
| pmbuf_next = pmbuf_curr->pnext; |
| if (pusb_tx_aggr->aggr_ctrl.aggr_mode == |
| MLAN_USB_AGGR_MODE_LEN_V2) { |
| if (i == use_count) |
| wlan_usb_tx_copy_buf_to_aggr_v2 |
| (pmadapter, pmbuf_aggr, |
| pmbuf_curr, MTRUE, |
| pusb_tx_aggr); |
| else |
| wlan_usb_tx_copy_buf_to_aggr_v2 |
| (pmadapter, pmbuf_aggr, |
| pmbuf_curr, MFALSE, |
| pusb_tx_aggr); |
| } else |
| wlan_usb_tx_copy_buf_to_aggr(pmadapter, |
| pmbuf_aggr, |
| pmbuf_curr, |
| pusb_tx_aggr); |
| pmbuf_curr = pmbuf_next; |
| } |
| DBG_HEXDUMP(MIF_D, "USB AggrTx", |
| pmbuf_aggr->pbuf + pmbuf_aggr->data_offset, |
| pmbuf_aggr->data_len); |
| } |
| return pmbuf_aggr; |
| } |
| |
| /** |
| * @brief Link buffer into aggregate head buffer |
| * |
| * @param pmbuf_aggr Pointer to aggregation buffer |
| * @param pmbuf Pointer to buffer to add to the buffer list |
| * @param pusb_tx_aggr Pointer to usb_tx_aggr_params |
| */ |
| static inline t_void |
| wlan_usb_tx_link_buf_to_aggr(pmlan_buffer pmbuf_aggr, pmlan_buffer pmbuf, |
| usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| /* link new buf at end of list */ |
| pmbuf->pnext = pmbuf_aggr; |
| pmbuf->pprev = pmbuf_aggr->pprev; |
| pmbuf->pparent = pmbuf_aggr; |
| pmbuf_aggr->pprev->pnext = pmbuf; |
| pmbuf_aggr->pprev = pmbuf; |
| pmbuf_aggr->use_count++; |
| pusb_tx_aggr->aggr_len = |
| usb_tx_aggr_pad_len(pusb_tx_aggr->aggr_len, pusb_tx_aggr); |
| pusb_tx_aggr->aggr_len += pmbuf->data_len; |
| } |
| |
| /** |
| * @brief Send aggregated buffer |
| * |
| * @param pmadapter Pointer to mlan_adapter structure |
| * @param pusb_tx_aggr Pointer to usb_tx_aggr_params |
| */ |
| static inline t_void |
| wlan_usb_tx_send_aggr(pmlan_adapter pmadapter, usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| mlan_status ret; |
| pmlan_buffer pmbuf_aggr = pusb_tx_aggr->pmbuf_aggr; |
| ENTER(); |
| if (!pusb_tx_aggr->pmbuf_aggr) { |
| LEAVE(); |
| return; |
| } |
| |
| if (pusb_tx_aggr->pmbuf_aggr->use_count) { |
| pmbuf_aggr = wlan_usb_copy_buf_to_aggr(pmadapter, pusb_tx_aggr); |
| /* allocate new buffer for aggregation if not exist */ |
| if (!pmbuf_aggr) { |
| PRINTM(MERROR, |
| "Error allocating [usb_tx] aggr mlan_buffer.\n"); |
| pmadapter->dbg.num_tx_host_to_card_failure += |
| pusb_tx_aggr->pmbuf_aggr->use_count; |
| wlan_write_data_complete(pmadapter, |
| pusb_tx_aggr->pmbuf_aggr, |
| MLAN_STATUS_FAILURE); |
| pusb_tx_aggr->pmbuf_aggr = MNULL; |
| pusb_tx_aggr->aggr_len = 0; |
| LEAVE(); |
| return; |
| } else { |
| wlan_write_data_complete(pmadapter, |
| pusb_tx_aggr->pmbuf_aggr, |
| MLAN_STATUS_SUCCESS); |
| pusb_tx_aggr->pmbuf_aggr = MNULL; |
| pusb_tx_aggr->aggr_len = 0; |
| } |
| } else if (pusb_tx_aggr->aggr_ctrl.aggr_mode == |
| MLAN_USB_AGGR_MODE_LEN_V2) { |
| t_u8 *payload = pmbuf_aggr->pbuf + pmbuf_aggr->data_offset; |
| *(t_u16 *)&payload[0] = wlan_cpu_to_le16(pmbuf_aggr->data_len); |
| *(t_u16 *)&payload[2] = |
| wlan_cpu_to_le16(MLAN_TYPE_AGGR_DATA_V2 | 0x80); |
| PRINTM(MIF_D, "USB Send single packet len=%d\n", |
| pmbuf_aggr->data_len); |
| DBG_HEXDUMP(MIF_D, "USB Tx", |
| pmbuf_aggr->pbuf + pmbuf_aggr->data_offset, |
| pmbuf_aggr->data_len); |
| } |
| |
| if (pmbuf_aggr && pmbuf_aggr->data_len) { |
| pmadapter->data_sent = MTRUE; |
| ret = pmadapter->callbacks.moal_write_data_async(pmadapter-> |
| pmoal_handle, |
| pmbuf_aggr, |
| pusb_tx_aggr-> |
| port); |
| switch (ret) { |
| case MLAN_STATUS_PRESOURCE: |
| PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); |
| break; |
| case MLAN_STATUS_RESOURCE: |
| /* Shouldn't reach here due to next condition. */ |
| /* TODO: (maybe) How to requeue the aggregate? */ |
| /* It may occur when the pending tx urbs reach the high |
| * mark */ |
| /* Thus, block further pkts for a bit */ |
| PRINTM(MERROR, |
| "Error: moal_write_data_async failed: 0x%X\n", |
| ret); |
| pmadapter->dbg.num_tx_host_to_card_failure++; |
| pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; |
| wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); |
| break; |
| case MLAN_STATUS_FAILURE: |
| pmadapter->data_sent = MFALSE; |
| PRINTM(MERROR, |
| "Error: moal_write_data_async failed: 0x%X\n", |
| ret); |
| pmadapter->dbg.num_tx_host_to_card_failure++; |
| pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; |
| wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); |
| break; |
| case MLAN_STATUS_PENDING: |
| pmadapter->data_sent = MFALSE; |
| break; |
| case MLAN_STATUS_SUCCESS: |
| wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); |
| break; |
| default: |
| break; |
| } |
| |
| /* aggr_buf now sent to bus, prevent others from using it */ |
| pusb_tx_aggr->pmbuf_aggr = MNULL; |
| pusb_tx_aggr->aggr_len = 0; |
| } |
| LEAVE(); |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| |
| /** |
| * @brief This function get pcie device from card type |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_get_usb_device(pmlan_adapter pmadapter) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u16 card_type = pmadapter->card_type; |
| |
| ENTER(); |
| |
| ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, |
| sizeof(mlan_usb_card), |
| MLAN_MEM_DEF, |
| (t_u8 **)&pmadapter->pcard_usb); |
| if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_usb) { |
| PRINTM(MERROR, "Failed to allocate pcard_usb\n"); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| switch (card_type) { |
| #ifdef USB8801 |
| case CARD_TYPE_USB8801: |
| pmadapter->pcard_info = &mlan_card_info_usb8801; |
| break; |
| #endif |
| #ifdef USB8897 |
| case CARD_TYPE_USB8897: |
| pmadapter->pcard_info = &mlan_card_info_usb8897; |
| break; |
| #endif |
| #ifdef USB8997 |
| case CARD_TYPE_USB8997: |
| pmadapter->pcard_info = &mlan_card_info_usb8997; |
| break; |
| #endif |
| #ifdef USB8978 |
| case CARD_TYPE_USB8978: |
| pmadapter->pcard_info = &mlan_card_info_usb8978; |
| break; |
| #endif |
| #ifdef USB9098 |
| case CARD_TYPE_USB9098: |
| pmadapter->pcard_info = &mlan_card_info_usb9098; |
| break; |
| #endif |
| #ifdef USB9097 |
| case CARD_TYPE_USB9097: |
| pmadapter->pcard_info = &mlan_card_info_usb9097; |
| break; |
| #endif |
| default: |
| PRINTM(MERROR, "can't get right USB card type \n"); |
| ret = MLAN_STATUS_FAILURE; |
| break; |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function downloads firmware to card |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pmfw A pointer to firmware image |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_usb_dnld_fw(pmlan_adapter pmadapter, pmlan_fw_image pmfw) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| ret = wlan_usb_prog_fw_w_helper(pmadapter, pmfw); |
| if (ret != MLAN_STATUS_SUCCESS) { |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function deaggregates USB RX Data Packet from device |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pmbuf A pointer to the received buffer |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| wlan_usb_deaggr_rx_pkt(pmlan_adapter pmadapter, pmlan_buffer pmbuf) |
| { |
| const t_u8 zero_rx_pd[sizeof(RxPD)] = { 0 }; |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u32 curr_pkt_len; |
| RxPD *prx_pd; |
| t_u8 *pdata; |
| t_s32 aggr_len; |
| pmlan_buffer pdeaggr_buf; |
| |
| ENTER(); |
| |
| pdata = pmbuf->pbuf + pmbuf->data_offset; |
| prx_pd = (RxPD *)pdata; |
| curr_pkt_len = wlan_le16_to_cpu(prx_pd->rx_pkt_length) + |
| wlan_le16_to_cpu(prx_pd->rx_pkt_offset); |
| /* if non-aggregate, just send through, don’t process here */ |
| aggr_len = pmbuf->data_len; |
| if ((aggr_len == (t_s32)curr_pkt_len) || |
| (wlan_usb_deaggr_rx_num_pkts(pmadapter, pdata, aggr_len) == 1) || |
| (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable != MTRUE)) { |
| ret = wlan_handle_rx_packet(pmadapter, pmbuf); |
| LEAVE(); |
| return ret; |
| } |
| |
| while (aggr_len >= (t_s32)sizeof(RxPD)) { |
| /* check for (all-zeroes) termination RxPD */ |
| if (!memcmp(pmadapter, pdata, zero_rx_pd, sizeof(RxPD))) { |
| break; |
| } |
| |
| /* make new buffer and copy packet to it (including RxPD). |
| * Also, reserve headroom so that there must have space |
| * to change RxPD to TxPD for bridge packet in uAP mode */ |
| pdeaggr_buf = wlan_alloc_mlan_buffer(pmadapter, curr_pkt_len, |
| MLAN_RX_HEADER_LEN, |
| MOAL_ALLOC_MLAN_BUFFER); |
| if (pdeaggr_buf == MNULL) { |
| PRINTM(MERROR, |
| "Error allocating [usb_rx] deaggr mlan_buffer\n"); |
| ret = MLAN_STATUS_FAILURE; |
| break; |
| } |
| pdeaggr_buf->bss_index = pmbuf->bss_index; |
| pdeaggr_buf->buf_type = pmbuf->buf_type; |
| pdeaggr_buf->data_len = curr_pkt_len; |
| pdeaggr_buf->in_ts_sec = pmbuf->in_ts_sec; |
| pdeaggr_buf->in_ts_usec = pmbuf->in_ts_usec; |
| pdeaggr_buf->priority = pmbuf->priority; |
| memcpy_ext(pmadapter, |
| pdeaggr_buf->pbuf + pdeaggr_buf->data_offset, pdata, |
| curr_pkt_len, pdeaggr_buf->data_len); |
| |
| /* send new packet to processing */ |
| ret = wlan_handle_rx_packet(pmadapter, pdeaggr_buf); |
| if (ret == MLAN_STATUS_FAILURE) { |
| break; |
| } |
| /* last block has no padding bytes */ |
| if (aggr_len == (t_s32)curr_pkt_len) { |
| break; |
| } |
| |
| /* round up to next block boundary */ |
| if (curr_pkt_len % |
| pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_align) |
| curr_pkt_len += |
| (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl. |
| aggr_align - |
| (curr_pkt_len % |
| pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl. |
| aggr_align)); |
| /* point to next packet */ |
| aggr_len -= curr_pkt_len; |
| pdata += curr_pkt_len; |
| prx_pd = (RxPD *)pdata; |
| curr_pkt_len = wlan_le16_to_cpu(prx_pd->rx_pkt_length) + |
| wlan_le16_to_cpu(prx_pd->rx_pkt_offset); |
| } |
| |
| /* free original pmbuf (since not sent for processing) */ |
| pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, |
| pmadapter->rx_data_ep, ret); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function restore tx_pause flag |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pusb_tx_aggr A pointer to usb_tx_aggr_params |
| * |
| * @return MTRUE/MFALSE |
| */ |
| static t_u8 |
| wlan_is_port_tx_paused(pmlan_adapter pmadapter, |
| usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| mlan_private *pmpriv = MNULL; |
| t_u8 i; |
| t_u8 ret = MFALSE; |
| for (i = 0; i < pmadapter->priv_num; i++) { |
| pmpriv = pmadapter->priv[i]; |
| if (pmpriv && pmpriv->tx_pause && |
| (pmpriv->port == pusb_tx_aggr->port)) { |
| ret = MTRUE; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * @brief This function handles the timeout of usb tx aggregation. |
| * It will send the aggregate buffer being held. |
| * |
| * @param function_context A pointer to function_context |
| * @return N/A |
| */ |
| t_void |
| wlan_usb_tx_aggr_timeout_func(t_void *function_context) |
| { |
| usb_tx_aggr_params *pusb_tx_aggr = |
| (usb_tx_aggr_params *)function_context; |
| pmlan_adapter pmadapter = (mlan_adapter *)pusb_tx_aggr->phandle; |
| pmlan_callbacks pcb = &pmadapter->callbacks; |
| |
| ENTER(); |
| pcb->moal_spin_lock(pmadapter->pmoal_handle, pusb_tx_aggr->paggr_lock); |
| pusb_tx_aggr->aggr_hold_timer_is_set = MFALSE; |
| if (pusb_tx_aggr->pmbuf_aggr && !pmadapter->data_sent |
| && !wlan_is_port_tx_paused(pmadapter, pusb_tx_aggr) |
| ) |
| wlan_usb_tx_send_aggr(pmadapter, pusb_tx_aggr); |
| pcb->moal_spin_unlock(pmadapter->pmoal_handle, |
| pusb_tx_aggr->paggr_lock); |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function aggregates USB TX Data Packet to send to device |
| * |
| * @param pmadapter A pointer to mlan_adapter |
| * @param pmbuf A pointer to the transmit buffer |
| * @param tx_param A pointer to mlan_tx_param |
| * @param pusb_tx_aggr A pointer to usb_tx_aggr_params |
| * |
| * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE |
| */ |
| /* |
| * Non Scatter-Gather code creates a new large buffer where each incoming |
| * buffer's data contents are copied to (aligned to USB boundaries). |
| * The individual buffers are ALSO linked to the large buffer, |
| * in order to handle complete AFTER the aggregate is sent. |
| * pmbuf_aggr->data_len is used to keep track of bytes aggregated so far. |
| */ |
| mlan_status |
| wlan_usb_host_to_card_aggr(pmlan_adapter pmadapter, |
| pmlan_buffer pmbuf, |
| mlan_tx_param *tx_param, |
| usb_tx_aggr_params *pusb_tx_aggr) |
| { |
| pmlan_callbacks pcb = &pmadapter->callbacks; |
| pmlan_buffer pmbuf_aggr; |
| mlan_status ret = MLAN_STATUS_PENDING; |
| t_u32 next_pkt_len = (tx_param) ? tx_param->next_pkt_len : 0; |
| t_u32 aggr_len_counter = 0; |
| /* indicators */ |
| t_u8 f_precopy_cur_buf = 0; |
| t_u8 f_send_aggr_buf = 0; |
| t_u8 f_postcopy_cur_buf = 0; |
| t_u32 max_aggr_size = 0, max_aggr_num = 0; |
| |
| ENTER(); |
| |
| pcb->moal_spin_lock(pmadapter->pmoal_handle, pusb_tx_aggr->paggr_lock); |
| |
| /* stop timer while we process */ |
| if (pusb_tx_aggr->aggr_hold_timer_is_set) { |
| pcb->moal_stop_timer(pmadapter->pmoal_handle, |
| pusb_tx_aggr->paggr_hold_timer); |
| pusb_tx_aggr->aggr_hold_timer_is_set = MFALSE; |
| } |
| |
| pmbuf_aggr = pusb_tx_aggr->pmbuf_aggr; |
| |
| if (pusb_tx_aggr->aggr_ctrl.aggr_tmo == MLAN_USB_TX_AGGR_TIMEOUT_DYN) { |
| if (!pmbuf_aggr) { |
| /* Start aggr from min timeout value in micro sec */ |
| pusb_tx_aggr->hold_timeout_msec = |
| MLAN_USB_TX_MIN_AGGR_TIMEOUT; |
| } else { |
| /* Increase timeout in milisecond if pkts are |
| * consecutive */ |
| if (pusb_tx_aggr->hold_timeout_msec < |
| MLAN_USB_TX_MAX_AGGR_TIMEOUT) |
| pusb_tx_aggr->hold_timeout_msec++; |
| } |
| } else { |
| if (pusb_tx_aggr->aggr_ctrl.aggr_tmo) |
| pusb_tx_aggr->hold_timeout_msec = |
| pusb_tx_aggr->aggr_ctrl.aggr_tmo / 1000; |
| } |
| |
| max_aggr_size = max_aggr_num = pusb_tx_aggr->aggr_ctrl.aggr_max; |
| if (pusb_tx_aggr->aggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_NUM) { |
| max_aggr_size *= MAX(MLAN_USB_MAX_PKT_SIZE, |
| pusb_tx_aggr->aggr_ctrl.aggr_align); |
| } |
| if (pusb_tx_aggr->aggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_LEN) |
| max_aggr_num /= pusb_tx_aggr->aggr_ctrl.aggr_align; |
| else if (pusb_tx_aggr->aggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_LEN_V2) |
| max_aggr_num = MLAN_USB_TX_AGGR_MAX_NUM; |
| if (!pmbuf_aggr) { |
| /* use this buf to start linked list, that's it */ |
| pmbuf->pnext = pmbuf->pprev = pmbuf; |
| pmbuf_aggr = pmbuf; |
| pusb_tx_aggr->pmbuf_aggr = pmbuf_aggr; |
| pusb_tx_aggr->aggr_len = pmbuf->data_len; |
| pmbuf->flags |= MLAN_BUF_FLAG_USB_TX_AGGR; |
| |
| } else { |
| /* DECIDE what to do */ |
| aggr_len_counter = usb_tx_aggr_pad_len(pusb_tx_aggr->aggr_len, |
| pusb_tx_aggr); |
| |
| if ((aggr_len_counter + pmbuf->data_len) < max_aggr_size) { |
| f_precopy_cur_buf = 1; /* can fit current packet in aggr |
| */ |
| if (next_pkt_len) { |
| aggr_len_counter += |
| usb_tx_aggr_pad_len(pmbuf->data_len, |
| pusb_tx_aggr); |
| if ((aggr_len_counter + next_pkt_len) >= |
| max_aggr_size) |
| f_send_aggr_buf = 1; /* can't fit next |
| packet, send now |
| */ |
| } |
| } else { |
| /* can't fit current packet */ |
| if (pusb_tx_aggr->aggr_len) |
| f_send_aggr_buf = 1; /* send aggr first */ |
| f_postcopy_cur_buf = 1; /* then copy into new aggr_buf |
| */ |
| } |
| } |
| |
| /* For zero timeout and zero next packet length send pkt now */ |
| if (!pusb_tx_aggr->aggr_ctrl.aggr_tmo && !next_pkt_len) |
| f_send_aggr_buf = 1; |
| |
| /* PERFORM ACTIONS as decided */ |
| if (f_precopy_cur_buf) { |
| PRINTM(MIF_D, "%s: Precopy current buffer.\n", __FUNCTION__); |
| wlan_usb_tx_link_buf_to_aggr(pmbuf_aggr, pmbuf, pusb_tx_aggr); |
| } |
| if (pmbuf_aggr->use_count + 1 >= max_aggr_num) |
| f_send_aggr_buf = 1; |
| |
| if (pmbuf->flags & MLAN_BUF_FLAG_NULL_PKT |
| || pmbuf->flags & MLAN_BUF_FLAG_TCP_ACK) |
| f_send_aggr_buf = 1; |
| |
| if (f_send_aggr_buf) { |
| PRINTM(MIF_D, "%s: Send aggregate buffer.\n", __FUNCTION__); |
| wlan_usb_tx_send_aggr(pmadapter, pusb_tx_aggr); |
| pmbuf_aggr = pusb_tx_aggr->pmbuf_aggr; /* update ptr */ |
| } |
| |
| if (f_postcopy_cur_buf) { |
| PRINTM(MIF_D, "%s: Postcopy current buffer.\n", __FUNCTION__); |
| if (!pmbuf_aggr) { /* this is possible if just sent (above) */ |
| /* use this buf to start linked list */ |
| pmbuf->pnext = pmbuf->pprev = pmbuf; |
| pmbuf_aggr = pmbuf; |
| pusb_tx_aggr->pmbuf_aggr = pmbuf_aggr; |
| pusb_tx_aggr->aggr_len = pmbuf->data_len; |
| pmbuf->flags |= MLAN_BUF_FLAG_USB_TX_AGGR; |
| } |
| } |
| /* (re)start timer if there is something in the aggregation buffer */ |
| if (pmbuf_aggr && pmbuf_aggr->data_len) { |
| if (pusb_tx_aggr->aggr_ctrl.aggr_tmo) { |
| pcb->moal_start_timer(pmadapter->pmoal_handle, |
| pusb_tx_aggr->paggr_hold_timer, |
| MFALSE, |
| pusb_tx_aggr->hold_timeout_msec); |
| pusb_tx_aggr->aggr_hold_timer_is_set = MTRUE; |
| } |
| } |
| |
| pcb->moal_spin_unlock(pmadapter->pmoal_handle, |
| pusb_tx_aggr->paggr_lock); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function wakes up the card. |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param timeout set timeout flag |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_pm_usb_wakeup_card(pmlan_adapter pmadapter, t_u8 timeout) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| t_u32 age_ts_usec; |
| |
| ENTER(); |
| PRINTM(MEVENT, "Wakeup device...\n"); |
| pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, |
| &pmadapter->pm_wakeup_in_secs, |
| &age_ts_usec); |
| |
| /* Simulation of HS_AWAKE event */ |
| pmadapter->pm_wakeup_fw_try = MFALSE; |
| pmadapter->pm_wakeup_card_req = MFALSE; |
| /* TODO USB suspend/resume */ |
| pmadapter->ps_state = PS_STATE_AWAKE; |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function downloads data from driver to card. |
| * |
| * Both commands and data packets are transferred to the card |
| * by this function. This function adds the PCIE specific header |
| * to the front of the buffer before transferring. The header |
| * contains the length of the packet and the type. The firmware |
| * handles the packets based upon this set type. |
| * |
| * @param pmpriv A pointer to pmlan_private structure |
| * @param type data or command |
| * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include |
| * PCIE header) |
| * @param tx_param A pointer to mlan_tx_param (can be MNULL if type is |
| * command) |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| wlan_usb_host_to_card(pmlan_private pmpriv, t_u8 type, |
| mlan_buffer *pmbuf, mlan_tx_param *tx_param) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| usb_tx_aggr_params *pusb_tx_aggr = MNULL; |
| pmlan_adapter pmadapter = pmpriv->adapter; |
| |
| ENTER(); |
| |
| if (!pmbuf) { |
| PRINTM(MERROR, "Passed NULL pmbuf to %s\n", __FUNCTION__); |
| return MLAN_STATUS_FAILURE; |
| } |
| if (type == MLAN_TYPE_CMD |
| #if (defined(USB9098) || defined(USB9097)) |
| || type == MLAN_TYPE_VDLL |
| #endif |
| ) { |
| pmadapter->cmd_sent = MTRUE; |
| ret = pmadapter->callbacks.moal_write_data_async(pmadapter-> |
| pmoal_handle, |
| pmbuf, |
| pmadapter-> |
| tx_cmd_ep); |
| if (ret == MLAN_STATUS_FAILURE) |
| pmadapter->cmd_sent = MFALSE; |
| LEAVE(); |
| return ret; |
| } |
| pusb_tx_aggr = wlan_get_usb_tx_aggr_params(pmadapter, pmpriv->port); |
| if (pusb_tx_aggr) { |
| ret = wlan_usb_host_to_card_aggr(pmadapter, pmbuf, tx_param, |
| pusb_tx_aggr); |
| } else { |
| pmadapter->data_sent = MTRUE; |
| ret = pmadapter->callbacks.moal_write_data_async(pmadapter-> |
| pmoal_handle, |
| pmbuf, |
| pmpriv->port); |
| switch (ret) { |
| case MLAN_STATUS_PRESOURCE: |
| PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); |
| break; |
| case MLAN_STATUS_RESOURCE: |
| |
| break; |
| case MLAN_STATUS_FAILURE: |
| pmadapter->data_sent = MFALSE; |
| break; |
| case MLAN_STATUS_PENDING: |
| pmadapter->data_sent = MFALSE; |
| break; |
| case MLAN_STATUS_SUCCESS: |
| break; |
| default: |
| break; |
| } |
| |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function handle event/cmd complete |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pmbuf A pointer to the mlan_buffer |
| * @return N/A |
| */ |
| static mlan_status |
| wlan_usb_cmdevt_complete(pmlan_adapter pmadapter, |
| mlan_buffer *pmbuf, mlan_status status) |
| { |
| ENTER(); |
| |
| pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, |
| pmadapter->rx_cmd_ep, status); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handle data complete |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pmbuf A pointer to the mlan_buffer |
| * @return N/A |
| */ |
| static mlan_status |
| wlan_usb_data_complete(pmlan_adapter pmadapter, |
| mlan_buffer *pmbuf, mlan_status status) |
| { |
| ENTER(); |
| |
| pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, |
| pmadapter->rx_data_ep, status); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function handle receive packet |
| * |
| * @param pmadapter A pointer to mlan_adapter structure |
| * @param pmbuf A pointer to the mlan_buffer |
| * @return |
| */ |
| static mlan_status |
| wlan_usb_handle_rx_packet(mlan_adapter *pmadapter, pmlan_buffer pmbuf) |
| { |
| ENTER(); |
| |
| if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable == MTRUE) |
| return wlan_usb_deaggr_rx_pkt(pmadapter, pmbuf); |
| else |
| return wlan_handle_rx_packet(pmadapter, pmbuf); |
| |
| LEAVE(); |
| } |
| |
| mlan_adapter_operations mlan_usb_ops = { |
| .dnld_fw = wlan_usb_dnld_fw, |
| .host_to_card = wlan_usb_host_to_card, |
| .wakeup_card = wlan_pm_usb_wakeup_card, |
| .event_complete = wlan_usb_cmdevt_complete, |
| .data_complete = wlan_usb_data_complete, |
| .cmdrsp_complete = wlan_usb_cmdevt_complete, |
| .handle_rx_packet = wlan_usb_handle_rx_packet, |
| |
| .intf_header_len = USB_INTF_HEADER_LEN, |
| }; |