/******************************************************************************
|
*
|
* Copyright(c) 2020 Realtek Corporation.
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms of version 2 of the GNU General Public License as
|
* published by the Free Software Foundation.
|
*
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* more details.
|
*
|
*****************************************************************************/
|
#include "h2c_agg.h"
|
|
static u32 mac_h2c_agg_tx_single_normal_h2c(struct mac_ax_adapter *adapter, u8 *h2cb)
|
{
|
#if MAC_AX_PHL_H2C
|
struct rtw_h2c_pkt *tx_h2cb = (struct rtw_h2c_pkt *)h2cb;
|
#else
|
struct h2c_buf *tx_h2cb = (struct h2c_buf *)h2cb;
|
#endif
|
u32 ret = MACSUCCESS;
|
|
ret = h2c_pkt_build_txd(adapter, tx_h2cb);
|
if (ret)
|
goto fail;
|
|
#if MAC_AX_PHL_H2C
|
ret = PLTFM_TX(tx_h2cb);
|
#else
|
ret = PLTFM_TX(tx_h2cb->data, tx_h2cb->len);
|
if (ret)
|
goto fail;
|
h2cb_free(adapter, tx_h2cb);
|
#endif
|
|
fail:
|
return ret;
|
}
|
|
static u32 mac_h2c_agg_tx_single_agg_h2c(struct mac_ax_adapter *adapter, u8 *agg_h2cb)
|
{
|
u32 ret = MACSUCCESS;
|
struct h2c_info h2c_info = {0};
|
u8 *content;
|
u32 cur_h2cb_len = 0;
|
#if MAC_AX_PHL_H2C
|
struct rtw_h2c_pkt *tx_h2cb = (struct rtw_h2c_pkt *)agg_h2cb;
|
cur_h2cb_len = tx_h2cb->data_len;
|
#else
|
struct h2c_buf *tx_h2cb = (struct h2c_buf *)agg_h2cb;
|
cur_h2cb_len = tx_h2cb->len;
|
#endif
|
|
h2c_info.agg_en = 0;
|
h2c_info.content_len = cur_h2cb_len - FWCMD_HDR_LEN;
|
h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
|
h2c_info.h2c_class = FWCMD_H2C_CL_FW_OFLD;
|
h2c_info.h2c_func = FWCMD_H2C_FUNC_H2C_AGG;
|
h2c_info.rec_ack = 0;
|
h2c_info.done_ack = 0;
|
|
content = (u8 *)PLTFM_MALLOC(h2c_info.content_len);
|
if (!content)
|
return MACBUFALLOC;
|
|
PLTFM_MEMCPY(content, (agg_h2cb + FWCMD_HDR_LEN), h2c_info.content_len);
|
|
ret = mac_h2c_common(adapter, &h2c_info, (u32 *)content);
|
|
PLTFM_FREE(content, h2c_info.content_len);
|
|
return ret;
|
}
|
|
void mac_h2c_agg_enable(struct mac_ax_adapter *adapter, u8 enable)
|
{
|
PLTFM_MUTEX_LOCK(&adapter->h2c_agg_info.h2c_agg_lock);
|
adapter->h2c_agg_info.h2c_agg_en = enable;
|
PLTFM_MUTEX_UNLOCK(&adapter->h2c_agg_info.h2c_agg_lock);
|
}
|
|
u32 mac_h2c_agg_tx(struct mac_ax_adapter *adapter)
|
{
|
#if MAC_AX_PHL_H2C
|
struct rtw_h2c_pkt *agg_h2cb = NULL;
|
struct rtw_h2c_pkt *cur_h2cb = NULL;
|
#else
|
struct h2c_buf *agg_h2cb = NULL;
|
struct h2c_buf *cur_h2cb = NULL;
|
#endif
|
struct mac_ax_h2c_agg_node *cur_agg_node = NULL;
|
struct mac_ax_h2c_agg_node *tmp_agg_node = NULL;
|
u32 agg_len = 0;
|
u32 cur_h2cb_len = 0;
|
u32 cur_sub_h2c_len = 0;
|
u32 cur_sub_h2c_len_swap = 0;
|
u32 next_h2cb_len = 0;
|
u32 ret = MACSUCCESS;
|
u8 *cur_h2cb_data = NULL;
|
u8 *buf = NULL;
|
|
PLTFM_MUTEX_LOCK(&adapter->h2c_agg_info.h2c_agg_lock);
|
|
if (!adapter->h2c_agg_info.h2c_agg_queue_head)
|
goto fail;
|
|
cur_agg_node = adapter->h2c_agg_info.h2c_agg_queue_head;
|
agg_h2cb = NULL;
|
agg_len = 0;
|
|
while (cur_agg_node) {
|
#if MAC_AX_PHL_H2C
|
cur_h2cb = (struct rtw_h2c_pkt *)cur_agg_node->h2c_pkt;
|
cur_h2cb_len = cur_h2cb->data_len;
|
cur_h2cb_data = cur_h2cb->vir_data;
|
next_h2cb_len = (!cur_agg_node->next ? 0 :
|
((struct rtw_h2c_pkt *)
|
(cur_agg_node->next->h2c_pkt))->data_len);
|
#else
|
cur_h2cb = (struct h2c_buf *)cur_agg_node->h2c_pkt;
|
cur_h2cb_len = cur_h2cb->len;
|
cur_h2cb_data = cur_h2cb->data;
|
next_h2cb_len = (!cur_agg_node->next ? 0 :
|
((struct h2c_buf *)
|
(cur_agg_node->next->h2c_pkt))->len);
|
#endif
|
|
if (!agg_h2cb) {
|
if (!H2C_PKT_AGGREGATABLE(cur_h2cb_len) ||
|
!cur_agg_node->next ||
|
(cur_agg_node->next &&
|
!H2C_PKT_AGGREGATABLE(next_h2cb_len))) {
|
ret = mac_h2c_agg_tx_single_normal_h2c(adapter, (u8 *)cur_h2cb);
|
if (ret) {
|
PLTFM_MSG_ERR("tx normal h2c pkt fail\n");
|
goto fail;
|
}
|
|
tmp_agg_node = cur_agg_node;
|
cur_agg_node = cur_agg_node->next;
|
adapter->h2c_agg_info.h2c_agg_queue_head = cur_agg_node;
|
PLTFM_FREE(tmp_agg_node, sizeof(struct mac_ax_h2c_agg_node));
|
continue;
|
}
|
|
agg_h2cb = h2cb_alloc(adapter, H2CB_CLASS_LONG_DATA);
|
if (!agg_h2cb) {
|
PLTFM_MSG_ERR("allocate agg_h2c fail\n");
|
ret = MACNOBUF;
|
goto fail;
|
}
|
agg_len = WD_BODY_LEN + FWCMD_HDR_LEN;
|
}
|
|
cur_sub_h2c_len = ALIGN_4_BYTE(cur_h2cb_len);
|
|
buf = h2cb_put(agg_h2cb, cur_sub_h2c_len + H2C_AGG_SUB_HDR_LEN);
|
if (!buf) {
|
PLTFM_MSG_ERR("creat sub_h2c_buf in agg_h2cb get fail\n");
|
ret = MACNOITEM;
|
goto fail;
|
}
|
|
cur_sub_h2c_len_swap = cpu_to_le32(cur_sub_h2c_len);
|
PLTFM_MEMCPY(buf, &cur_sub_h2c_len_swap, H2C_AGG_SUB_HDR_LEN);
|
PLTFM_MEMCPY(buf + H2C_AGG_SUB_HDR_LEN, cur_h2cb_data, cur_h2cb_len);
|
agg_len = agg_len + cur_sub_h2c_len + H2C_AGG_SUB_HDR_LEN;
|
|
tmp_agg_node = cur_agg_node;
|
cur_agg_node = cur_agg_node->next;
|
adapter->h2c_agg_info.h2c_agg_queue_head = cur_agg_node;
|
PLTFM_FREE(tmp_agg_node, sizeof(struct mac_ax_h2c_agg_node));
|
|
#if MAC_AX_PHL_H2C
|
PLTFM_RECYCLE_H2C(cur_h2cb);
|
#else
|
h2cb_free(adapter, cur_h2cb);
|
#endif
|
|
if (!cur_agg_node ||
|
((agg_len + ALIGN_4_BYTE(next_h2cb_len) +
|
H2C_AGG_SUB_HDR_LEN) >= H2C_LONG_DATA_LEN)) {
|
ret = mac_h2c_agg_tx_single_agg_h2c(adapter, (u8 *)agg_h2cb);
|
if (ret) {
|
PLTFM_MSG_ERR("tx normal agg_h2c pkt fail\n");
|
goto fail;
|
}
|
agg_h2cb = NULL;
|
agg_len = 0;
|
}
|
}
|
|
fail:
|
if (ret) {
|
PLTFM_MSG_ERR("h2c agg error handle\n");
|
|
mac_h2c_agg_flush(adapter);
|
|
if (agg_h2cb) {
|
#if MAC_AX_PHL_H2C
|
PLTFM_RECYCLE_H2C((struct rtw_h2c_pkt *)agg_h2cb);
|
#else
|
h2cb_free(adapter, agg_h2cb);
|
#endif
|
}
|
} else {
|
adapter->h2c_agg_info.h2c_agg_queue_head = NULL;
|
adapter->h2c_agg_info.h2c_agg_queue_last = NULL;
|
adapter->h2c_agg_info.h2c_agg_pkt_num = 0;
|
}
|
PLTFM_MUTEX_UNLOCK(&adapter->h2c_agg_info.h2c_agg_lock);
|
return ret;
|
}
|
|
void mac_h2c_agg_flush(struct mac_ax_adapter *adapter)
|
{
|
struct mac_ax_h2c_agg_node *cur_agg_node = NULL;
|
struct mac_ax_h2c_agg_node *tmp_agg_node = NULL;
|
|
PLTFM_MUTEX_LOCK(&adapter->h2c_agg_info.h2c_agg_lock);
|
cur_agg_node = adapter->h2c_agg_info.h2c_agg_queue_head;
|
|
while (cur_agg_node) {
|
#if MAC_AX_PHL_H2C
|
PLTFM_RECYCLE_H2C((struct rtw_h2c_pkt *)cur_agg_node->h2c_pkt);
|
#else
|
h2cb_free(adapter, (struct h2c_buf *)cur_agg_node->h2c_pkt);
|
#endif
|
tmp_agg_node = cur_agg_node;
|
cur_agg_node = cur_agg_node->next;
|
PLTFM_FREE(tmp_agg_node, sizeof(struct mac_ax_h2c_agg_node));
|
}
|
|
adapter->h2c_agg_info.h2c_agg_queue_head = NULL;
|
adapter->h2c_agg_info.h2c_agg_queue_last = NULL;
|
adapter->h2c_agg_info.h2c_agg_pkt_num = 0;
|
PLTFM_MUTEX_UNLOCK(&adapter->h2c_agg_info.h2c_agg_lock);
|
}
|