mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-11-01 05:25:21 +00:00
19732f578f
Corresponding firmware version: SDIO-UART W8987 Firmware version 16.92.21.p99 PCIE-UART W8997 Firmware version 16.92.21.p84.4 SDIO-UART W8997 Firmware version 16.92.21.p84.4 SDIO-UART IW416 Firmware version 16.92.21.p84.3 SDIO_UART IW612 Firmware version 18.99.2.p19.6 SDIO-UART W8801 Firmware version 14.92.36.p182 SDIO-UART W9098 Firmware version 17.92.1.p136.128 PCIE-UART W9098 Firmware version 17.92.1.p136.129 Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
1446 lines
41 KiB
C
1446 lines
41 KiB
C
/** @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,
|
||
.support_11mc = 0,
|
||
};
|
||
#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,
|
||
.support_11mc = 0,
|
||
};
|
||
#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,
|
||
.support_11mc = 1,
|
||
};
|
||
#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,
|
||
.support_11mc = 1,
|
||
};
|
||
#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,
|
||
.support_11mc = 1,
|
||
};
|
||
#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,
|
||
.support_11mc = 1,
|
||
};
|
||
#endif
|
||
|
||
#ifdef USBIW624
|
||
static const struct _mlan_card_info mlan_card_info_usbIW624 = {
|
||
.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,
|
||
.support_11mc = 1,
|
||
};
|
||
#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
|
||
#if defined(USBIW624)
|
||
if (IS_USBIW624(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;
|
||
}
|
||
|
||
if (check_fw_status &&
|
||
(SyncFWHeader.status & MBIT(9))) {
|
||
PRINTM(MERROR,
|
||
"FW received Blk with SE_BOOT error 0x%x\n",
|
||
SyncFWHeader.status);
|
||
retries = 0;
|
||
ret = MLAN_STATUS_FAILURE;
|
||
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 (t_u32)(
|
||
(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) {
|
||
wlan_update_port_status(pmadapter, pusb_tx_aggr->port, MTRUE);
|
||
pmadapter->data_sent = wlan_usb_data_sent(pmadapter);
|
||
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:
|
||
wlan_update_port_status(pmadapter, pusb_tx_aggr->port,
|
||
MFALSE);
|
||
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:
|
||
wlan_update_port_status(pmadapter, pusb_tx_aggr->port,
|
||
MFALSE);
|
||
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
|
||
#ifdef USBIW624
|
||
case CARD_TYPE_USBIW624:
|
||
pmadapter->pcard_info = &mlan_card_info_usbIW624;
|
||
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;
|
||
t_u8 port_index = 0;
|
||
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;
|
||
port_index = wlan_get_port_index(pmadapter, pusb_tx_aggr->port);
|
||
if (pusb_tx_aggr->pmbuf_aggr &&
|
||
wlan_is_port_ready(pmadapter, port_index) &&
|
||
!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 used to check if any USB port still available
|
||
*
|
||
* @param pmadapter A pointer to mlan_adapter
|
||
*
|
||
* @return MTRUE--non of the port is available.
|
||
* MFALSE -- still have port available.
|
||
*/
|
||
inline t_u8 wlan_usb_data_sent(pmlan_adapter pmadapter)
|
||
{
|
||
int i;
|
||
for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) {
|
||
if (pmadapter->pcard_usb->usb_port_status[i] == MFALSE)
|
||
return MFALSE;
|
||
}
|
||
return MTRUE;
|
||
}
|
||
|
||
/**
|
||
* @brief This function resync the USB tx port
|
||
*
|
||
* @param pmadapter A pointer to mlan_adapter
|
||
*
|
||
* @return N/A
|
||
*/
|
||
void wlan_resync_usb_port(pmlan_adapter pmadapter)
|
||
{
|
||
t_u32 active_port = pmadapter->usb_tx_ports[0];
|
||
int i;
|
||
/* MC is enabled */
|
||
if (pmadapter->mc_status) {
|
||
for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM);
|
||
i++) {
|
||
if (pmadapter->priv[i]) {
|
||
if (((GET_BSS_ROLE(pmadapter->priv[i]) ==
|
||
MLAN_BSS_ROLE_UAP) &&
|
||
!pmadapter->priv[i]->uap_bss_started) ||
|
||
((GET_BSS_ROLE(pmadapter->priv[i]) ==
|
||
MLAN_BSS_ROLE_STA) &&
|
||
!pmadapter->priv[i]->media_connected)) {
|
||
PRINTM(MINFO,
|
||
"Set deactive interface to default EP\n");
|
||
pmadapter->priv[i]->port =
|
||
pmadapter->usb_tx_ports[0];
|
||
;
|
||
pmadapter->priv[i]->port_index = 0;
|
||
}
|
||
}
|
||
}
|
||
/** Enable all the ports */
|
||
for (i = 0; i < MAX_USB_TX_PORT_NUM; i++)
|
||
pmadapter->pcard_usb->usb_port_status[i] = MFALSE;
|
||
} else {
|
||
/* Get active port from connected interface */
|
||
for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM);
|
||
i++) {
|
||
if (pmadapter->priv[i]) {
|
||
if (((GET_BSS_ROLE(pmadapter->priv[i]) ==
|
||
MLAN_BSS_ROLE_UAP) &&
|
||
pmadapter->priv[i]->uap_bss_started) ||
|
||
((GET_BSS_ROLE(pmadapter->priv[i]) ==
|
||
MLAN_BSS_ROLE_STA) &&
|
||
pmadapter->priv[i]->media_connected)) {
|
||
active_port = pmadapter->priv[i]->port;
|
||
PRINTM(MEVENT, "active port=%d\n",
|
||
active_port);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/** set all the interface to the same port */
|
||
for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM);
|
||
i++) {
|
||
if (pmadapter->priv[i]) {
|
||
pmadapter->priv[i]->port = active_port;
|
||
pmadapter->priv[i]->port_index =
|
||
wlan_get_port_index(pmadapter,
|
||
active_port);
|
||
}
|
||
}
|
||
for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) {
|
||
if (active_port == pmadapter->usb_tx_ports[i])
|
||
pmadapter->pcard_usb->usb_port_status[i] =
|
||
MFALSE;
|
||
else
|
||
pmadapter->pcard_usb->usb_port_status[i] =
|
||
MTRUE;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
/**
|
||
* @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) || defined(USBIW624))
|
||
|| 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->pcard_usb->usb_port_status[pmpriv->port_index] =
|
||
MTRUE;
|
||
pmadapter->data_sent = wlan_usb_data_sent(pmadapter);
|
||
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->pcard_usb
|
||
->usb_port_status[pmpriv->port_index] = MFALSE;
|
||
pmadapter->data_sent = MFALSE;
|
||
break;
|
||
case MLAN_STATUS_PENDING:
|
||
pmadapter->pcard_usb
|
||
->usb_port_status[pmpriv->port_index] = MFALSE;
|
||
pmadapter->data_sent = MFALSE;
|
||
break;
|
||
case MLAN_STATUS_SUCCESS:
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
LEAVE();
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @brief This function handle event 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_evt_complete(pmlan_adapter pmadapter,
|
||
mlan_buffer *pmbuf, mlan_status status)
|
||
{
|
||
ENTER();
|
||
pmadapter->event_cause = 0;
|
||
pmadapter->pmlan_buffer_event = MNULL;
|
||
pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf,
|
||
pmadapter->rx_cmd_ep, status);
|
||
|
||
LEAVE();
|
||
return MLAN_STATUS_SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief This function handle 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_cmd_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_evt_complete,
|
||
.data_complete = wlan_usb_data_complete,
|
||
.cmdrsp_complete = wlan_usb_cmd_complete,
|
||
.handle_rx_packet = wlan_usb_handle_rx_packet,
|
||
|
||
.intf_header_len = USB_INTF_HEADER_LEN,
|
||
};
|