1454 lines
48 KiB
C
1454 lines
48 KiB
C
/****************************************************************************
|
|
|
|
Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
|
|
|
|
This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
|
|
be copied by any method or incorporated into another program without
|
|
the express written consent of Aerospace C.Power. This Information or any portion
|
|
thereof remains the property of Aerospace C.Power. The Information contained herein
|
|
is believed to be accurate and Aerospace C.Power assumes no responsibility or
|
|
liability for its use in any way and conveys no license or title under
|
|
any patent or copyright and makes no representation or warranty that this
|
|
Information is free from patent or copyright infringement.
|
|
|
|
****************************************************************************/
|
|
|
|
#include "iot_config.h"
|
|
#include "mac_tx_hw.h"
|
|
#if HW_PLATFORM == HW_PLATFORM_SIMU
|
|
#include "simulator_txrx.h"
|
|
#endif
|
|
#include "iot_errno.h"
|
|
#include "iot_io.h"
|
|
#include "iot_system.h"
|
|
#include "mac_pdev.h"
|
|
#include "mac_desc_engine.h"
|
|
#include "iot_bitops.h"
|
|
#include "mac_hwq_mgr.h"
|
|
#include "hw_phy_init.h"
|
|
#include "mac_isr.h"
|
|
#include "hw_tonemap.h"
|
|
#include "sw_sched.h"
|
|
#include "phy_chn.h"
|
|
#include "mac_crc.h"
|
|
#include "mpdu_header.h"
|
|
#include "mac_desc_engine.h"
|
|
#include "iot_dbglog_api.h"
|
|
#include "iot_dbglog_parser.h"
|
|
#include "iot_plc_led.h"
|
|
#include "mac_rawdata_hw.h"
|
|
#include "mac_cfg.h"
|
|
#include "rate_control.h"
|
|
#include "mac_cert_test.h"
|
|
#include "mac_tx_power.h"
|
|
#include "chip_reg_base.h"
|
|
#include "mac_sched_hw.h"
|
|
#include "hw_war.h"
|
|
#include "phy_txrx_pwr.h"
|
|
#include "tx_desc_reg_api.h"
|
|
#include "phy_perf.h"
|
|
#include "mac_sched.h"
|
|
#include "mac_hplc_ext_api.h"
|
|
#include "mac_hplc_ext.h"
|
|
/*
|
|
* mpdu
|
|
*/
|
|
|
|
/* check if the queue has pending desc
|
|
* 0 - means no desc pending
|
|
* others - means have desc pending
|
|
*/
|
|
uint32_t mac_tx_hwq_mpdu_pending(
|
|
mac_queue_ctxt_t *tx_ctxt, \
|
|
uint32_t hwq_id)
|
|
{
|
|
tx_mpdu_start *mpdu;
|
|
tx_mpdu_end *end;
|
|
tx_dummy_node *dummy;
|
|
uint32_t tx_done = 1; /* init as 1 for warning */
|
|
|
|
for (dummy = tx_ctxt->cur_hdl_desc[hwq_id]; \
|
|
dummy; \
|
|
dummy = (tx_dummy_node *)dummy->next)
|
|
{
|
|
/* check if any desc is not done */
|
|
if (dummy->desc_type == DESC_TYPE_TX_DUMMY)
|
|
{
|
|
/* if dummy node desc */
|
|
//tx_done = dummy->tx_done;
|
|
continue;
|
|
}
|
|
else
|
|
if (dummy->desc_type == DESC_TYPE_TX_MPDU_START) {
|
|
/* if mpdu start desc */
|
|
mpdu = (tx_mpdu_start *)dummy;
|
|
end = mpdu->tx_status;
|
|
IOT_ASSERT(end);
|
|
tx_done = end->tx_done;
|
|
}
|
|
else {
|
|
/* should not other desc currently */
|
|
IOT_ASSERT(0);
|
|
}
|
|
if (!tx_done)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* return 0 - means E_OK
|
|
* else - means not all resource recycled
|
|
* if disable_hwq set, its mean mac reinit, need flush all sw pkt and dummy,
|
|
* then stop hwq; else keep hwq status.
|
|
*/
|
|
uint32_t mac_tx_flush_hwq(mac_queue_ctxt_t *tx_ctxt, uint32_t qid,
|
|
uint8_t disable_hwq)
|
|
{
|
|
uint32_t is_enable, ret;
|
|
|
|
is_enable = mac_txq_is_enable(qid);
|
|
|
|
if (tx_ctxt->q_depth[qid] == 0) {
|
|
IOT_ASSERT(is_enable == 0);
|
|
return ERR_OK;
|
|
}
|
|
|
|
if (is_enable) {
|
|
/* disable txq */
|
|
mac_txq_enable(qid, NULL);
|
|
}
|
|
|
|
/* mark tx done for each desc */
|
|
tx_mpdu_start *mpdu;
|
|
tx_mpdu_end *end;
|
|
tx_dummy_node *dummy;
|
|
tx_dummy_node *last_node;
|
|
|
|
/* enq a dummy at hwq last position, then comp func can free all mpdu */
|
|
last_node = (tx_dummy_node *)tx_ctxt->last_desc[qid];
|
|
if (last_node->desc_type != DESC_TYPE_TX_DUMMY) {
|
|
ret = mac_desc_get(&g_mac_desc_eng, PLC_TX_DUMMY_NODE_POOL, \
|
|
(void**)&dummy);
|
|
IOT_ASSERT(ret == 0);
|
|
dummy->desc_type = DESC_TYPE_TX_DUMMY;
|
|
mac_tx_hw_mpdu(tx_ctxt, qid, (tx_mpdu_start*)dummy);
|
|
}
|
|
|
|
for (dummy = tx_ctxt->cur_hdl_desc[qid]; \
|
|
dummy; \
|
|
dummy = (tx_dummy_node *)dummy->next)
|
|
{
|
|
#if DBG_MAC_HWQ_FLUSH
|
|
iot_printf("hwq[qid].desc=%d, tx_done=%d\n",
|
|
qid, dummy->desc_type, dummy->tx_done);
|
|
iot_dbglog_input(PLC_MAC_TX_HW_MID, DBGLOG_WARN, IOT_MAC_TX_HW_FLUSH,
|
|
3, qid, dummy->desc_type, dummy->tx_done);
|
|
#endif
|
|
/* check if any desc is not done */
|
|
if (dummy->desc_type == DESC_TYPE_TX_DUMMY)
|
|
{
|
|
/* if dummy node desc */
|
|
dummy->tx_done = 1;
|
|
}
|
|
else if (dummy->desc_type == DESC_TYPE_TX_MPDU_START)
|
|
{
|
|
/* if mpdu start desc */
|
|
mpdu = (tx_mpdu_start *)dummy;
|
|
end = mpdu->tx_status;
|
|
IOT_ASSERT(end);
|
|
/* mark done */
|
|
end->tx_done = 1;
|
|
end->tx_ok = 0;
|
|
end->is_filtered = 1;
|
|
end->mac_event_id = 1;
|
|
}
|
|
else
|
|
{
|
|
/* should not other desc currently */
|
|
IOT_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
ret = mac_tx_hw_mpdu_comp(qid, 0);
|
|
|
|
/* after flush, hwq must only one dummy in it, and hwq is closed */
|
|
last_node = (tx_dummy_node *)tx_ctxt->last_desc[qid];
|
|
if (last_node == NULL || last_node->desc_type != DESC_TYPE_TX_DUMMY || \
|
|
mac_txq_is_enable(qid) || tx_ctxt->q_depth[qid] != 1) {
|
|
IOT_ASSERT(0);
|
|
}
|
|
|
|
/* recover hwq */
|
|
if (is_enable && disable_hwq == 0) {
|
|
/* enq dummy and enable hwq */
|
|
mac_txq_enable(qid, tx_ctxt->last_desc[qid]);
|
|
} else {
|
|
/* flush dummy, ane keep hwq closed */
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_DUMMY_NODE_POOL, \
|
|
tx_ctxt->last_desc[qid]);
|
|
tx_ctxt->last_desc[qid] = NULL;
|
|
tx_ctxt->cur_hdl_desc[qid] = NULL;
|
|
tx_ctxt->q_depth[qid] = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
uint32_t mac_tx_flush_bcsma_pending_queue(mac_queue_ctxt_t *tx_ctxt)
|
|
{
|
|
uint32_t ret = 1;
|
|
|
|
IOT_ASSERT(tx_ctxt);
|
|
/* get swq type id */
|
|
for (uint32_t phase = PLC_PHASE_A; phase <= PLC_PHASE_CNT; phase++) {
|
|
uint32_t swq_id = mac_q_get_swq_type(PLC_BCN_REGION_BCSMA, phase,
|
|
LID_BCSMA_START);
|
|
if (tx_ctxt->q_depth[swq_id] > BCSMA_Q_WARNING_DEPTH) {
|
|
ret = mac_tx_flush_hwq(tx_ctxt, swq_id, false);
|
|
iot_printf("mac phase:%d flush bcsma pkt.\n", phase);
|
|
/* record flush tx bcsma cnt */
|
|
mac_add_flush_tx_bcsma_cnt();
|
|
}
|
|
}
|
|
/* if return 1 -> do not flush hwq.
|
|
* if return 0 -> already flush hwq
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
uint32_t mac_tx_flush_all_tdma_queue(mac_queue_ctxt_t *tx_ctxt)
|
|
{
|
|
for (uint32_t i = 0; i < tx_ctxt->last_hwq_id; i++)
|
|
{
|
|
/* look for every tdma queue */
|
|
if (!mac_txq_is_csma(i)) {
|
|
/* tdma */
|
|
if (mac_tx_hwq_mpdu_pending(tx_ctxt, i))
|
|
{
|
|
/* if pending desc */
|
|
mac_tx_flush_hwq(tx_ctxt, i, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t mac_tx_flush_all_queue(mac_queue_ctxt_t *tx_ctxt, \
|
|
uint32_t is_all_csma, uint32_t is_all_tdma)
|
|
{
|
|
extern mac_pdev_t* g_mac_pdev[];
|
|
mac_pdev_t *pdev = g_mac_pdev[PLC_PDEV_ID];
|
|
mac_vdev_t *vdev = pdev->vdev[0];
|
|
|
|
for (uint32_t i = 0; i <= tx_ctxt->last_hwq_id; i++)
|
|
{
|
|
if (vdev->stop_flag) {
|
|
/* if stop, need flush all */
|
|
mac_tx_flush_hwq(tx_ctxt, i, vdev->stop_flag);
|
|
continue;
|
|
}
|
|
/* look for every queue */
|
|
if ((mac_txq_is_csma(i) && is_all_csma) \
|
|
|| (!mac_txq_is_csma(i) && is_all_tdma))
|
|
{
|
|
/* check if packets exist, flush */
|
|
if (mac_tx_hwq_mpdu_pending(tx_ctxt, i))
|
|
{
|
|
/* if pending desc */
|
|
mac_tx_flush_hwq(tx_ctxt, i, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mac_tx_csma_dump()
|
|
{
|
|
mac_queue_ctxt_t *tx_ctxt = &(g_mac_pdev[PLC_PDEV_ID]->hwq_hdl);
|
|
uint32_t hwq_id, type;
|
|
uint32_t dump[9] = {0};
|
|
uint8_t idx = 0, *p = (uint8_t *)dump, *q = (uint8_t *)&dump[3];
|
|
uint8_t dummy_cnt, csma_cnt;
|
|
|
|
for (hwq_id = MAC_QUE_CSMA_A_0; hwq_id <= MAC_QUE_CSMA_C_3;
|
|
hwq_id++, idx++) {
|
|
if (tx_ctxt->q_depth[hwq_id] > 255) {
|
|
p[idx] = 255;
|
|
} else {
|
|
p[idx] = (uint8_t)tx_ctxt->q_depth[hwq_id];
|
|
}
|
|
tx_mpdu_start *cur_ptr = (tx_mpdu_start*)tx_ctxt->cur_hdl_desc[hwq_id];
|
|
dummy_cnt = 0;
|
|
csma_cnt = 0;
|
|
for (uint16_t j = 0; (j < tx_ctxt->q_depth[hwq_id]) && (j < 10) &&
|
|
(cur_ptr != NULL); j++) {
|
|
IOT_ASSERT(cur_ptr);
|
|
type = cur_ptr->desc_type;
|
|
if (type == DESC_TYPE_TX_DUMMY) {
|
|
if (((tx_dummy_node*)cur_ptr)->tx_done) {
|
|
dummy_cnt++;
|
|
if (dummy_cnt > 0x0F) {
|
|
dummy_cnt = 0x0F;
|
|
}
|
|
}
|
|
} else {
|
|
if (cur_ptr->tx_status->tx_done) {
|
|
csma_cnt++;
|
|
if (csma_cnt > 0x0F) {
|
|
csma_cnt = 0x0F;
|
|
}
|
|
}
|
|
}
|
|
cur_ptr = cur_ptr->next;
|
|
}
|
|
|
|
q[idx] = csma_cnt | (dummy_cnt << 4);
|
|
}
|
|
iot_printf("%s, msdu_tx_cnt:%lu, 0x%08x, 0x%08x, 0x%08x, 0x%08x, "
|
|
"0x%08x, 0x%08x\n", __FUNCTION__,
|
|
mac_get_msdu_tx_cnt(), dump[0], dump[1], dump[2], dump[3],
|
|
dump[4], dump[5]);
|
|
|
|
iot_dbglog_input(PLC_MAC_STATUSE_MID, DBGLOG_ERR,
|
|
IOT_MAC_STATUS13_ID, 7, mac_get_msdu_tx_cnt(),
|
|
dump[0], dump[1], dump[2], dump[3], dump[4], dump[5]);
|
|
|
|
dump[0] = mac_get_tx_msdu_comp_trigger_cnt();
|
|
dump[1] = mac_get_send_msdu_cnt();
|
|
dump[2] = mac_get_swq_to_hwq_cnt();
|
|
dump[3] = g_phy_cpu_share_ctxt.tx_td_start_status;
|
|
dump[4] = g_phy_cpu_share_ctxt.tx_td_start_cnt;
|
|
dump[5] = g_phy_cpu_share_ctxt.tx_td_fc_done_cnt;
|
|
dump[6] = mac_get_csma_ignore_cca_cnt();
|
|
dump[7] = g_phy_cpu_share_ctxt.phy_tx_abort_cnt;
|
|
iot_printf("tx_sts_dump. %u, %u, %u, 0x%08x, %u, %u, %u, %u\n",
|
|
dump[0], dump[1], dump[2], dump[3], dump[4],
|
|
dump[5], dump[6], dump[7]);
|
|
iot_dbglog_input(PLC_MAC_STATUSE_MID, DBGLOG_ERR,
|
|
IOT_MAC_STATUS16_ID, 8,
|
|
dump[0], dump[1], dump[2], dump[3], dump[4],
|
|
dump[5], dump[6], dump[7]);
|
|
}
|
|
|
|
uint32_t mac_tx_hw_mpdu(mac_queue_ctxt_t *tx_ctxt, uint32_t hwq_id,
|
|
tx_mpdu_start *mpdu)
|
|
{
|
|
#ifdef TX_BUF_DUMP
|
|
mem_dump((uint32_t *)mpdu->pb_list->pb_buf_addr, 16);
|
|
#endif
|
|
|
|
tx_mpdu_start *last_desc = NULL;
|
|
uint32_t is_csma;
|
|
|
|
if (mac_txq_is_enable(hwq_id))
|
|
{
|
|
/* if enabled before, there must be valid last_desc */
|
|
/* attach the mpdu after the last desc */
|
|
last_desc = (tx_mpdu_start*)tx_ctxt->last_desc[hwq_id];
|
|
IOT_ASSERT(last_desc);
|
|
void **ptr = (void**)&last_desc->next;
|
|
*ptr = (void*)mpdu;
|
|
}
|
|
else
|
|
{
|
|
/* if queue disabled */
|
|
if (NULL == tx_ctxt->cur_hdl_desc[hwq_id]) {
|
|
/* if never enabled, it would be empty,
|
|
* so just set cur ptr
|
|
*/
|
|
tx_ctxt->cur_hdl_desc[hwq_id] = (void*)mpdu;
|
|
}
|
|
else {
|
|
/* if have cur, it may be enabled before,
|
|
* so should have valid last_desc */
|
|
last_desc = (tx_mpdu_start*)tx_ctxt->last_desc[hwq_id];
|
|
IOT_ASSERT(last_desc && (last_desc->next == NULL));
|
|
void **ptr = (void**)&last_desc->next;
|
|
*ptr = (void*)mpdu;
|
|
}
|
|
if (mac_is_block_all_tx(tx_ctxt) == 0 &&
|
|
mpdu->desc_type != DESC_TYPE_TX_DUMMY) {
|
|
/* enable the queue if not dummy */
|
|
/* start from the enq mpdu */
|
|
mac_txq_enable(hwq_id, tx_ctxt->cur_hdl_desc[hwq_id]);
|
|
}
|
|
}
|
|
|
|
/* mpdu may be a list */
|
|
for (tx_mpdu_start *tmp = mpdu;
|
|
tmp != NULL; tmp = tmp->next) {
|
|
#if DEBUG_SSN
|
|
if (tmp->desc_type != DESC_TYPE_TX_DUMMY && tmp->pb_list) {
|
|
sg_sof_pb_hdr_t* pbh =
|
|
(sg_sof_pb_hdr_t*) &tmp->pb_list->sof_pb_header;
|
|
iot_printf("--ssn=%d,h=%d,e=%d\n", pbh->seq, pbh->mac_frame_start,
|
|
pbh->mac_frame_end);
|
|
}
|
|
#endif
|
|
last_desc = tmp;
|
|
tx_ctxt->q_depth[hwq_id]++;
|
|
}
|
|
tx_ctxt->last_desc[hwq_id] = (void*)last_desc;
|
|
|
|
/* flush hwq for block hw tx */
|
|
if (mac_is_block_all_tx(tx_ctxt) &&
|
|
mpdu->desc_type != DESC_TYPE_TX_DUMMY) {
|
|
IOT_ASSERT(mac_txq_is_enable(hwq_id) == 0);
|
|
mac_tx_flush_hwq(tx_ctxt, hwq_id, false);
|
|
}
|
|
|
|
/* dbg for tdma can not tx */
|
|
if (hwq_id == MAC_QUE_BCN_A || hwq_id == MAC_QUE_BCN_B ||
|
|
hwq_id == MAC_QUE_BCN_C) {
|
|
if (tx_ctxt->q_depth[MAC_QUE_BCN_A] > 3 ||
|
|
tx_ctxt->q_depth[MAC_QUE_BCN_B] > 3 ||
|
|
tx_ctxt->q_depth[MAC_QUE_BCN_C] > 3 ) {
|
|
mac_dump_sched_dbg();
|
|
}
|
|
}
|
|
|
|
/* bcn Q depth should less than 3, 2 mpdu and 1 dummy */
|
|
if ((hwq_id == MAC_QUE_BCN_A || hwq_id == MAC_QUE_BCN_B ||
|
|
hwq_id == MAC_QUE_BCN_C) &&
|
|
tx_ctxt->q_depth[hwq_id] > BCN_WARNING_DEPTH) {
|
|
tx_mpdu_start *cur_ptr = (tx_mpdu_start*)tx_ctxt->cur_hdl_desc[hwq_id];
|
|
uint16_t mpdu_hold_cnt = 0;
|
|
for (uint16_t i = 0; i < tx_ctxt->q_depth[hwq_id]; i++) {
|
|
IOT_ASSERT(cur_ptr);
|
|
uint32_t type = cur_ptr->desc_type;
|
|
uint32_t done = (type == DESC_TYPE_TX_MPDU_START) ?
|
|
cur_ptr->tx_status->tx_done : ((tx_dummy_node*)cur_ptr)->tx_done;
|
|
iot_printf("cur node type = %d, done = %d\n", type, done);
|
|
if (done == 0 && type == DESC_TYPE_TX_MPDU_START) {
|
|
mpdu_hold_cnt++;
|
|
}
|
|
cur_ptr = cur_ptr->next;
|
|
}
|
|
|
|
/* NOTE: sometimes, tXdone event has occurred,
|
|
* but has not yet been processed.
|
|
*/
|
|
if (mpdu_hold_cnt > 1) {
|
|
#if DEBUG_CANNOT_SENDOUT_PKT
|
|
//debug beacon can not send out
|
|
phy_dbg_sts_print();
|
|
mac_dump_buf(MAC_DUMP_TYPE_2, g_phy_cpu_share_ctxt.tx_fc,
|
|
FC_LEN/sizeof(uint32_t), (uint32_t *)RGF_HWQ_BASEADDR,
|
|
108, NULL, 0, true);
|
|
#else
|
|
IOT_ASSERT(0);
|
|
#endif
|
|
}
|
|
}
|
|
/* send the packet in hwq immediately */
|
|
mac_txq_trigger_send(hwq_id);
|
|
is_csma = mac_txq_is_csma(hwq_id);
|
|
if (is_csma) {
|
|
/* To make the false alarm of RX FC,
|
|
* we need to adjust the threshold of
|
|
* packet detection.
|
|
* Here we use TX hw queue depth as a
|
|
* reference. CSMA WARN as the base
|
|
*/
|
|
uint32_t cur_hwq_depth = min(tx_ctxt->q_depth[hwq_id],
|
|
CSMA_Q_WARNING_DEPTH);
|
|
uint32_t false_alarm = phy_get_periodic_fc_err_num();
|
|
|
|
if (cur_hwq_depth > CSMA_Q_NORMAL_DEPTH &&
|
|
false_alarm > CSMA_RX_FALSE_ALARM_THD) {
|
|
phy_pkt_thd_set(
|
|
(uint8_t) (cur_hwq_depth * 100 / CSMA_Q_WARNING_DEPTH));
|
|
}
|
|
}
|
|
|
|
#if DEBUG_CANNOT_SENDOUT_PKT
|
|
/* TODO: here just do csma check spur.
|
|
* mabey need do tdma check spur.
|
|
*/
|
|
mac_pdev_t *pdev = get_pdev_ptr(PLC_PDEV_ID);
|
|
if (is_csma) {
|
|
if (pdev->plc_tx_timer && !pdev->plc_tx_st) {
|
|
os_start_timer(pdev->plc_tx_timer, TX_HANG_CHECK_TIME_MS);
|
|
pdev->plc_tx_st = PLC_DEBUG_NORMAL_ST;
|
|
pdev->plc_debug_cnt = 0;
|
|
}
|
|
if (tx_ctxt->q_depth[hwq_id] > CSMA_Q_WARNING_DEPTH) {
|
|
// WAR if qny csma queue depth > threshold
|
|
phy_csma_ignore_cca(true);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
uint8_t mac_crc_set_fc_swcrc(uint32_t proto, tx_mpdu_start *cur_mpdu)
|
|
{
|
|
uint8_t ret = ERR_OK;
|
|
IOT_ASSERT(cur_mpdu);
|
|
void *fc = mac_tx_mpdu_start_get_fc_ptr(cur_mpdu);
|
|
|
|
switch (proto)
|
|
{
|
|
#if SUPPORT_SMART_GRID
|
|
case PLC_PROTO_TYPE_SG:
|
|
{
|
|
frame_control_t *sg_fc = \
|
|
(frame_control_t *)fc;
|
|
if (phy_get_tx_fc_swcrc_cfg())
|
|
{
|
|
sg_fc->vf.bcn.fccs = \
|
|
mac_crc_get_fc_swcrc(proto, sg_fc);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if SUPPORT_SOUTHERN_POWER_GRID
|
|
case PLC_PROTO_TYPE_SPG:
|
|
{
|
|
spg_frame_control_t *spg_fc = \
|
|
(spg_frame_control_t *)fc;
|
|
if (phy_get_tx_fc_swcrc_cfg())
|
|
{
|
|
uint32_t fccs = \
|
|
mac_crc_get_fc_swcrc(proto, spg_fc);
|
|
spg_fc->vf.bcn.fccs[0] = fccs & 0xFF;
|
|
spg_fc->vf.bcn.fccs[1] = (fccs >> 8) & 0xFF;
|
|
spg_fc->vf.bcn.fccs[2] = (fccs >> 16) & 0xFF;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if SUPPORT_GREEN_PHY
|
|
case PLC_PROTO_TYPE_GP:
|
|
{
|
|
hpav_frame_control *gp_fc = \
|
|
(hpav_frame_control *)fc;
|
|
if (phy_get_tx_fc_swcrc_cfg())
|
|
{
|
|
uint32_t fccs = \
|
|
mac_crc_get_fc_swcrc(proto, gp_fc);
|
|
gp_fc->fccs_av.fccs_av0 = fccs & 0xFF;
|
|
gp_fc->fccs_av.fccs_av1 = (fccs >> 8) & 0xFF;
|
|
gp_fc->fccs_av.fccs_av2 = (fccs >> 16) & 0xFF;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default:
|
|
ret = ERR_NOSUPP;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
uint8_t mac_crc_set_pb_swcrc(uint32_t proto, tx_pb_start *cur_pb_st, \
|
|
uint8_t delimite, uint32_t pb_sz)
|
|
{
|
|
uint32_t crc = 0;
|
|
|
|
IOT_ASSERT(cur_pb_st);
|
|
|
|
switch (proto)
|
|
{
|
|
#if SUPPORT_SMART_GRID
|
|
case PLC_PROTO_TYPE_SG:
|
|
break;
|
|
|
|
#endif
|
|
#if SUPPORT_SOUTHERN_POWER_GRID
|
|
case PLC_PROTO_TYPE_SPG:
|
|
break;
|
|
#endif
|
|
#if SUPPORT_GREEN_PHY
|
|
case PLC_PROTO_TYPE_GP:
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return ERR_NOSUPP;
|
|
}
|
|
|
|
if (phy_get_tx_pb_swcrc_cfg())
|
|
{
|
|
crc = mac_crc_get_pb_swcrc(proto, &cur_pb_st->sof_pb_header, \
|
|
(uint8_t *)cur_pb_st->pb_buf_addr, pb_sz, delimite);
|
|
|
|
cur_pb_st->sw_pb_crc = crc;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
uint16_t mac_tx_cal_fl(uint8_t is_bcast, uint8_t rate_id,
|
|
uint32_t proto_band_id, uint32_t tmi, uint32_t extmi, uint32_t pb_num)
|
|
{
|
|
uint32_t fl_ppb;
|
|
|
|
if (rate_id > MAC_BB_MAX_RATE) {
|
|
IOT_ASSERT(0);
|
|
return 0;
|
|
}
|
|
/* sw cfg fc frame len */
|
|
if (proto_band_id < IOT_SUPPORT_TONE_MULTI_BAND021) {
|
|
if (mac_get_fc_fl_cfg_status()) {
|
|
uint32_t hw_band_id = phy_proto_band_to_hw_band(proto_band_id);
|
|
fl_ppb = phy_get_flppb_from_table(hw_band_id, tmi, extmi);
|
|
return (uint16_t)(is_bcast ? \
|
|
((fl_ppb * pb_num + mac_get_tx_cifs()) / 10) : \
|
|
((fl_ppb * pb_num + mac_get_tx_rifs() + \
|
|
mac_get_tx_cifs() + \
|
|
mac_rx_get_delim(rate_id, hw_band_id)) / 10));
|
|
}
|
|
} else {
|
|
/*Note: proto_band_id must be the actual band id */
|
|
IOT_ASSERT(0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mac_tx_mpdu_fill_pb_start(tx_pb_start *pb,
|
|
tx_pb_start *next, void *buf_addr, uint32_t pb_seq,
|
|
uint32_t msdu_start, uint32_t msdu_end, uint32_t proto
|
|
)
|
|
{
|
|
(void)msdu_end;
|
|
(void)msdu_start;
|
|
IOT_ASSERT(pb);
|
|
pb->next_pb = next;
|
|
pb->pb_buf_addr = (uint32_t)buf_addr;
|
|
|
|
#if SUPPORT_SMART_GRID
|
|
if (PLC_PROTO_TYPE_SG == proto)
|
|
{
|
|
#if SUPPORT_IEEE_1901
|
|
i1901_sof_pb_hdr_t *hdr = (i1901_sof_pb_hdr_t *)&pb->sof_pb_header;
|
|
hdr->mac_frame_end = msdu_end & 0x1;
|
|
hdr->mac_frame_start = msdu_start & 0x1;
|
|
hdr->seq = pb_seq & 0x3f;
|
|
#else
|
|
sg_sof_pb_hdr_t *hdr =
|
|
(sg_sof_pb_hdr_t *)&pb->sof_pb_header; //pb header
|
|
hdr->mac_frame_end = msdu_end & 0x1;
|
|
hdr->mac_frame_start = msdu_start & 0x1;
|
|
hdr->seq = pb_seq & 0x3f;
|
|
#endif
|
|
} else
|
|
#endif
|
|
#if SUPPORT_SOUTHERN_POWER_GRID
|
|
if (PLC_PROTO_TYPE_SPG == proto)
|
|
{
|
|
spg_sof_pb_hdr_t *hdr =
|
|
(spg_sof_pb_hdr_t *)&pb->sof_pb_header; //pb header
|
|
hdr->seq = pb_seq & 0x3f;
|
|
} else
|
|
#endif
|
|
#if SUPPORT_GREEN_PHY
|
|
if (PLC_PROTO_TYPE_GP == proto)
|
|
{ /* green phy */
|
|
gp_sof_pb_hdr_t *hdr =
|
|
(gp_sof_pb_hdr_t *)&pb->sof_pb_header; //pb header
|
|
hdr->ssn = pb_seq & 0x3f;
|
|
hdr->vpf = 0;
|
|
hdr->mmqf = 0;
|
|
hdr->mfbf = 0;
|
|
hdr->opsf = 0;
|
|
hdr->mfbo = 0;
|
|
} else
|
|
#endif
|
|
{
|
|
//todo
|
|
}
|
|
}
|
|
|
|
uint32_t mac_tx_mpdu_form_pb_list(tx_pb_start **pb_list, \
|
|
iot_pkt_t *pkt_buf, uint32_t pb_num_per_mpdu, \
|
|
uint32_t bitmap, uint32_t pb_sz, uint32_t proto
|
|
)
|
|
{
|
|
tx_pb_start *pb_start = NULL;
|
|
tx_pb_start *tmp = NULL;
|
|
|
|
uint8_t pb_hdr_resv_crc_len = mac_get_pb_hdr_resv_crc_len(\
|
|
FC_DELIM_SOF, proto);
|
|
uint32_t buf_len = iot_pkt_block_len(pkt_buf, IOT_PKT_BLOCK_DATA);
|
|
uint32_t all_pb_num = iot_ceil(buf_len, (pb_sz - pb_hdr_resv_crc_len));
|
|
#if DEBUG_SPG_PER_MSDU_MPDU_TX && IOT_MP_SUPPORT
|
|
if (PLC_PROTO_TYPE_SPG == proto) {
|
|
if ((136 == pb_sz) && (all_pb_num > 1)) {
|
|
IOT_ASSERT_DUMP(0, &all_pb_num, 1);
|
|
}
|
|
}
|
|
#endif
|
|
for (uint32_t i = 0; i < all_pb_num; i++) {
|
|
if (0 == pb_num_per_mpdu || 0 == (bitmap >> i))
|
|
break;
|
|
if (bitmap & (0x1 << i)) {
|
|
pb_num_per_mpdu--;
|
|
if (pb_start == NULL) {
|
|
mac_desc_get(&g_mac_desc_eng, PLC_TX_PB_START_POOL, \
|
|
(void**)&tmp);
|
|
IOT_ASSERT(tmp != NULL);
|
|
pb_start = tmp;
|
|
}
|
|
/*is next pb exist*/
|
|
if (pb_num_per_mpdu != 0 && 0 != (bitmap >> (i + 1))) {
|
|
mac_desc_get(&g_mac_desc_eng, PLC_TX_PB_START_POOL, \
|
|
(void**)&tmp->next_pb);
|
|
IOT_ASSERT(tmp->next_pb);
|
|
}
|
|
else {
|
|
tmp->next_pb = NULL;
|
|
}
|
|
/*fill pb start desc*/
|
|
#if DEBUG_SSN
|
|
iot_printf("--send:0x%x,ssn=%d\n",bitmap,i);
|
|
#endif
|
|
mac_tx_mpdu_fill_pb_start(tmp, tmp->next_pb, iot_pkt_data(pkt_buf) + \
|
|
i * (pb_sz - pb_hdr_resv_crc_len), i, (i == 0 ? 1 : 0), \
|
|
((i == (all_pb_num - 1)) ? 1 : 0), proto);
|
|
mac_crc_set_pb_swcrc(proto, tmp, \
|
|
FC_DELIM_SOF, pb_sz);
|
|
tmp = tmp->next_pb;
|
|
}
|
|
}
|
|
if (tmp != NULL) {
|
|
iot_printf("bitmap=0x%x, pb_num_per_mpdu=%d, all_pb_num=%d"
|
|
"buf_len=%d\n",
|
|
bitmap, pb_num_per_mpdu, all_pb_num, buf_len);
|
|
IOT_ASSERT(0);
|
|
}
|
|
*pb_list = pb_start;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void mac_tx_mpdu_fill_total_len(tx_mpdu_start *mpdu, uint32_t len)
|
|
{
|
|
mpdu->total_bytes = len;
|
|
}
|
|
|
|
void mac_tx_mpdu_fill_proto(tx_mpdu_start *mpdu, uint32_t proto)
|
|
{
|
|
mpdu->proto_type = proto;
|
|
}
|
|
|
|
|
|
/* transform tx phase to led req */
|
|
static inline uint8_t mac_trans_tx_phase2led_req(uint32_t phase)
|
|
{
|
|
switch(phase){
|
|
case PLC_PHASE_A:
|
|
return IOT_PLC_LED_REQ_PLC_TX_PHASE_A;
|
|
break;
|
|
case PLC_PHASE_B:
|
|
return IOT_PLC_LED_REQ_PLC_TX_PHASE_B;
|
|
break;
|
|
case PLC_PHASE_C:
|
|
return IOT_PLC_LED_REQ_PLC_TX_PHASE_C;
|
|
break;
|
|
default:
|
|
IOT_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mac_tx_led_control(void *mpdu)
|
|
{
|
|
tx_mpdu_start *mpdu_st = (tx_mpdu_start *)mpdu;
|
|
if (mpdu_st) {
|
|
uint8_t tx_phase = (uint8_t)(mpdu_st->tx_phase & 0x3);
|
|
if (PLC_PHASE_ALL == tx_phase) {
|
|
iot_plc_led_request(mac_trans_tx_phase2led_req(PLC_PHASE_A));
|
|
iot_plc_led_request(mac_trans_tx_phase2led_req(PLC_PHASE_B));
|
|
iot_plc_led_request(mac_trans_tx_phase2led_req(PLC_PHASE_C));
|
|
} else {
|
|
iot_plc_led_request(mac_trans_tx_phase2led_req(tx_phase));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if ENA_WAR_421_DEBUG
|
|
uint32_t g_war_421_cnt = 0;
|
|
uint32_t g_is_check_phy_rx = 1;
|
|
#endif
|
|
|
|
uint32_t mac_tx_hw_mpdu_comp_dsr(uint32_t hwq_id)
|
|
{
|
|
return mac_tx_hw_mpdu_comp(hwq_id, 1);
|
|
}
|
|
|
|
uint32_t mac_tx_hw_mpdu_comp(uint32_t hwq_id, uint32_t is_phy_comp)
|
|
{
|
|
extern mac_pdev_t* g_mac_pdev[];
|
|
mac_pdev_t *pdev = g_mac_pdev[PLC_PDEV_ID];
|
|
mac_vdev_t *vdev;
|
|
tx_mpdu_start *next_ptr;
|
|
tx_pb_start *pb_to_free;
|
|
tx_pb_start *pb_start;
|
|
tx_mpdu_end *mpdu_ed;
|
|
tx_dummy_node *dummy_node;
|
|
tx_mpdu_start *current_mpdu = \
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id];
|
|
uint32_t need_trigger_swq = 0;
|
|
uint32_t ret = ERR_OK;
|
|
/* mac proto */
|
|
uint32_t proto = PHY_PROTO_TYPE_GET();
|
|
uint32_t delimiter_type;
|
|
void *fc;
|
|
mac_msdu_t *free_msdu;
|
|
#if PLC_MAC_TX_DEBUG_LOG >= PLC_MAC_LOG_LEVEL_1
|
|
/* log the enter of this functon */
|
|
iot_printf("hwq:%d tx_done", hwq_id);
|
|
iot_dbglog_input(PLC_MAC_TX_MID, DBGLOG_INFO_LVL_2, IOT_MAC_TX_DONE_ID, 1,
|
|
hwq_id);
|
|
if (current_mpdu && current_mpdu->desc_type == DESC_TYPE_TX_MPDU_START) {
|
|
iot_printf(",phase:%d", current_mpdu->tx_phase);
|
|
}
|
|
#endif
|
|
|
|
#if DEBUG_NID_ERR
|
|
if ((mac_get_cert_test_mode() != CERT_TEST_CMD_NOT_RX)
|
|
&& (g_phy_cpu_share_ctxt.nid_err_cnt != 0)) {
|
|
uint32_t dbg_dump_buf[2];
|
|
dbg_dump_buf[0] = g_phy_cpu_share_ctxt.cur_nid;
|
|
dbg_dump_buf[1] = g_phy_cpu_share_ctxt.config_nid;
|
|
IOT_ASSERT_DUMP(0, dbg_dump_buf, 2);
|
|
}
|
|
#endif
|
|
|
|
#if DEBUG_CANNOT_SENDOUT_PKT
|
|
if(pdev->plc_tx_timer && pdev->plc_tx_st && mac_txq_is_csma(hwq_id))
|
|
{
|
|
os_stop_timer(pdev->plc_tx_timer);
|
|
pdev->plc_tx_st = PLC_DEBUG_INIT_ST;
|
|
if (pdev->plc_con_tx_timeout_cnt > 0) {
|
|
pdev->plc_con_tx_timeout_cnt = 0;
|
|
phy_csma_ignore_cca(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while (current_mpdu) {
|
|
fc = mac_tx_mpdu_start_get_fc_ptr(current_mpdu);
|
|
/*current node is mpdu*/
|
|
if (current_mpdu->desc_type == DESC_TYPE_TX_MPDU_START) {
|
|
mpdu_ed = current_mpdu->tx_status;
|
|
IOT_ASSERT(mpdu_ed);
|
|
if (mpdu_ed->tx_done == 0) {
|
|
/* if not done then back next time */
|
|
break;
|
|
}
|
|
|
|
mac_fc_i1901_to_sg(PHY_PROTO_TYPE_GET(), fc);
|
|
vdev = find_vdev_by_nid(PLC_PDEV_ID,
|
|
mac_get_nid_from_fc(proto, fc));
|
|
IOT_ASSERT(vdev);
|
|
/* record the tx time */
|
|
vdev->last_tx_ntb = (uint32_t)mpdu_ed->first_try_ts;
|
|
#if ENA_WAR_421_DEBUG
|
|
if (vdev->is_up) {
|
|
if (vdev->last_rx_ntb == 0 && g_is_check_phy_rx) {
|
|
/* if not receiving any frame after boot up */
|
|
if (g_war_421_cnt++ > 100) {
|
|
/* every 100 packet, logging */
|
|
phy_dbg_sts_print();
|
|
g_war_421_cnt = 0;
|
|
}
|
|
} else {
|
|
g_is_check_phy_rx = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* LED control hook */
|
|
mac_tx_led_control(current_mpdu);
|
|
|
|
/* if the last desc process done by HW,
|
|
* put a dummy desc after it, hw would keep
|
|
* polling at the last node
|
|
*/
|
|
if (current_mpdu->next == NULL) {
|
|
ret = mac_desc_get(&g_mac_desc_eng, \
|
|
PLC_TX_DUMMY_NODE_POOL, \
|
|
(void**)&dummy_node);
|
|
IOT_ASSERT(ret == 0);
|
|
/* init the dummy node */
|
|
if(vdev->stop_flag == 1){
|
|
dummy_node->tx_done = 1;
|
|
}
|
|
dummy_node->desc_type = DESC_TYPE_TX_DUMMY;
|
|
|
|
/* en hwq */
|
|
mac_tx_hw_mpdu(&pdev->hwq_hdl, \
|
|
hwq_id, (tx_mpdu_start*)dummy_node);
|
|
#if PLC_MAC_TX_DEBUG_LOG > PLC_MAC_LOG_LEVEL_1
|
|
iot_printf("dummy end\n");
|
|
iot_dbglog_input(PLC_MAC_TX_HW_MID, \
|
|
DBGLOG_INFO_LVL_1, IOT_MAC_TX_HW_DUMMY_END_ID, 0);
|
|
|
|
#endif
|
|
/*
|
|
* dummy would generate TX INT, so no
|
|
* need to back again
|
|
* ret = ERR_AGAIN;
|
|
*/
|
|
if(vdev->stop_flag == 0){
|
|
ret = ERR_OK;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
/* check if we can free something */
|
|
next_ptr = (tx_mpdu_start*)current_mpdu->next;
|
|
if (next_ptr->desc_type == DESC_TYPE_TX_MPDU_START) {
|
|
if (!next_ptr->tx_status->tx_done) {
|
|
ret = ERR_OK;
|
|
break;
|
|
}
|
|
}
|
|
else if (next_ptr->desc_type == DESC_TYPE_TX_DUMMY) {
|
|
dummy_node = (tx_dummy_node*)next_ptr;
|
|
if (!dummy_node->tx_done) {
|
|
/*
|
|
* dummy would generate TX INT, so no
|
|
* need to back again
|
|
* ret = ERR_AGAIN;
|
|
*/
|
|
ret = ERR_OK;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
IOT_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
/* now we can free the mpdu */
|
|
|
|
#if PLC_MAC_TX_DEBUG_LOG >= PLC_MAC_LOG_LEVEL_1
|
|
/* log the every tx done mpdu */
|
|
delimiter_type = mac_get_delimi_from_fc(proto,
|
|
fc);
|
|
if ((FC_DELIM_SOF == delimiter_type) && current_mpdu->need_ack) {
|
|
/* unicast sof */
|
|
iot_printf("\n++tx ok=0x%x,tx done=0x%x"
|
|
",ntb=%lu,sack=%lu"
|
|
",fl=%d.", mpdu_ed->tx_ok, mpdu_ed->tx_done,
|
|
mpdu_ed->first_try_ts, mpdu_ed->sack_timestamp,
|
|
(*(frame_control_t*) fc).vf.sof.frame_len);
|
|
iot_dbglog_input(PLC_MAC_TX_MID, DBGLOG_INFO_LVL_2,
|
|
IOT_MAC_TXDONE_OK_ID, 2, mpdu_ed->tx_ok,
|
|
mpdu_ed->tx_done);
|
|
iot_dbglog_input(PLC_MAC_TX_MID, DBGLOG_INFO_LVL_2,
|
|
IOT_MAC_TXDONE_TS_ID, 2,
|
|
mpdu_ed->first_try_ts,
|
|
mpdu_ed->sack_timestamp);
|
|
#if ENA_WAR_244
|
|
if (g_phy_cpu_share_ctxt.sack_miss_cnt
|
|
|| g_phy_cpu_share_ctxt.sack_err_occur_cnt) {
|
|
iot_printf("sack:miss=0x%x-err=0x%x.",
|
|
g_phy_cpu_share_ctxt.sack_miss_cnt,
|
|
g_phy_cpu_share_ctxt.sack_err_occur_cnt);
|
|
iot_dbglog_input(PLC_MAC_TX_MID, DBGLOG_INFO_LVL_2,
|
|
IOT_MAC_TXDONE_SACK_ID, 2,
|
|
g_phy_cpu_share_ctxt.sack_miss_cnt,
|
|
g_phy_cpu_share_ctxt.sack_err_occur_cnt);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
/* other frame */
|
|
iot_printf("\n--tx done=0x%x"
|
|
",ntb=%lu,delim=%d.", mpdu_ed->tx_done,
|
|
mpdu_ed->first_try_ts, delimiter_type);
|
|
}
|
|
|
|
if (mpdu_ed->total_retry_cnt) {
|
|
iot_printf("HW_R_C:%d, st1:0x%x, st2:0x%x, st3:0x%x, st4:0x%x,"
|
|
"st5:0x%x, st6:0x%x, st7:0x%x.",
|
|
mpdu_ed->total_retry_cnt,
|
|
mpdu_ed->try_status_01.retry_time_offset,
|
|
mpdu_ed->try_status_02.retry_time_offset,
|
|
mpdu_ed->try_status_03.retry_time_offset,
|
|
mpdu_ed->try_status_04.retry_time_offset,
|
|
mpdu_ed->try_status_05.retry_time_offset,
|
|
mpdu_ed->try_status_06.retry_time_offset,
|
|
mpdu_ed->try_status_07.retry_time_offset);
|
|
}
|
|
|
|
/* check tx abort */
|
|
if (mpdu_ed->is_phyerr) {
|
|
iot_printf("tx_abort:reason=%d, "
|
|
"desc_sym:%d, fc: 0x%x, 0x%x, 0x%x, 0x%x, "
|
|
"udrun_cnt:%d, pb_num:%d", mpdu_ed->phyerr_id,
|
|
mac_tx_mpdu_start_get_symppb(current_mpdu),
|
|
pdev->fc_hw[0], pdev->fc_hw[1], pdev->fc_hw[2],
|
|
pdev->fc_hw[3], pdev->mac_underrun_cnt,
|
|
current_mpdu->pb_num);
|
|
iot_dbglog_input(PLC_MAC_TX_HW_MID, DBGLOG_WARN,
|
|
IOT_MAC_TX_ABORT_ID, 1,
|
|
mpdu_ed->phyerr_id);
|
|
}
|
|
|
|
iot_printf("\n");
|
|
|
|
#if DEG_TX_ABORT
|
|
phy_get_tx_abort_info();
|
|
#endif
|
|
|
|
#endif /* PLC_MAC_TX_DEBUG_LOG >= PLC_MAC_LOG_LEVEL_1 */
|
|
|
|
//tx mpdu cnt
|
|
mac_add_mpdu_tx_cnt();
|
|
|
|
delimiter_type = \
|
|
mac_get_delimi_from_fc(proto, fc);
|
|
|
|
if (mac_is_unicast(proto, fc, 0)) {
|
|
if(!(mpdu_ed->tx_ok)) {
|
|
/* not receive ack cnt */
|
|
mac_add_tx_no_ack_cnt(vdev->proxy_tei == \
|
|
mac_get_fc_dtei(phy_proto_type_get(), fc));
|
|
}
|
|
else {
|
|
/*receive ack , but pb all err*/
|
|
if (!current_mpdu->tx_status->sack_bitmap) {
|
|
mac_add_tx_unicast_all_pb_err_cnt(vdev->proxy_tei == \
|
|
mac_get_fc_dtei(phy_proto_type_get(), fc));
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (delimiter_type) {
|
|
case FC_DELIM_BEACON: {
|
|
/*set new cur_ptr*/
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] = \
|
|
(void*)(current_mpdu->next);
|
|
|
|
/* bcn's pb buf is not freed currently */
|
|
if (!current_mpdu->pb_buf_reuse) {
|
|
if (current_mpdu->is_msdu) {
|
|
/* bcn should not set msdu type */
|
|
IOT_ASSERT(0);
|
|
}
|
|
else {
|
|
/* free the pb buf in mpdu way,
|
|
* in this condition, all pb
|
|
* share the same offset
|
|
*/
|
|
tx_pb_start *pb = current_mpdu->pb_list;
|
|
uint32_t pb_cnt = 0;
|
|
while (pb) {
|
|
pb_cnt++;
|
|
iot_pkt_t *pkt = \
|
|
(iot_pkt_t*)(pb->pb_buf_addr \
|
|
- current_mpdu->sw_buf_offset);
|
|
iot_pkt_free(pkt);
|
|
pb = pb->next_pb;
|
|
}
|
|
IOT_ASSERT(current_mpdu->pb_num == pb_cnt);
|
|
}
|
|
}
|
|
|
|
if(!current_mpdu->tx_desc_reuse)
|
|
{
|
|
/*free desc*/
|
|
ret = mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_PB_START_POOL, \
|
|
current_mpdu->pb_list);
|
|
IOT_ASSERT(ERR_OK == ret);
|
|
current_mpdu->pb_list = NULL;
|
|
ret = mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_MPDU_END_POOL, \
|
|
current_mpdu->tx_status);
|
|
IOT_ASSERT(ERR_OK == ret);
|
|
current_mpdu->tx_status = NULL;
|
|
ret = mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_MPDU_START_POOL, \
|
|
current_mpdu);
|
|
IOT_ASSERT(ERR_OK == ret);
|
|
}
|
|
|
|
/* hwq depth subtract 1 */
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
|
|
break;
|
|
}
|
|
case FC_DELIM_SOF: {
|
|
#if MAC_TIMESTAMPING
|
|
mac_pdev_t *pdev_t = g_mac_pdev[PLC_PDEV_ID];
|
|
uint32_t tx_timestamp = current_mpdu->tx_status->first_try_local_ts;
|
|
uint32_t fl = current_mpdu->fc.sg_fc.vf.sof.frame_len;
|
|
iot_printf("loopback time = %dus, dsr_to_isr_time = %dus, "
|
|
"phy_pld_interrupt = %d, rx_timestamp = %d, rx_dsr_time = %d, "
|
|
"tx_hung_time = %d, tx_timestamp = %d, frame_length = %d\n",
|
|
(pdev_t->mac_tx_sw_ntb - pdev_t->mac_rx_ntb)/25,
|
|
(pdev_t->mac_rx_ntb - pdev_t->mac_isr_ntb)/25,
|
|
g_phy_cpu_share_ctxt.phy_pld_int_ntb, pdev_t->mac_rx_timestamp,
|
|
pdev_t->mac_rx_ntb, pdev_t->mac_tx_sw_ntb,tx_timestamp, fl
|
|
);
|
|
#endif
|
|
free_msdu = NULL;
|
|
/*msdu comp*/
|
|
if (current_mpdu->is_msdu) {
|
|
/* if pb buf reuse, then msdu need be freed */
|
|
free_msdu = mac_tx_hw_msdu_comp(hwq_id, current_mpdu,
|
|
pdev, is_phy_comp);
|
|
need_trigger_swq = 1;
|
|
}
|
|
else if (!current_mpdu->pb_buf_reuse) {
|
|
//TODO:need ftm to the same as mm
|
|
if (g_fw_mode != FTM_MODE) {
|
|
uint8_t cnt = 0;
|
|
uint32_t pb_num = mac_get_sof_pbnum(proto, fc);
|
|
/* if pb buf is not continuous,
|
|
* use the following method
|
|
*/
|
|
tx_pb_start *pb = NULL;
|
|
for (pb = current_mpdu->pb_list; pb != NULL;
|
|
pb = pb->next_pb) {
|
|
iot_pkt_t *pkt = (iot_pkt_t*) (pb->pb_buf_addr
|
|
- current_mpdu->sw_buf_offset);
|
|
iot_pkt_free(pkt);
|
|
cnt++;
|
|
}
|
|
if (cnt != pb_num) {
|
|
iot_printf("%d!=%d\n", cnt, pb_num);
|
|
IOT_ASSERT(0);
|
|
}
|
|
} else {
|
|
/* free the pb buf in one pkt buf,
|
|
* only support ftm mode,
|
|
* pb num 1-4
|
|
*/
|
|
tx_pb_start *pb = current_mpdu->pb_list;
|
|
IOT_ASSERT(pb);
|
|
iot_pkt_t *pkt = (iot_pkt_t*) (pb->pb_buf_addr
|
|
- current_mpdu->sw_buf_offset);
|
|
iot_pkt_free(pkt);
|
|
}
|
|
}
|
|
|
|
/*set new cur_ptr*/
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] = \
|
|
(void*)(current_mpdu->next);
|
|
|
|
if(!current_mpdu->tx_desc_reuse)
|
|
{
|
|
/*free pb list*/
|
|
if (current_mpdu->pb_list != NULL) {
|
|
pb_start = current_mpdu->pb_list;
|
|
pb_to_free = pb_start->next_pb;
|
|
while (pb_to_free != NULL) {
|
|
pb_start->next_pb = pb_to_free->next_pb;
|
|
mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_PB_START_POOL, pb_to_free);
|
|
pb_to_free = pb_start->next_pb;
|
|
}
|
|
mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_PB_START_POOL, pb_start);
|
|
current_mpdu->pb_list = NULL;
|
|
}
|
|
/*free mpdu_end*/
|
|
mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_MPDU_END_POOL, mpdu_ed);
|
|
current_mpdu->tx_status = NULL;
|
|
|
|
/*free tx_mpdu_start*/
|
|
if ( \
|
|
/* if msdu and not first mpdu, free it */
|
|
(current_mpdu->is_msdu && !current_mpdu->list_start) \
|
|
/* or if not msdu, free the mpdu */
|
|
|| !current_mpdu->is_msdu) {
|
|
mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_MPDU_START_POOL, current_mpdu);
|
|
}
|
|
}
|
|
|
|
if (free_msdu) {
|
|
mac_msdu_deinit(free_msdu);
|
|
}
|
|
/* no matter msdu or mpdu, at least a mpdu is processed */
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
|
|
/* trigger another sw scheuler */
|
|
if (need_trigger_swq) {
|
|
if(g_fw_mode!=FTM_MODE)
|
|
{
|
|
/* FTM not enable SWQ currently */
|
|
mac_swsch_trigger_tx(0, vdev->vdev_id);
|
|
mac_add_tx_msdu_comp_trigger_cnt();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
/*sw need send ack when cert mode */
|
|
case FC_DELIM_SACK:{
|
|
if (mac_get_cert_test_mode()) {
|
|
/*set new cur_ptr*/
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] = \
|
|
(void*)(current_mpdu->next);
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
} else {
|
|
/*set new cur_ptr*/
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] = \
|
|
(void*)(current_mpdu->next);
|
|
/* cert mpdu and end not free */
|
|
if(!current_mpdu->tx_desc_reuse) {
|
|
/*free desc*/
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_MPDU_END_POOL, \
|
|
(tx_mpdu_end*)current_mpdu->tx_status);
|
|
current_mpdu->tx_status = NULL;
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_MPDU_START_POOL, \
|
|
current_mpdu);
|
|
}
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
}
|
|
break;
|
|
}
|
|
case FC_DELIM_NNCCO:{
|
|
#if (PLC_MAC_RX_DEBUG_LOG >= PLC_MAC_LOG_LEVEL_1)
|
|
void *fc = mac_tx_mpdu_start_get_fc_ptr(current_mpdu);
|
|
#if ENA_WAR_NNCCO_FEAT == 1
|
|
fc = g_phy_cpu_share_ctxt.tx_fc;
|
|
#else
|
|
fc = g_phy_ctxt.indep.nn_cco_fc;
|
|
#endif
|
|
iot_printf("NNCCO tx - st:%d,dur:%d,nid:0x%x"
|
|
",tx_ts:%lu\n",
|
|
mac_get_nncco_protect_region_offset(proto, fc),
|
|
mac_get_nncco_protect_dur(proto, fc),
|
|
mac_get_nncco_received_nid(proto, fc),
|
|
mpdu_ed->first_try_ts);
|
|
#endif
|
|
|
|
/*set new cur_ptr*/
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] = \
|
|
(void*)(current_mpdu->next);
|
|
/* cert mpdu and end not free */
|
|
if(!current_mpdu->tx_desc_reuse) {
|
|
/*free desc*/
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_MPDU_END_POOL, \
|
|
current_mpdu->tx_status);
|
|
current_mpdu->tx_status = NULL;
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_MPDU_START_POOL, \
|
|
current_mpdu);
|
|
}
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
|
|
#if (PLC_MAC_RX_DEBUG_LOG >= PLC_MAC_LOG_LEVEL_2)
|
|
iot_printf("[mac_check_dbg], phase1:%lu, phase2:%lu, phase3:%lu\n",\
|
|
pdev->hwq_hdl.q_depth[6], \
|
|
pdev->hwq_hdl.q_depth[10], \
|
|
pdev->hwq_hdl.q_depth[14]);
|
|
#endif
|
|
break;
|
|
}
|
|
case I1901_RTS_CTS: {
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] =
|
|
(void*)(current_mpdu->next);
|
|
if(!current_mpdu->tx_desc_reuse) {
|
|
/*free desc*/
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_MPDU_END_POOL,
|
|
current_mpdu->tx_status);
|
|
current_mpdu->tx_status = NULL;
|
|
mac_desc_free(&g_mac_desc_eng, PLC_TX_MPDU_START_POOL,
|
|
current_mpdu);
|
|
}
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
break;
|
|
}
|
|
default: {
|
|
IOT_ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (current_mpdu->desc_type == DESC_TYPE_TX_DUMMY) {
|
|
/* tx dummy desc */
|
|
/*current node is dummy*/
|
|
dummy_node = (tx_dummy_node*)current_mpdu;
|
|
if (dummy_node->tx_done == 0) {
|
|
if (mac_txq_is_dbg_mode()) {
|
|
/* dummy not done, should not happen in dbg mode */
|
|
IOT_ASSERT(0);
|
|
}
|
|
ret = ERR_OK;
|
|
break;
|
|
}
|
|
if (dummy_node->next == 0) {
|
|
/* the dummy is the last frame, do nothing */
|
|
ret = ERR_OK;
|
|
break;
|
|
}
|
|
else {
|
|
/* if dummy is not the last frame */
|
|
next_ptr = (tx_mpdu_start*)dummy_node->next;
|
|
/* should no two continuous dummy, so the next
|
|
* frame must be mpdu
|
|
*/
|
|
IOT_ASSERT(next_ptr->desc_type \
|
|
== DESC_TYPE_TX_MPDU_START);
|
|
if (!next_ptr->tx_status->tx_done) {
|
|
/* if the next frame of dummy is not done
|
|
* just back later
|
|
*/
|
|
ret = ERR_OK;
|
|
break;
|
|
}
|
|
else {
|
|
/* if the mpdu after dummy is done,
|
|
* free the dummy node
|
|
*/
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id] = \
|
|
(void*)dummy_node->next;
|
|
mac_desc_free(&g_mac_desc_eng, \
|
|
PLC_TX_DUMMY_NODE_POOL, dummy_node);
|
|
pdev->hwq_hdl.q_depth[hwq_id]--;
|
|
#if PLC_MAC_TX_DEBUG_LOG > PLC_MAC_LOG_LEVEL_1
|
|
iot_printf("dummy freed\n");
|
|
iot_dbglog_input(PLC_MAC_TX_HW_MID, \
|
|
DBGLOG_INFO_LVL_1, IOT_MAC_TX_HW_DUMMY_FREE_ID, 0);
|
|
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* other desc type */
|
|
IOT_ASSERT(0);
|
|
}
|
|
/* next mpdu */
|
|
current_mpdu = \
|
|
pdev->hwq_hdl.cur_hdl_desc[hwq_id];
|
|
} //while
|
|
|
|
#if ENA_WAR_440
|
|
mac_set_tx_underflow_cnt(g_phy_cpu_share_ctxt.tx_underflow_cnt);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
void mac_handle_timeout_tx_abort()
|
|
{
|
|
#if WAR_TIMEOUT_TX_ABORT
|
|
g_phy_cpu_share_ctxt.timeout_tx_abort_cnt++;
|
|
iot_printf("phy tx abort can't clear! phy_tx_abort_cnt:%d\n",\
|
|
g_phy_cpu_share_ctxt.timeout_tx_abort_cnt);
|
|
g_phy_cpu_share_ctxt.timeout_tx_abort_flag = false;
|
|
#endif
|
|
}
|
|
|
|
void IRAM_ATTR mac_check_timeout_tx_abort()
|
|
{
|
|
#if WAR_TIMEOUT_TX_ABORT
|
|
uint32_t value = 0;
|
|
if(g_phy_cpu_share_ctxt.timeout_tx_abort_flag)
|
|
{
|
|
value |= 1 << MAC_DSR_CHECK_TX_ABORT_ID;
|
|
|
|
if (value) {
|
|
/* deliver the DSR events to mac task context */
|
|
os_set_task_event_with_v_from_isr(p_mac_glb->task_h, value);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint32_t mac_cal_fd_and_preamble_time(uint32_t bandid, uint32_t tmi)
|
|
{
|
|
uint32_t preamble_time = 0;
|
|
|
|
uint32_t fd_time = DEFAULT_PHY_TX_FD_TIME;
|
|
|
|
if (tmi > SG_TMI_MAX) {
|
|
IOT_ASSERT(0);
|
|
}
|
|
|
|
if (tmi == SG_TMI_MAX) {
|
|
preamble_time = (uint32_t)(phy_sg_emcs_pream_num_get(bandid) * 40.96);
|
|
} else {
|
|
preamble_time = (uint32_t)(phy_sg_bmcs_pream_num_get(bandid) * 40.96);
|
|
}
|
|
|
|
return fd_time + preamble_time;
|
|
}
|
|
|
|
void mac_tx_fill_extsackinfo(uint32_t proto, void *fc, uint32_t delimiter, \
|
|
uint32_t nid, uint32_t ext_type, uint8_t *addr, uint16_t stei, uint8_t sn)
|
|
{
|
|
(void)sn;
|
|
switch (proto) {
|
|
#if SUPPORT_SMART_GRID
|
|
case PLC_PROTO_TYPE_SG:
|
|
{
|
|
/* nid share the same place for SG */
|
|
frame_control_t *sg_fc = (frame_control_t *)fc;
|
|
if (delimiter != FC_DELIM_SACK) {
|
|
IOT_ASSERT(0);
|
|
}
|
|
sg_fc->delimiter_type = delimiter;
|
|
if (ext_type == SACK_EXT_TYPE_SEARCH) {
|
|
IOT_ASSERT(addr != NULL);
|
|
sg_fc->nid = 0;
|
|
os_mem_cpy(&sg_fc->vf.search.mac_addr, addr, IOT_MAC_ADDR_LEN);
|
|
sg_fc->vf.search.stei = PLC_TEI_CTRL;
|
|
sg_fc->vf.search.ext_type = ext_type;
|
|
}
|
|
if (ext_type == SACK_EXT_TYPE_SYNC) {
|
|
sg_fc->nid = nid;
|
|
sg_fc->vf.sync.stei = stei;
|
|
sg_fc->vf.sync.ext_type = ext_type;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
#if SUPPORT_SOUTHERN_POWER_GRID
|
|
case PLC_PROTO_TYPE_SPG:
|
|
{
|
|
spg_frame_control_t *spg_fc = (spg_frame_control_t *)fc;
|
|
if (delimiter != FC_DELIM_SACK) {
|
|
IOT_ASSERT(0);
|
|
}
|
|
spg_fc->delimiter_type = (uint8_t)delimiter;
|
|
if (ext_type == SACK_EXT_TYPE_SEARCH) {
|
|
IOT_ASSERT(addr != NULL);
|
|
spg_fc->snid = 0;
|
|
os_mem_cpy(&spg_fc->vf.search.mac_addr, addr, IOT_MAC_ADDR_LEN);
|
|
spg_fc->vf.search.stei = PLC_TEI_CTRL;
|
|
spg_fc->vf.search.ext_type = ext_type;
|
|
spg_fc->vf.search.sn = sn;
|
|
spg_fc->vf.search.version = SPG_STANDARD_VERSION;
|
|
}
|
|
if (ext_type == SACK_EXT_TYPE_SYNC) {
|
|
spg_fc->snid = (uint8_t)nid;
|
|
spg_fc->vf.sync.stei = stei;
|
|
spg_fc->vf.sync.ext_type = ext_type;
|
|
spg_fc->vf.sync.sn = sn;
|
|
spg_fc->vf.sync.version = SPG_STANDARD_VERSION;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|