Files
kunlun/plc/halmac/sched/mac_sched.c
2024-09-28 14:24:04 +08:00

2796 lines
96 KiB
C
Executable File

/****************************************************************************
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.
****************************************************************************/
/* os shim includes */
#include "os_types.h"
#include "os_mem.h"
#include "os_timer.h"
/* common includes */
#include "iot_module.h"
#include "iot_errno.h"
#include "iot_utils.h"
#include "iot_io.h"
#include "iot_config.h"
#include "iot_dbglog_api.h"
#include "iot_dbglog_parser.h"
/* mac module internal includes */
#include "mac_vdev.h"
#include "mac_sched.h"
#include "mac_sched_hw.h"
#include "command_list.h"
#include "mac_desc_engine.h"
#include "mac_hwq_mgr.h"
#include "mac_pdev.h"
#include "mac_tx_hw.h"
#include "nn_cco.h"
#include "mac.h"
#include "mac_cert_test.h"
/* rf support */
#include "mac_rf_sched.h"
/* public api includes */
#include "plc_fr.h"
#include "mac_bcm_api.h"
#include "mac_dsr.h"
#include "mac_isr.h"
#include "phy_bb.h"
#include "mpdu_header.h"
#include "mac_check_spur_cco.h"
#if HW_PLATFORM != HW_PLATFORM_SIMU
/* csma region pkt tx may be overlapped with next region due to HW issue.
* reserve some gap in the end of the csma region of each beacon period.
* unit is 1ms
*/
#define MAC_SCHED_CSMA_GAP_MS 40
#else /* HW_PLATFORM != HW_PLATFORM_SIMU */
#define MAC_SCHED_CSMA_GAP_MS 0
#endif /* HW_PLATFORM != HW_PLATFORM_SIMU */
/* define the rx only slot duration for CCO role device, the unit is 1ms. */
#define MAC_SCHED_CCO_RX_ONLY_SLOT_DUR 100
/* if there is big hole between current ntb and start ntb of network beacon
* period. try to insert csma region into the hole to improve bandwidth
* utilization.
* if the whole equal or larged than this value, local device should insert
* the csma region. the unit is 1 ntb.
*/
#define MAC_SCHED_INSERT_CSMA_TH MAC_MS_TO_NTB(600)
/* define neighbor network bandwidth change detection threshold.
* if neighbor network start ntb moved equal or larged than this value, local
* device should treat neighbor network bandwidth as changed. the unit is 1 ntb.
*/
#define MAC_SCHED_NN_BW_CHG_TH MAC_MS_TO_NTB(2)
/* define neighbor network bandwidth safe guard threshold.
* if neighbor network start ntb far away from the end of current protected,
* region, local device won't move the start ntb of the beacon period after
* current protected region as there is still enough time to negotiate the
* bandwidth for next beacon period. uint is 1 ntb.
*/
#define MAC_SCHED_NN_BW_GUARD MAC_MS_TO_NTB(1000)
/* define neighbor network info time out value for neighbor networks which
* can't see local network. set this value to 10 seconds to pass cert test
* case 3.2.9.2.
* the unit is 1 ntb.
*/
#define MAC_SCHED_NN_NO_WATCH_TO_DUR MAC_MS_TO_NTB(10 * 1000)
/* define max allowed protected region
* if neighbor network required protected region larger than this value,
* local device will consider the request as invalid. unit is 1ms.
*/
#define MAC_SCHED_NN_MAX_REGION (5000)
/* define the neighbor network negotiation frame tx timer interval.
* unit is 1ms.
*/
#define MAC_SCHED_NN_TX_INTERVAL 500
/* define the supported minimum duration for command list.
* the unit is 1ms.
*/
#define MAC_SCHED_CMD_LIST_DUR_MIN 500
/* define the maximum count of csma slots supported */
#define MAC_SCHED_CSMA_SPLIT_MAX 500
/* define the max cnt of not watched this network */
#define MAC_SCHED_NN_NOT_WATCH_MAX 16
/* length of the head of hw_sched_cmd_list_t */
#define MAC_SCHED_CMD_LIST_HEAD_LEN (sizeof(hw_sched_cmd_list_t) - \
sizeof(hw_sched_cmd_t) * HW_SCHED_CMD_MAX_CNT)
/* neighbor network info */
typedef struct _mac_nn_info {
/* network id of the neighbor network */
uint32_t nid :24,
/* flag to mark if the neighbor network are watching us */
watched :1,
/* number of not watch */
not_watch_cnt :7;
/* latest nncco frame rx ntb from the neighbor network */
uint64_t rx_ntb;
/* start ntb of next protected region */
uint64_t start_ntb;
/* end ntb of next protecte region */
uint64_t end_ntb;
/* requested protected region length, unit is 1ms */
uint16_t region_dur;
/* neighbor network self rf channel id */
uint8_t self_ch_id;
/* neighbor network self rf option id */
uint8_t self_option;
} mac_nn_info_t;
/* scheulder context */
typedef struct _mac_sched_ctx {
/* current command list queue in use */
hw_sched_cmd_list_t *curr_hw_cmd_list;
/* command list queue */
hw_sched_cmd_list_t *hw_cmd_list[HW_SCHED_QUEUE_DEPTH];
/* command list queue count */
uint8_t hw_cmd_list_cnt;
/* command list queue pos of the next free slot */
uint8_t hw_cmd_list_pos;
#if PLC_SUPPORT_CCO_ROLE
/* flag to mark if neighbor network negotiation frame tx allowed */
uint8_t allow_tx :1,
/* flag to mark if next neighbor network negotiation frame tx need to
* carry all neighbor network NID watched by local device.
*/
bc_all :1,
/* flag to mark if NID conflict detected */
nid_conflict:1,
/* flag to mark if neighbour network negotiation is enabled,
* default is enabled
*/
nw_nego_en: 1,
/* reserved for future */
rsvd :4;
/* next watched neighbor network nid broadcast index, local device should
* try to broadcast all watched neighbor network nid in each beacon period.
*/
uint8_t nid_bc_idx;
/* number of available nid in the prev_nid_list */
uint8_t prev_nid_list_cnt;
/* number of available nid in the curr_nid_list */
uint8_t curr_nid_list_cnt;
/* protected region length. unit is 1ms */
uint16_t region_dur;
/* start ntb 64bit version neighbor network negotiation frame tx allowed */
uint64_t allow_start_ntb;
/* end ntb 64bit version neighbor network negotiation frame tx allowed */
uint64_t allow_end_ntb;
/* heard network id list in previous beacon periods. */
mac_nn_info_t prev_nid_list[PLC_MAX_NEIGHBOR_NETWORK];
/* heard network id list in current beacon period. note that network heard
* in previous beacon periods and its protected region not expired yet will
* also be included.
*/
mac_nn_info_t curr_nid_list[PLC_MAX_NEIGHBOR_NETWORK];
/* neighbor network negotiation from tx timer */
timer_id_t tx_timer;
#endif
} mac_sched_ctx_t;
/* pack for the structures in the whole file */
#pragma pack(push) /* save the pack status */
#pragma pack(1) /* 1 byte align */
/* csma slot info */
typedef struct _mac_csma_slot {
/* slot phase info */
uint8_t phase : 2,
/* flag to mark if this slot is the last slot */
last : 1,
/* reserved for future */
rsvd : 5;
} mac_csma_slot_t;
#pragma pack(pop) /* restore the pack status */
/* temp buffer to save splitted csma slot info */
static mac_csma_slot_t g_csma_slot[MAC_SCHED_CSMA_SPLIT_MAX];
/* slot count of each phase. the phase seuqence will be affected by
* g_slot_phase.
*/
static uint16_t g_slot_cnt[PLC_PHASE_CNT];
/* phase array to identify the phase sequence */
static uint16_t g_slot_phase[PLC_PHASE_CNT];
/* last slot duration of each phase as the last slot may not be a multiple
* of fragment length. the phase seuqence is always A, B, C and won't be
* affected by g_slot_phase.
*/
static uint16_t g_slot_last[PLC_PHASE_CNT];
#if MAC_SCHED_DEBUG
static void mac_sched_dump(hw_sched_cmd_list_t *cl, uint16_t cnt)
{
uint16_t i;
iot_printf("%s total %lu, recusive %lu stntb:%lu\n",
__FUNCTION__, cnt, cl->recursive, cl->start_ntb);
for (i = 0; i < cnt; i++) {
if (cl->cmd[i].t_info.e.r_flag) {
if (cl->cmd[i].t_info.e.s_flag) {
iot_printf("rs phase - %lu, start offset %lu, "
"end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.se.start_t,
cl->cmd[i].t_info.se.end_t, cl->cmd[i].tx_q_en_bm);
} else {
if (cl->cmd[i].t_info.r.rf_flag) {
iot_printf("rf phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.r.end_t,
cl->cmd[i].tx_q_en_bm);
}
if (cl->cmd[i].t_info.r.re_flag) {
iot_printf("rl phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.r.end_t,
cl->cmd[i].tx_q_en_bm);
}
if (!cl->cmd[i].t_info.r.rf_flag &&
!cl->cmd[i].t_info.r.re_flag) {
iot_printf("rm phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.r.end_t,
cl->cmd[i].tx_q_en_bm);
}
}
} else if (cl->cmd[i].t_info.e.s_flag) {
iot_printf("st phase - %lu, start offset %lu, end offset - %lu, "
"q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.se.start_t,
cl->cmd[i].t_info.se.end_t, cl->cmd[i].tx_q_en_bm);
} else {
iot_printf("e phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.e.end_t,
cl->cmd[i].tx_q_en_bm);
}
}
iot_printf("------------------------------------------------------\n");
}
#else /* MAC_SCHED_DEBUG */
#define mac_sched_dump(cl, cnt)
#endif /* MAC_SCHED_DEBUG */
#if MAC_SCHED_NN_DEBUG
void mac_sched_nn_dump(mac_vdev_t *vdev)
{
uint8_t i;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
/* dump previous neighbor network info */
iot_printf("%s prev ----------------------------\n", __FUNCTION__);
for (i = 0; i < ctx->prev_nid_list_cnt; i++) {
iot_printf("%s nid %lu, start_ntb %lu-%lu, end_ntb %lu-%lu\n",
__FUNCTION__, ctx->prev_nid_list[i].nid,
iot_uint64_higher32(ctx->prev_nid_list[i].start_ntb),
iot_uint64_lower32(ctx->prev_nid_list[i].start_ntb),
iot_uint64_higher32(ctx->prev_nid_list[i].end_ntb),
iot_uint64_lower32(ctx->prev_nid_list[i].end_ntb));
}
iot_printf("%s current -------------------------\n", __FUNCTION__);
for (i = 0; i < ctx->curr_nid_list_cnt; i++) {
iot_printf("%s nid %lu, start_ntb %lu-%lu, end_ntb %lu-%lu\n",
__FUNCTION__, ctx->curr_nid_list[i].nid,
iot_uint64_higher32(ctx->curr_nid_list[i].start_ntb),
iot_uint64_lower32(ctx->curr_nid_list[i].start_ntb),
iot_uint64_higher32(ctx->curr_nid_list[i].end_ntb),
iot_uint64_lower32(ctx->curr_nid_list[i].end_ntb));
}
}
#else /* MAC_SCHED_NN_DEBUG */
#define mac_sched_nn_dump(vdev)
#endif /* MAC_SCHED_NN_DEBUG */
/* get wide band beacon queue bitmap according to appointed phase */
static uint32_t mac_sched_get_wb_bc_q(mac_vdev_t *vdev, uint8_t phase)
{
uint32_t ret = 0;
mac_queue_ctxt_t *queue = &g_mac_pdev[vdev->ref_pdev_id]->hwq_hdl;
switch (phase) {
case PLC_PHASE_A:
case PLC_PHASE_ALL: /* force A if all for HW limitation of TDMA Q */
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_BCN_A);
break;
case PLC_PHASE_B:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_BCN_B);
break;
case PLC_PHASE_C:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_BCN_C);
break;
default:
IOT_ASSERT(0);
break;
}
return ret;
}
/* get csma queue bitmap according to appointed phase */
static uint32_t mac_sched_get_csma_q(mac_vdev_t *vdev, uint8_t phase)
{
uint32_t ret = 0;
mac_queue_ctxt_t *queue = &g_mac_pdev[vdev->ref_pdev_id]->hwq_hdl;
switch (phase) {
case PLC_PHASE_A:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_A_0);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_A_1);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_A_2);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_A_3);
break;
case PLC_PHASE_B:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_B_0);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_B_1);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_B_2);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_B_3);
break;
case PLC_PHASE_C:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_C_0);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_C_1);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_C_2);
ret |= 1 << mac_q_get_hwqid(queue, MAC_QUE_CSMA_C_3);
break;
default:
IOT_ASSERT(0);
break;
}
return ret;
}
/* get tdma queue bitmap according to appointed phase */
static uint32_t mac_sched_get_tdma_q(mac_vdev_t *vdev, uint8_t phase)
{
uint32_t ret = 0;
mac_queue_ctxt_t *queue = &g_mac_pdev[vdev->ref_pdev_id]->hwq_hdl;
switch (phase) {
case PLC_PHASE_A:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_TDMA_A);
break;
case PLC_PHASE_B:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_TDMA_B);
break;
case PLC_PHASE_C:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_TDMA_C);
break;
default:
IOT_ASSERT(0);
break;
}
return ret;
}
/* get dedicated csma queue bitmap according to appointed phase */
static uint32_t mac_sched_get_dcsma_q(mac_vdev_t *vdev, uint8_t phase)
{
uint32_t ret = 0;
mac_queue_ctxt_t *queue = &g_mac_pdev[vdev->ref_pdev_id]->hwq_hdl;
switch (phase) {
case PLC_PHASE_A:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_BCSMA_A);
break;
case PLC_PHASE_B:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_BCSMA_B);
break;
case PLC_PHASE_C:
ret = 1 << mac_q_get_hwqid(queue, MAC_QUE_BCSMA_C);
break;
default:
IOT_ASSERT(0);
break;
}
return ret;
}
static hw_sched_cmd_list_t *mac_sched_alloc_cmd_list(mac_vdev_t *vdev)
{
uint32_t cl_reset_len;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_list_t *cl = NULL;
IOT_ASSERT(ctx->hw_cmd_list_cnt <= HW_SCHED_QUEUE_DEPTH);
if (ctx->hw_cmd_list_cnt < HW_SCHED_QUEUE_DEPTH) {
/* free command list vailable, allocate new one */
if (ctx->hw_cmd_list[ctx->hw_cmd_list_pos] == NULL) {
mac_desc_get(&g_mac_desc_eng, PLC_SCHED_CMD_LIST_POOL, (void **)&cl);
ctx->hw_cmd_list[ctx->hw_cmd_list_pos] = cl;
} else {
cl = ctx->hw_cmd_list[ctx->hw_cmd_list_pos];
}
ctx->hw_cmd_list_pos++;
if (ctx->hw_cmd_list_pos == HW_SCHED_QUEUE_DEPTH)
ctx->hw_cmd_list_pos = 0;
ctx->hw_cmd_list_cnt++;
} else {
/* check if any command list returned from HW */
if (mac_sched_get_cmd_list_cnt(vdev) >= HW_SCHED_QUEUE_DEPTH) {
//TODO: dbg for hw sched
os_mem_cpy(ctx->hw_cmd_list[0]->cmd, ctx->hw_cmd_list[1], 20);
IOT_ASSERT_DUMP(0, (uint32_t *)ctx->hw_cmd_list[0], 10);
}
/* re-use the previos command list returned from HW */
cl = ctx->hw_cmd_list[ctx->hw_cmd_list_pos];
ctx->hw_cmd_list_pos++;
if (ctx->hw_cmd_list_pos == HW_SCHED_QUEUE_DEPTH)
ctx->hw_cmd_list_pos = 0;
}
IOT_ASSERT(cl);
cl_reset_len = MAC_SCHED_CMD_LIST_HEAD_LEN;
cl_reset_len += min(cl->total_cnt + 1, HW_SCHED_CMD_MAX_CNT) *
sizeof(hw_sched_cmd_t);
os_mem_set(cl, 0, cl_reset_len);
#if MAC_SCHED_DEBUG
iot_printf("%s allocate %p\n", __FUNCTION__, cl->cmd);
#endif
return cl;
}
static void mac_sched_free_cmd_list(mac_vdev_t *vdev)
{
mac_sched_ctx_t *ctx = vdev->sched_ctx;
ctx->hw_cmd_list_cnt = 0;
ctx->hw_cmd_list_pos = 0;
}
/* split csma slot according to SG spec. the unit of frag is 1ms. */
static uint16_t mac_sched_split_csma(mac_bc_cmsa_si_t *csma, uint32_t frag)
{
uint16_t g_slot_base[PLC_PHASE_CNT];
uint16_t i, j, k, idx, b_switch, total_cnt = 0;
uint8_t phase_cnt = 0;
IOT_ASSERT(csma->phase_cnt <= PLC_PHASE_CNT);
IOT_ASSERT(frag);
os_mem_set(g_csma_slot, 0, sizeof(g_csma_slot));
os_mem_set(g_slot_cnt, 0, sizeof(g_slot_cnt));
os_mem_set(g_slot_phase, 0, sizeof(g_slot_phase));
os_mem_set(g_slot_base, 0, sizeof(g_slot_base));
os_mem_set(g_slot_last, 0, sizeof(g_slot_last));
/* calculate slot cnt of each phase */
for (i = 0; i < csma->phase_cnt; i++) {
/* exclude 0 duration phase */
if (csma->slot_dur[i]) {
g_slot_cnt[phase_cnt] = (uint16_t)(csma->slot_dur[i] / frag);
g_slot_last[csma->phase[i] - 1] =
(uint16_t)(csma->slot_dur[i] % frag);
g_slot_phase[phase_cnt] = csma->phase[i];
if (g_slot_cnt[phase_cnt] == 0) {
g_slot_cnt[phase_cnt] = 1;
} else {
g_slot_last[csma->phase[i] - 1] += (uint16_t)frag;
}
total_cnt += g_slot_cnt[phase_cnt];
phase_cnt++;
}
}
if (total_cnt == 0 || phase_cnt == 0)
return 0;
IOT_ASSERT(total_cnt <= MAC_SCHED_CSMA_SPLIT_MAX);
/* sort from less to more */
for (i = 0; i < phase_cnt; i++) {
for (j = i + 1; j < phase_cnt; j++) {
b_switch = 0;
if (g_slot_cnt[i] > g_slot_cnt[j]) {
b_switch = 1;
} else if (g_slot_cnt[i] == g_slot_cnt[j]) {
/* for same slot count phase, we should follow A, B, C
* sequence according to smart grid spec.
*/
if (g_slot_phase[j] < g_slot_phase[i]) {
b_switch = 1;
}
}
if (b_switch) {
k = g_slot_cnt[i];
g_slot_cnt[i] = g_slot_cnt[j];
g_slot_cnt[j] = k;
k = g_slot_phase[i];
g_slot_phase[i] = g_slot_phase[j];
g_slot_phase[j] = k;
}
}
}
/* arrange slot for each phase */
for (i = 0; i < phase_cnt; i++) {
/* the last phase always try to use any left slot */
if (i == (phase_cnt - 1))
g_slot_base[i] = 1;
else
g_slot_base[i] = total_cnt / g_slot_cnt[i];
for (j = 0; j < g_slot_cnt[i]; j++) {
idx = i + j * g_slot_base[i];
if (g_csma_slot[idx].phase == 0) {
/* free slot found */
g_csma_slot[idx].phase = (uint8_t)g_slot_phase[i];
if (j == (g_slot_cnt[i] - 1)) {
g_csma_slot[idx].last = 1;
}
} else {
/* try to find the next free slot */
for (k = idx; k < total_cnt; k++) {
if (g_csma_slot[k].phase == 0) {
g_csma_slot[k].phase = (uint8_t)g_slot_phase[i];
if (j == (g_slot_cnt[i] - 1)) {
g_csma_slot[k].last = 1;
}
break;
}
}
IOT_ASSERT(k < total_cnt);
}
}
}
#if MAC_SCHED_DEBUG
iot_printf("%s total %lu, frag %lu, phase %lu %lu, phase %lu %lu, "
"phase %lu %lu-------\n",
__FUNCTION__, total_cnt, frag, csma->phase[0], csma->slot_dur[0],
csma->phase[1], csma->slot_dur[1], csma->phase[2], csma->slot_dur[2]);
for (i = 0; i < total_cnt; i++) {
switch (g_csma_slot[i].phase) {
case PLC_PHASE_A:
iot_printf("A");
break;
case PLC_PHASE_B:
iot_printf("B");
break;
case PLC_PHASE_C:
iot_printf("C");
break;
default:
IOT_ASSERT(0);
break;
}
}
iot_printf("------------------------------------------------------\n");
#endif /* MAC_SCHED_DEBUG */
return total_cnt;
}
/* load command list into the HW scheduler, return 1 if the command list
* is already done.
*/
static uint32_t mac_sched_load_cmd_list(mac_vdev_t *vdev,
hw_sched_cmd_list_t *cl)
{
uint32_t cnt, curr_dur, left_dur;
hw_sched_cmd_t *start_cmd, *end_cmd, *last_cmd, *prev_cmd = NULL;
if (cl->next_idx == cl->total_cnt) {
/* current command list is done */
return 1;
}
IOT_ASSERT(cl->next_idx < cl->total_cnt);
/* set start ntb of next beacon period */
mac_sched_set_bp_start_ntb(vdev, cl->start_ntb);
/* calculate how many commands can be push into the HW scheduler */
cnt = cl->total_cnt - cl->next_idx;
cnt = min(cnt, HW_SHCED_CMD_DEPTH);
start_cmd = cl->cmd + cl->next_idx;
if (cl->next_idx) {
prev_cmd = start_cmd - 1;
}
end_cmd = start_cmd + (cnt - 1);
last_cmd = cl->cmd + (cl->total_cnt - 1);
/* push commands into the HW */
mac_sched_set_bp_cmd_list(vdev, start_cmd, (uint16_t)cnt);
if (cl->recursive) {
/* for recursive command list, no fragment required */
IOT_ASSERT(cl->total_cnt <= HW_SHCED_CMD_DEPTH);
/* set command list duration */
mac_sched_set_bp_dur(vdev, (uint16_t)start_cmd->t_info.se.end_t);
#if MAC_SCHED_DEBUG
iot_printf("%s %lu start offset %lu, end offset %lu\n", __FUNCTION__,
cl->total_cnt, start_cmd->t_info.se.start_t,
start_cmd->t_info.se.end_t);
#endif
} else {
if (prev_cmd) {
curr_dur = end_cmd->t_info.e.end_t - prev_cmd->t_info.e.end_t;
} else {
curr_dur = end_cmd->t_info.e.end_t;
}
if (last_cmd != end_cmd) {
/* reduce current command list duration to make sure enough
* duration left for the last command list.
*/
left_dur = last_cmd->t_info.e.end_t - end_cmd->t_info.e.end_t;
while (left_dur < MAC_SCHED_CMD_LIST_DUR_MIN) {
end_cmd--;
cnt--;
IOT_ASSERT(end_cmd > start_cmd);
left_dur = last_cmd->t_info.e.end_t - end_cmd->t_info.e.end_t;
}
if (prev_cmd) {
curr_dur = end_cmd->t_info.e.end_t - prev_cmd->t_info.e.end_t;
} else {
curr_dur = end_cmd->t_info.e.end_t;
}
}
if (prev_cmd) {
IOT_ASSERT(curr_dur >= MAC_SCHED_CMD_LIST_DUR_MIN);
}
/* set command list duration */
mac_sched_set_bp_dur(vdev, (uint16_t)end_cmd->t_info.e.end_t);
#if MAC_SCHED_DEBUG
iot_printf("%s %lu start offset %lu, end offset %lu\n", __FUNCTION__,
cl->total_cnt, prev_cmd ? prev_cmd->t_info.e.end_t : 0,
end_cmd->t_info.e.end_t);
#endif
}
/* trigger new beacon period */
mac_sched_trigger_bp(vdev);
cl->next_idx += cnt;
return 0;
}
void mac_sched_cco_snr_rx(mac_vdev_t *vdev, uint32_t nid, int8_t snr,
uint8_t rf_id, uint8_t rf_option, uint8_t is_rf, uint8_t band_id)
{
mac_cco_snr_rpt_t *rpt;
iot_pkt_t *pkt;
if (!vdev->start_cfg.mac_cco_snr_rx_func) {
goto out;
}
pkt = iot_pkt_alloc(sizeof(*rpt), PLC_MAC_SCHED_MID);
if (!pkt) {
goto out;
}
rpt = (mac_cco_snr_rpt_t *)iot_pkt_data(pkt);
rpt->nid = nid;
rpt->snr = snr;
rpt->rf_channel = rf_id;
rpt->rf_option = rf_option;
rpt->is_rf = is_rf;
rpt->band_id = band_id;
vdev->start_cfg.mac_cco_snr_rx_func(vdev->start_cfg.mac_callback_arg, pkt);
out:
return;
}
#if (PLC_SUPPORT_CCO_ROLE)
void mac_sched_nn_report_nid(mac_vdev_t *vdev)
{
iot_pkt_t *buf;
uint8_t i;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
mac_nid_list_rpt_t *rpt;
if (ctx->curr_nid_list_cnt == 0)
return;
buf = iot_pkt_alloc(sizeof(*rpt) +
(sizeof(rpt->nid_list[0]) * ctx->curr_nid_list_cnt), PLC_MAC_SCHED_MID);
if (!buf)
return;
rpt = (mac_nid_list_rpt_t *)iot_pkt_data(buf);
rpt->nid_cnt = ctx->curr_nid_list_cnt;
for (i = 0; i < ctx->curr_nid_list_cnt; i++) {
rpt->nid_list[i].nid = ctx->curr_nid_list[i].nid;
rpt->nid_list[i].sp_flag = !(ctx->curr_nid_list[i].watched);
rpt->nid_list[i].bandwidth = ctx->curr_nid_list[i].region_dur;
rpt->nid_list[i].rf_channel = ctx->curr_nid_list[i].self_ch_id;
rpt->nid_list[i].rf_option = ctx->curr_nid_list[i].self_option;
}
vdev->start_cfg.mac_nid_rpt_func(vdev->start_cfg.mac_callback_arg, buf);
}
/* @brief mac_sched_nn_calc_start_ntb() - calculate start ntb according to
* latest neighbor network info
* @param vdev: pointer to mac vdev.
* @param start_ntb64: planned start ntb 64bit version.
* @param region_dur: required protected region length. unit is 1ms.
* @param latest_end_ntb64: end ntb 64bit version of current conflict region.
* @retval calculated start ntb 64bit version.
*/
static uint64_t mac_sched_nn_calc_start_ntb(mac_vdev_t *vdev,
uint64_t start_ntb64, uint16_t region_dur, uint64_t *latest_end_ntb64)
{
uint8_t i, confict_found, nid_list_cnt, overlapped;
uint32_t ntb_dur;
uint64_t new_start_ntb, new_end_ntb;
mac_nn_info_t *nn_info, *nid_list;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
nid_t nid;
new_start_ntb = start_ntb64;
ntb_dur = MAC_MS_TO_NTB(region_dur);
new_end_ntb = new_start_ntb + ntb_dur;
#if MAC_SCHED_NN_DEBUG
iot_printf("%s next_start_ntb %lu-%lu, next_end_ntb %lu-%lu\n",
__FUNCTION__, iot_uint64_higher32(new_start_ntb),
iot_uint64_lower32(new_start_ntb), iot_uint64_higher32(new_end_ntb),
iot_uint64_lower32(new_end_ntb));
#endif
*latest_end_ntb64 = 0;
nid_list_cnt = ctx->curr_nid_list_cnt;
nid_list = ctx->curr_nid_list;
again:
confict_found = 0;
/* compare all networks to find out the bandwidth allocation */
for (i = 0; i < nid_list_cnt; i++) {
if (nid_list[i].nid == 0 || nid_list[i].region_dur == 0)
continue;
/* found existing slot */
nn_info = &nid_list[i];
if ((nn_info->start_ntb - MAC_SCHED_NN_BW_CHG_TH) <= new_start_ntb
&&
(nn_info->end_ntb - MAC_SCHED_NN_BW_CHG_TH) <= new_start_ntb) {
/* no overlapped region */
overlapped = 0;
} else if ((nn_info->start_ntb + MAC_SCHED_NN_BW_CHG_TH)
>= new_end_ntb &&
(nn_info->end_ntb + MAC_SCHED_NN_BW_CHG_TH) >= new_end_ntb) {
/* no overlapped region */
overlapped = 0;
} else {
overlapped = 1;
}
if (overlapped == 0) {
if ((nn_info->end_ntb > new_end_ntb) &&
(nn_info->end_ntb - new_end_ntb) < MAC_SCHED_NN_BW_GUARD) {
if (*latest_end_ntb64 < nn_info->end_ntb) {
*latest_end_ntb64 = nn_info->end_ntb;
}
}
continue;
} else {
if (*latest_end_ntb64 < nn_info->end_ntb) {
*latest_end_ntb64 = nn_info->end_ntb;
}
}
/* if code hit here, it means overlapped protected region
* detected.
*/
if (nn_info->watched == 0) {
/* rule 1, if remote network can't see us, we should be back
* off.
*/
#if MAC_SCHED_NN_DEBUG
iot_printf("%s rule 1 applied, nn_start_ntb %lu-%lu, "
"nn_end_ntb %lu-%lu\n", __FUNCTION__,
iot_uint64_higher32(nn_info->start_ntb),
iot_uint64_lower32(nn_info->start_ntb),
iot_uint64_higher32(nn_info->end_ntb),
iot_uint64_lower32(nn_info->end_ntb));
#endif
new_start_ntb = nn_info->end_ntb + MAC_SCHED_NN_BW_CHG_TH;
new_end_ntb = new_start_ntb + ntb_dur;
confict_found = 1;
continue;
}
if (((nn_info->start_ntb + MAC_SCHED_NN_BW_CHG_TH)
<= new_start_ntb) &&
((nn_info->end_ntb + MAC_SCHED_NN_BW_CHG_TH)
<= new_end_ntb)) {
/* rule 2, if remote network beacon period ended first, we
* should be back off.
*/
#if MAC_SCHED_NN_DEBUG
iot_printf("%s rule 2 applied, nn_start_ntb %lu-%lu, "
"nn_end_ntb %lu-%lu\n", __FUNCTION__,
iot_uint64_higher32(nn_info->start_ntb),
iot_uint64_lower32(nn_info->start_ntb),
iot_uint64_higher32(nn_info->end_ntb),
iot_uint64_lower32(nn_info->end_ntb));
#endif
new_start_ntb = nn_info->end_ntb + MAC_SCHED_NN_BW_CHG_TH;
new_end_ntb = new_start_ntb + ntb_dur;
confict_found = 1;
continue;
} else if ((nn_info->start_ntb - MAC_SCHED_NN_BW_CHG_TH)
< new_start_ntb) {
/* two networks ended in same time */
if ((ERR_OK == vdev_get_nid(vdev, &nid)) \
&& (PLC_NID_INVALID != nid)) {
if (nn_info->nid < nid) {
/* rule 3, if remote network nid is smaller than us, we
* should be back off.
*/
#if MAC_SCHED_NN_DEBUG
iot_printf("%s rule 3 applied, nn_start_ntb %lu-%lu, "
"nn_end_ntb %lu-%lu\n", __FUNCTION__,
iot_uint64_higher32(nn_info->start_ntb),
iot_uint64_lower32(nn_info->start_ntb),
iot_uint64_higher32(nn_info->end_ntb),
iot_uint64_lower32(nn_info->end_ntb));
#endif
new_start_ntb = nn_info->end_ntb + MAC_SCHED_NN_BW_CHG_TH;
new_end_ntb = new_start_ntb + ntb_dur;
confict_found = 1;
continue;
}
}
}
}
if (confict_found)
goto again;
*latest_end_ntb64 += MAC_SCHED_NN_BW_CHG_TH;
#if MAC_SCHED_NN_DEBUG
if (start_ntb64 != new_start_ntb) {
iot_printf("%s start_ntb %lu-%lu, new_start_ntb %lu-%lu, "
"latest_end_ntb %lu-%lu\n",
__FUNCTION__,
iot_uint64_higher32(start_ntb64),
iot_uint64_lower32(start_ntb64),
iot_uint64_higher32(new_start_ntb),
iot_uint64_lower32(new_start_ntb),
iot_uint64_higher32(*latest_end_ntb64),
iot_uint64_lower32(*latest_end_ntb64));
}
#endif
return new_start_ntb;
}
static void mac_sched_nn_update(mac_vdev_t *vdev)
{
uint8_t i, j, remove;
uint64_t curr_ntb64;
mac_nn_info_t *nn_info;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
mac_sched_nn_dump(vdev);
/* save neighbor network info watched in current beacon period */
os_mem_cpy(ctx->prev_nid_list, ctx->curr_nid_list,
sizeof(ctx->curr_nid_list));
ctx->prev_nid_list_cnt = ctx->curr_nid_list_cnt;
curr_ntb64 = mac_sched_get_ntb64(vdev);
/* clean up current neighbor network info */
i = 0;
while (i < ctx->curr_nid_list_cnt) {
nn_info = &ctx->curr_nid_list[i];
remove = 0;
if (nn_info->watched) {
if (curr_ntb64 >= nn_info->end_ntb ||
(nn_info->end_ntb + MAC_SCHED_NN_BW_CHG_TH) <=
vdev->bcn_ctx.cco.next_start_ntb64) {
/* neighbor network protected region invalid or no overlapped
* region, remove network from current neighbor network list
*/
remove = 1;
}
#if HW_PLATFORM != HW_PLATFORM_SIMU
} else {
/* for neighbor network which can't see us, let's keep it for
* a while to pass cert case 3.2.9.2.
*/
if ((curr_ntb64 - nn_info->rx_ntb) > MAC_SCHED_NN_NO_WATCH_TO_DUR) {
remove = 1;
}
#endif /* HW_PLATFORM != HW_PLATFORM_SIMU */
}
if (remove) {
j = ctx->curr_nid_list_cnt - 1;
if (j > i) {
os_mem_cpy(nn_info, &ctx->curr_nid_list[j], sizeof(*nn_info));
}
os_mem_set(&ctx->curr_nid_list[j], 0, sizeof(*nn_info));
ctx->curr_nid_list_cnt--;
} else {
i++;
}
}
/* reset neighbor network info broadcast index to 0 for the new
* beacon period.
*/
ctx->nid_bc_idx = 0;
}
static void mac_sched_nn_tx_internal(mac_vdev_t *vdev, uint32_t nid)
{
uint64_t curr_ntb;
uint32_t dur;
uint8_t cert_mode = mac_get_cert_test_flag();
mac_sched_ctx_t *ctx = vdev->sched_ctx;
if (PLC_SUPPORT_NEIGHBOR_NW_NEGO &&
ctx->nw_nego_en &&
ctx->allow_tx &&
ctx->nid_conflict == 0) {
curr_ntb = mac_sched_get_ntb64(vdev);
if (curr_ntb <= ctx->allow_start_ntb ||
curr_ntb >= ctx->allow_end_ntb)
return;
/* calculate protected zone duration */
dur = ctx->region_dur;
curr_ntb = vdev->bcn_ctx.cco.next_start_ntb64;
uint64_t last_ntb = vdev->bcn_ctx.cco.curr_start_ntb64;
/* fix gw hplc cert test bed case 3.2.9.5, cco nncco tx too frequently,
* test bed sof TX has conflict with NNCCO frame when using kl3
* hardware device.
*/
if (cert_mode && !vdev->sta_joined) {
/* if tx at single phase or cert mode currently
* we do tx 3 packets seperately one by one
*/
if (PLC_SUPPORT_CCO_TX_3_PHASE_SLOT) {
/* still need to queue 3 packet here to fullfil cert test
* case 3.2.9.1.
*/
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO,
HW_DESC_TX_PORT_PLC, 3, dur, last_ntb, curr_ntb, nid,
PLC_PHASE_A);
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO,
HW_DESC_TX_PORT_PLC, 3, dur, last_ntb, curr_ntb, nid,
PLC_PHASE_A);
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO,
HW_DESC_TX_PORT_PLC, 3, dur, last_ntb, curr_ntb, nid,
PLC_PHASE_A);
} else {
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO,
HW_DESC_TX_PORT_PLC, 3, dur, last_ntb, curr_ntb, nid,
vdev->l_phase1);
if (vdev->l_phase2) {
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO,
HW_DESC_TX_PORT_PLC, 3, dur, last_ntb, curr_ntb, nid,
vdev->l_phase2);
}
if (vdev->l_phase3) {
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO,
HW_DESC_TX_PORT_PLC, 3, dur, last_ntb, curr_ntb, nid,
vdev->l_phase3);
}
}
} else {
phase_t tdphase = PLC_PHASE_ALL;
if (vdev->nncco_tx_3phase && !cert_mode) {
tdphase = (phase_t)vdev->nncco_td_tx_phase;
if (tdphase == PLC_PHASE_A) {
if (vdev->l_phase2) {
vdev->nncco_td_tx_phase = PLC_PHASE_B;
} else if (vdev->l_phase3) {
vdev->nncco_td_tx_phase = PLC_PHASE_C;
}
} else if (tdphase == PLC_PHASE_B) {
if (vdev->l_phase3) {
vdev->nncco_td_tx_phase = PLC_PHASE_C;
} else {
vdev->nncco_td_tx_phase = PLC_PHASE_A;
}
} else {
vdev->nncco_td_tx_phase = PLC_PHASE_A;
}
}
/* if tx at 3 phase and not cert mode,
* we send nncco at 3 phase simutenously
* and so just one packet is enough.
*/
mac_tx_nncco(vdev->ref_pdev_id,
vdev->bcn_ctx.fc.nid, FC_DELIM_NNCCO, HW_DESC_TX_PORT_PLC,
3, dur, last_ntb, curr_ntb, nid, tdphase);
}
}
}
static void mac_sched_nn_tx_start(mac_vdev_t *vdev, uint64_t start_ntb,
uint64_t end_ntb)
{
uint64_t tmp_ntb;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
if (ctx->allow_tx == 0) {
ctx->allow_tx = 1;
ctx->bc_all = 1;
ctx->allow_start_ntb = start_ntb;
ctx->allow_end_ntb = end_ntb;
tmp_ntb = mac_sched_get_ntb64(vdev);
if (start_ntb > tmp_ntb) {
#if MAC_SCHED_TIME_DISORDER_DEBUG
uint64_t tmp_ntb_pt = tmp_ntb;
tmp_ntb = start_ntb - tmp_ntb;
if (iot_uint64_higher32(tmp_ntb) != 0) {
iot_printf("%s, start_ntb: %lu-%lu, tmp_ntb: %lu-%lu\n",
__FUNCTION__,
iot_uint64_higher32(start_ntb),
iot_uint64_lower32(start_ntb),
iot_uint64_higher32(tmp_ntb_pt),
iot_uint64_lower32(tmp_ntb_pt));
IOT_ASSERT(0);
}
#else
tmp_ntb = start_ntb - tmp_ntb;
IOT_ASSERT(iot_uint64_higher32(tmp_ntb) == 0);
#endif
/* add one more ms to avoid 0 interval timer */
os_start_timer(ctx->tx_timer,
MAC_NTB_TO_MS(iot_uint64_lower32(tmp_ntb) + 1));
} else {
os_start_timer(ctx->tx_timer, 1);
}
}
}
static void mac_sched_nn_tx_stop(mac_vdev_t *vdev)
{
mac_sched_ctx_t *ctx = vdev->sched_ctx;
if (ctx->allow_tx) {
os_stop_timer(ctx->tx_timer);
ctx->allow_tx = 0;
}
}
static void mac_sched_tx_timer_func(timer_id_t timer_id, void *arg)
{
(void)timer_id;
mac_msg_t *msg;
msg = mac_alloc_msg();
if (msg) {
msg->type = MAC_MSG_TYPE_TIMER;
msg->id = MAC_MSG_ID_SCHED_TX;
msg->data1 = (uint32_t)arg;
mac_queue_msg(msg, MAC_MSG_QUEUE_HP);
} else {
/* maybe delay the timer handling is a better choice */
IOT_ASSERT(0);
}
}
static uint64_t mac_sched_nn_new_bp_start(mac_vdev_t *vdev,
uint64_t start_ntb64, uint64_t *next_start_ntb64,
uint16_t protect_region_dur, uint16_t csma_offset)
{
uint32_t ntb_dur;
uint64_t new_start_ntb64, end_ntb64;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
new_start_ntb64 = start_ntb64;
mac_pdev_t *pdev = get_pdev_ptr(PLC_PDEV_ID);
new_start_ntb64 = mac_cco_check_spur_get_intvl(&pdev->mac_check_spur_ctxt, \
new_start_ntb64);
if (!PLC_SUPPORT_NEIGHBOR_NW_NEGO || !ctx->nw_nego_en) {
*next_start_ntb64 = new_start_ntb64 +
MAC_MS_TO_NTB(vdev->bcn_ctx.time_slot.bc_period);
goto out;
}
ntb_dur = MAC_MS_TO_NTB(ctx->region_dur);
if (ntb_dur == 0) {
/* if protected region length is zero, it may be the first beacon
* period. suppose next protected region is same as current protected
* region.
*/
ntb_dur = MAC_MS_TO_NTB(vdev->bcn_ctx.time_slot.protected_region_dur);
if (ntb_dur == 0) {
*next_start_ntb64 = new_start_ntb64 +
MAC_MS_TO_NTB(vdev->bcn_ctx.time_slot.bc_period);
goto out;
}
}
new_start_ntb64 = mac_sched_nn_calc_start_ntb(vdev, new_start_ntb64,
(uint16_t)MAC_NTB_TO_MS(ntb_dur), &end_ntb64);
*next_start_ntb64 = new_start_ntb64 +
MAC_MS_TO_NTB(vdev->bcn_ctx.time_slot.bc_period);
if (end_ntb64 > *next_start_ntb64)
*next_start_ntb64 = end_ntb64;
/* init new protected region length */
ctx->region_dur = protect_region_dur;
mac_sched_nn_update(vdev);
/* start tx timer for nncco frame */
if (vdev->bcn_ctx.cco.started) {
#if MAC_SCHED_TIME_DISORDER_DEBUG
iot_printf("%s, start_ntb: %lu-%lu, csma_offset:%lu\n", __FUNCTION__,
iot_uint64_higher32(new_start_ntb64),
iot_uint64_lower32(new_start_ntb64),
csma_offset);
#endif
mac_sched_nn_tx_start(vdev,
new_start_ntb64 + MAC_MS_TO_NTB(csma_offset),
(*next_start_ntb64 - MAC_MS_TO_NTB(MAC_BP_AHEAD_ALERT_DUR)));
}
out:
//if (new_start_ntb64 != start_ntb64) {
iot_printf("%s start_ntb: %lu-%lu, new_start_ntb %lu-%lu, "
"next_start_ntb %lu-%lu\n", __FUNCTION__,
iot_uint64_higher32(start_ntb64),
iot_uint64_lower32(start_ntb64),
iot_uint64_higher32(new_start_ntb64),
iot_uint64_lower32(new_start_ntb64),
iot_uint64_higher32(*next_start_ntb64),
iot_uint64_lower32(*next_start_ntb64));
//}
return new_start_ntb64;
}
#if HW_PLATFORM == HW_PLATFORM_SIMU
static uint8_t mac_sched_extract_nn_info(mac_vdev_t *vdev,\
mac_nn_info_t *nn_info, void *fc, uint32_t rx_ntb)
{
uint8_t need_check = 0;
uint64_t ntb64;
nid_t nid;
nncco_fc_info_t nn_fc_info = { 0 };
mac_get_nncco_sw_info_from_fc(fc, &nn_fc_info);
if (ERR_OK == vdev_get_nid(vdev, &nid)) {
if (nn_fc_info.sw_receive_nid == nid) {
nn_info->watched = 1;
}
}
if (nn_fc_info.sw_duration) {
nn_info->region_dur = (uint16_t)nn_fc_info.sw_duration;
/* for simulator case, we only support 32bit ntb */
ntb64 = nn_fc_info.start_ntb_l32;
if (ntb64 > nn_info->start_ntb) {
if ((ntb64 - MAC_SCHED_NN_BW_CHG_TH) >= nn_info->start_ntb)
need_check = 1;
} else {
if ((ntb64 + MAC_SCHED_NN_BW_CHG_TH) <= nn_info->start_ntb)
need_check = 1;
}
nn_info->rx_ntb = rx_ntb;
nn_info->start_ntb = ntb64;
nn_info->end_ntb = nn_info->start_ntb +
MAC_MS_TO_NTB(nn_info->region_dur);
} else {
nn_info->region_dur = 0;
}
return need_check;
}
uint32_t mac_sched_get_nidmap(mac_vdev_t *vdev)
{
(void)vdev;
return 0;
}
#else /* HW_PLATFORM == HW_PLATFORM_SIMU */
/* convert a passed ntb32 value to ntb64 value. the ntb32 value should be at
* most 0xFFFFFFFF ntb ahead of current ntb.
*/
uint64_t mac_sched_ntb_32_to_64(mac_vdev_t *vdev, uint32_t ntb32)
{
uint64_t ntb64;
uint32_t l_ntb, h_ntb;
ntb64 = mac_sched_get_ntb64(vdev);
l_ntb = iot_uint64_lower32(ntb64);
h_ntb = iot_uint64_higher32(ntb64);
if (l_ntb < ntb32) {
/* wrap around happened, move h_ntb back */
IOT_ASSERT(h_ntb);
h_ntb--;
}
ntb64 = h_ntb;
ntb64 <<= 32;
ntb64 |= ntb32;
return ntb64;
}
static uint8_t mac_sched_extract_nn_info(mac_vdev_t *vdev,\
mac_nn_info_t *nn_info, void *fc, uint32_t rx_ntb)
{
uint8_t need_check = 0;
uint64_t ntb64, rx_ntb64;
nid_t nid;
nncco_fc_info_t nn_fc_info = { 0 };
mac_get_nncco_info_from_fc(fc, &nn_fc_info);
uint32_t dur = nn_fc_info.duration;
uint32_t start_offset = nn_fc_info.sbandoffset;
vdev_get_nid(vdev, &nid);
nn_info->self_ch_id = nn_fc_info.self_rf_channel;
nn_info->self_option = nn_fc_info.self_rf_option;
uint32_t rlt = mac_nncco_nid_compare(nn_fc_info.receive_nid, nid);
if (!rlt) {
/* NOTE: not_watch_cnt only 7bit, so can not over 127 */
if (++nn_info->not_watch_cnt > MAC_SCHED_NN_NOT_WATCH_MAX) {
iot_printf("%s, nid:0x%x not see us\n",
__FUNCTION__, nn_fc_info.nid);
nn_info->not_watch_cnt = 0;
nn_info->watched = 0;
}
} else {
nn_info->not_watch_cnt = 0;
nn_info->watched = rlt;
}
if (dur &&
dur < MAC_SCHED_NN_MAX_REGION) {
nn_info->region_dur = (uint16_t)dur;
rx_ntb64 = mac_sched_ntb_32_to_64(vdev, rx_ntb);
ntb64 = rx_ntb64 + MAC_MS_TO_NTB(start_offset);
if (ntb64 > nn_info->start_ntb) {
if ((ntb64 - MAC_SCHED_NN_BW_CHG_TH) >= nn_info->start_ntb)
need_check = 1;
} else {
if ((ntb64 + MAC_SCHED_NN_BW_CHG_TH) <= nn_info->start_ntb)
need_check = 1;
}
nn_info->rx_ntb = rx_ntb64;
nn_info->start_ntb = ntb64;
nn_info->end_ntb = nn_info->start_ntb +
MAC_MS_TO_NTB(nn_info->region_dur);
} else {
nn_info->region_dur = 0;
}
return need_check;
}
#if SUPPORT_SOUTHERN_POWER_GRID
uint32_t mac_sched_get_nidmap(mac_vdev_t *vdev)
{
mac_sched_ctx_t *ctx = vdev->sched_ctx;
uint32_t nid_bitmap = 0;
mac_nn_info_t *nn_info = NULL;
for (uint32_t i = 0; i < ctx->curr_nid_list_cnt; i++) {
nn_info = &ctx->curr_nid_list[i];
nid_bitmap |= (1<< (nn_info->nid -1));
}
return nid_bitmap;
}
#else
uint32_t mac_sched_get_nidmap(mac_vdev_t *vdev)
{
(void)vdev;
return 0;
}
#endif
#endif /* HW_PLATFORM == HW_PLATFORM_SIMU */
void mac_sched_nn_rx(mac_pdev_t *pdev, void *fc, uint32_t ntb,
int8_t snr, uint8_t is_rf, uint8_t band_id)
{
uint8_t i, need_check = 0;
uint64_t ntb64, tmp_ntb64;
mac_vdev_t *vdev = pdev->vdev[0];
mac_sched_ctx_t *ctx = vdev->sched_ctx;
mac_nn_info_t *nn_info = NULL;
uint32_t proto = PHY_PROTO_TYPE_GET();
uint32_t nid = mac_get_nid_from_fc(proto, fc);
nid_t vnid;
if (mac_vdev_cfg_get_node_role(vdev) != PLC_DEV_ROLE_CCO) {
/* todo: get rf info by mac_sched_extract_nn_info */
mac_sched_cco_snr_rx(vdev, nid, snr,
mac_get_nncco_self_ch_id(proto, fc),
mac_get_nncco_self_option(proto, fc), is_rf, band_id);
return;
}
#if MAC_SCHED_NN_DEBUG
mac_debug_nncco_info(fc, ntb);
#endif /* MAC_SCHED_NN_DEBUG */
if (nid == 0)
return;
vdev_get_nid(vdev, &vnid);
if (nid == vnid) {
ctx->nid_conflict = 1;
} else if ((snr < -3) && !mac_get_cert_test_flag()) {
/* ignore network with low snr, reduce unnecessary negotiation */
return;
}
for (i = 0; i < ctx->curr_nid_list_cnt; i++) {
if (ctx->curr_nid_list[i].nid == nid) {
/* found existing slot */
nn_info = &ctx->curr_nid_list[i];
break;
}
}
if (i == ctx->curr_nid_list_cnt) {
if (ctx->curr_nid_list_cnt < PLC_MAX_NEIGHBOR_NETWORK) {
nn_info = &ctx->curr_nid_list[ctx->curr_nid_list_cnt++];
nn_info->nid = nid;
} else {
goto out;
}
}
if (nn_info) {
need_check = mac_sched_extract_nn_info(vdev, nn_info, fc, ntb);
}
if (PLC_SUPPORT_NEIGHBOR_NW_NEGO && ctx->nw_nego_en && need_check
&& ctx->region_dur) {
tmp_ntb64 = mac_sched_nn_calc_start_ntb(vdev,
vdev->bcn_ctx.cco.next_start_ntb64, ctx->region_dur, &ntb64);
if (tmp_ntb64 != vdev->bcn_ctx.cco.next_start_ntb64) {
vdev->bcn_ctx.cco.next_start_ntb64 = tmp_ntb64;
iot_printf("%s conflict detected nid:0x%x,"
"next_bcn:%lu-%lu\n", __FUNCTION__, nid,
iot_uint64_higher32(tmp_ntb64),
iot_uint64_lower32(tmp_ntb64));
/* local network bandwidth allocation changed, notify
* neighbor networks as soon as possible.
*/
mac_sched_nn_update(vdev);
mac_sched_nn_tx_internal(vdev, nn_info->nid);
}
}
if ((nid == vnid) || ((RF_CHANNEL_ID_INVALID != nn_info->self_ch_id)
&& (RF_OPTION_INVALID != nn_info->self_option)
&& (nn_info->self_ch_id == mac_rf_get_self_channel())
&& (nn_info->self_option == mac_rf_get_self_option()))) {
/* NID or channel id conflict detected, report to cvg immediately */
mac_sched_nn_report_nid(vdev);
}
out:
return;
}
void mac_sched_nn_tx(mac_vdev_t *vdev)
{
uint8_t i;
uint32_t nid;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
if (PLC_SUPPORT_NEIGHBOR_NW_NEGO && ctx->nw_nego_en && ctx->allow_tx) {
os_start_timer(ctx->tx_timer, MAC_SCHED_NN_TX_INTERVAL);
mac_sched_nn_update(vdev);
if (ctx->bc_all) {
ctx->bc_all = 0;
if (ctx->curr_nid_list_cnt) {
for (i = 0; i < ctx->curr_nid_list_cnt; i++) {
nid = ctx->curr_nid_list[i].nid;
mac_sched_nn_tx_internal(vdev, nid);
}
} else {
mac_sched_nn_tx_internal(vdev, 0);
}
} else {
if (ctx->nid_bc_idx < ctx->curr_nid_list_cnt) {
nid = ctx->curr_nid_list[ctx->nid_bc_idx++].nid;
} else if (ctx->curr_nid_list_cnt) {
ctx->nid_bc_idx = 0;
nid = ctx->curr_nid_list[ctx->nid_bc_idx++].nid;
} else {
ctx->nid_bc_idx = 0;
nid = 0;
}
mac_sched_nn_tx_internal(vdev, nid);
}
}
}
static void mac_sched_cco_init(mac_vdev_t *vdev)
{
uint32_t id;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
if (mac_vdev_cfg_get_node_role(vdev) == PLC_DEV_ROLE_CCO) {
id = (vdev->ref_pdev_id << 8) | vdev->vdev_id;
ctx->tx_timer = os_create_timer(PLC_MAC_SCHED_MID, false,
mac_sched_tx_timer_func, (void *)id);
ctx->nw_nego_en = 1;
}
}
static void mac_sched_cco_stop(mac_vdev_t *vdev)
{
mac_sched_ctx_t *ctx = vdev->sched_ctx;
if (mac_vdev_cfg_get_node_role(vdev) == PLC_DEV_ROLE_CCO) {
mac_sched_nn_tx_stop(vdev);
ctx->nid_conflict = 0;
}
}
/* cco role device need to take care of both phase and band config */
uint64_t mac_sched_cco_set(mac_vdev_t *vdev, mac_bc_time_slot_t *ts,
uint64_t start_ntb64, uint64_t *next_start_ntb64)
{
uint16_t i, cnt = 0;
uint64_t tmp_ntb;
uint32_t tmp_cnt;
uint16_t csma_slot_cnt, csma_frag, start_offset = 0;
uint16_t csma_offset, end_offset;
hw_sched_cmd_list_t *cl = NULL;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_t *cmd, *prev_cmd;
uint32_t proto = PHY_PROTO_TYPE_GET();
cl = mac_sched_alloc_cmd_list(vdev);
IOT_ASSERT(cl);
ctx->curr_hw_cmd_list = cl;
cmd = cl->cmd;
//TODO: dbg for hw sched cmd
cl->alloc_ntb = mac_sched_get_ntb(vdev);
cl->caller = 1;
/* prepare central beacon slot */
for (i = 0; i < ts->cco_bc_cnt; i++) {
if (i < PLC_PHASE_CNT) {
cmd->phase = i + 1;
cmd->tx_q_en_bm = mac_sched_get_wb_bc_q(vdev, (uint8_t)cmd->phase);
start_offset += ts->bc_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
} else {
cmd->phase = PLC_PHASE_ALL;
cmd->tx_q_en_bm = 0;
start_offset += ts->bc_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
}
}
/* prepere proxy and discovery beacon slot */
if (ts->non_cco_bc_info.bc_cnt) {
cmd->phase = ts->non_cco_bc_info.phase[0];
start_offset += ts->bc_slot_dur;
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < ts->non_cco_bc_info.bc_cnt; i++) {
if (cmd->phase != ts->non_cco_bc_info.phase[i]) {
/* a new time slot required */
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = ts->non_cco_bc_info.phase[i];
}
start_offset += ts->bc_slot_dur;
cmd->t_info.e.end_t = start_offset;
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
/* prepare tdma slot. tdma is behind csma for SPG */
if ((proto != PLC_PROTO_TYPE_SPG) && ts->tdma_slot_dur) {
/* prepare tdma for cco device A, B, C phase */
for (i = 0; i < ts->cco_bc_cnt && i < PLC_PHASE_CNT; i++) {
cmd->phase = i + 1;
cmd->tx_q_en_bm = mac_sched_get_tdma_q(vdev, (uint8_t)cmd->phase);
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
}
/* prepare tdma for pco and sta device */
if (ts->non_cco_bc_info.bc_cnt) {
cmd->phase = ts->non_cco_bc_info.phase[0];
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < ts->non_cco_bc_info.bc_cnt; i++) {
if (cmd->phase != ts->non_cco_bc_info.phase[i]) {
/* a new time slot required */
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = ts->non_cco_bc_info.phase[i];
}
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
}
csma_offset = start_offset;
/* prepare csma slot */
csma_frag = ts->csma_slot_frag_s;
csma_slot_cnt = mac_sched_split_csma(&ts->csma_info, ts->csma_slot_frag_s);
if (csma_slot_cnt) {
cmd->phase = g_csma_slot[0].phase;
if (PLC_SUPPORT_CCO_TX_3_PHASE_SLOT) {
/* always enable PHASE A HWQ as cco always send packets in
* 3 physical phases simultaneously.
*/
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, PLC_PHASE_A);
} else {
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, (uint8_t)cmd->phase);
}
if (g_csma_slot[0].last) {
start_offset += g_slot_last[g_csma_slot[0].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < csma_slot_cnt; i++) {
if (cmd->phase != g_csma_slot[i].phase) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = g_csma_slot[i].phase;
if (PLC_SUPPORT_CCO_TX_3_PHASE_SLOT) {
/* always enable PHASE A HWQ as cco always send packets in
* 3 physical phases simultaneously.
*/
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, PLC_PHASE_A);
} else {
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
(uint8_t)cmd->phase);
}
}
if (g_csma_slot[i].last) {
start_offset += g_slot_last[g_csma_slot[i].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
}
if (MAC_SCHED_CSMA_GAP_MS) {
prev_cmd = cmd - 1;
if ((cmd->t_info.e.end_t - prev_cmd->t_info.e.end_t) >
MAC_SCHED_CSMA_GAP_MS) {
/* last slot has enough room to reserve the gap */
cmd->t_info.e.end_t -= MAC_SCHED_CSMA_GAP_MS;
prev_cmd = cmd;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->tx_q_en_bm = 0;
cmd->phase = prev_cmd->phase;
cmd->t_info.e.end_t = start_offset;
} else {
/* last sot has no enough room to reserve the gap, disable tx
* for the last slot.
*/
cmd->tx_q_en_bm = 0;
}
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
/* prepare tdma slot. tdma is behind csma for SPG */
if ((proto == PLC_PROTO_TYPE_SPG) && ts->tdma_slot_dur) {
/* prepare tdma for cco device A, B, C phase */
for (i = 0; i < ts->cco_bc_cnt && i < PLC_PHASE_CNT; i++) {
cmd->phase = i + 1;
cmd->tx_q_en_bm = mac_sched_get_tdma_q(vdev, (uint8_t)cmd->phase);
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
}
/* prepare tdma for pco and sta device */
if (ts->non_cco_bc_info.bc_cnt) {
cmd->phase = ts->non_cco_bc_info.phase[0];
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < ts->non_cco_bc_info.bc_cnt; i++) {
if (cmd->phase != ts->non_cco_bc_info.phase[i]) {
/* a new time slot required */
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = ts->non_cco_bc_info.phase[i];
}
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
}
/* prepare dedicated csma slot */
csma_slot_cnt = mac_sched_split_csma(&ts->d_csma_info,
ts->csma_slot_frag_s);
if (csma_slot_cnt) {
cmd->phase = g_csma_slot[0].phase;
if (PLC_SUPPORT_CCO_TX_3_PHASE_SLOT) {
/* always enable PHASE A HWQ as cco always send packets in
* 3 physical phases simultaneously.
*/
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev, PLC_PHASE_A);
} else {
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev, (uint8_t)cmd->phase);
}
if (g_csma_slot[0].last) {
start_offset += g_slot_last[g_csma_slot[0].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < csma_slot_cnt; i++) {
if (cmd->phase != g_csma_slot[i].phase) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = g_csma_slot[i].phase;
if (PLC_SUPPORT_CCO_TX_3_PHASE_SLOT) {
/* always enable PHASE A HWQ as cco always send packets in
* 3 physical phases simultaneously.
*/
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev, PLC_PHASE_A);
} else {
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev,
(uint8_t)cmd->phase);
}
}
if (g_csma_slot[i].last) {
start_offset += g_slot_last[g_csma_slot[i].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
}
if (MAC_SCHED_CSMA_GAP_MS) {
prev_cmd = cmd - 1;
if ((cmd->t_info.e.end_t - prev_cmd->t_info.e.end_t) >
MAC_SCHED_CSMA_GAP_MS) {
/* last slot has enough room to reserve the gap */
cmd->t_info.e.end_t -= MAC_SCHED_CSMA_GAP_MS;
prev_cmd = cmd;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->tx_q_en_bm = 0;
cmd->phase = prev_cmd->phase;
cmd->t_info.e.end_t = start_offset;
} else {
/* last sot has no enough room to reserve the gap, disable tx
* for the last slot.
*/
cmd->tx_q_en_bm = 0;
}
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
start_ntb64 = mac_sched_nn_new_bp_start(vdev, start_ntb64,
next_start_ntb64, ts->protected_region_dur, csma_offset);
cl->start_ntb = iot_uint64_lower32(start_ntb64);
cl->start_ntb_h = iot_uint64_higher32(start_ntb64);
tmp_ntb = *next_start_ntb64 - start_ntb64;
IOT_ASSERT(iot_uint64_higher32(tmp_ntb) == 0);
end_offset = (uint16_t)MAC_NTB_TO_MS(iot_uint64_lower32(tmp_ntb));
while (end_offset > start_offset) {
/* there is a hole between the end of current bp to the start of next
* bp, fill in rx only slot for the hole to:
* 1. delay the next beacon end alert to delay the next bp start ntb
* final calculation to avoid protected region conflict.
* 2. switch among different phases to increase the possiblilty to
* receive neighbor network negotiation frame from other networks.
*/
start_offset += MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
if (start_offset > end_offset)
start_offset = end_offset;
cmd->phase = vdev->l_phase1;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, (uint8_t)cmd->phase);
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
if (vdev->l_phase2 && end_offset > start_offset) {
start_offset += MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
if (start_offset > end_offset)
start_offset = end_offset;
cmd->phase = vdev->l_phase2;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, (uint8_t)cmd->phase);
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
}
if (vdev->l_phase3 && end_offset > start_offset) {
start_offset += MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
if (start_offset > end_offset)
start_offset = end_offset;
cmd->phase = vdev->l_phase3;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, (uint8_t)cmd->phase);
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
}
}
tmp_ntb = mac_sched_get_ntb64(vdev);
if ((tmp_ntb + MAC_SCHED_INSERT_CSMA_TH) < start_ntb64) {
/* there is a hole between current ntb and start ntb of the new beacon
* period. let's insert csma region to improve the bandwidth
* utilization.
*/
tmp_cnt = (uint32_t)(MAC_NTB_TO_MS(start_ntb64 - tmp_ntb) /
MAC_SCHED_CCO_RX_ONLY_SLOT_DUR);
/* make sure command list memory is enough */
tmp_cnt = min(tmp_cnt, ((uint32_t)HW_SCHED_CMD_MAX_CNT - cnt));
/* make sure command end offset won't be overflowed */
tmp_cnt = min(tmp_cnt,
(0x7FFF - start_offset) / (uint32_t)MAC_SCHED_CCO_RX_ONLY_SLOT_DUR);
if (tmp_cnt) {
iot_printf("%s insert csma %lu into hole from %lu to %lu\n",
__FUNCTION__, tmp_cnt, iot_uint64_lower32(tmp_ntb),
iot_uint64_lower32(start_ntb64));
/* try to insert csma command put as many as possible */
os_mem_move(&cl->cmd[tmp_cnt], &cl->cmd[0],
sizeof(cl->cmd[0]) * cnt);
cmd = cl->cmd;
start_offset = 0;
i = 0;
while (i < tmp_cnt) {
start_offset += MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
cmd->phase = vdev->l_phase1;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
(uint8_t)cmd->phase);
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
i++;
if (vdev->l_phase2 && i < tmp_cnt) {
start_offset += MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
cmd->phase = vdev->l_phase2;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
(uint8_t)cmd->phase);
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
i++;
}
if (vdev->l_phase3 && i < tmp_cnt) {
start_offset += MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
cmd->phase = vdev->l_phase3;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
(uint8_t)cmd->phase);
cmd->t_info.e.end_t = start_offset;
cnt++;
cmd++;
i++;
}
}
cmd--;
/* disable tx for the last csma command before beacon region.
* see MAC_SCHED_CSMA_GAP_MS for more info.
*/
cmd->tx_q_en_bm = 0;
cmd++;
/* fix the end offset of following commands */
for (; i < cnt; i++) {
cmd->t_info.e.end_t += start_offset;
cmd++;
}
cl->start_ntb -=
(MAC_MS_TO_NTB(MAC_SCHED_CCO_RX_ONLY_SLOT_DUR) * tmp_cnt);
}
}
cl->total_cnt = cnt;
mac_sched_dump(cl, cnt);
mac_sched_load_cmd_list(vdev, cl);
mac_sched_enable_bp(vdev, 1);
return start_ntb64;
}
/* cco role device need to take care of both phase and band config */
void mac_sched_cco_set_htbus(mac_vdev_t *vdev, mac_bc_htbus_time_slot_t *ts,
uint64_t start_ntb64)
{
uint16_t cnt = 0;
uint16_t start_offset = 0;
uint8_t rsp_slot_cnt;
hw_sched_cmd_list_t *cl = NULL;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_t *cmd;
cl = mac_sched_alloc_cmd_list(vdev);
IOT_ASSERT(cl);
ctx->curr_hw_cmd_list = cl;
cmd = cl->cmd;
cl->alloc_ntb = mac_sched_get_ntb(vdev);
cl->caller = 1;
cl->start_ntb = iot_uint64_lower32(start_ntb64);
cl->start_ntb_h = iot_uint64_higher32(start_ntb64);
/* prepare request command slot */
if (ts->cco_slot) {
cmd->phase = vdev->l_phase1;
start_offset += ts->cco_slot;
cmd->t_info.e.end_t = start_offset;
cmd->tx_q_en_bm = mac_sched_get_wb_bc_q(vdev, (uint8_t)cmd->phase);
cnt++;
cmd++;
}
/* prepare response command slot */
rsp_slot_cnt = (uint8_t)iot_bitmap_cbs(ts->rsp_bm, sizeof(ts->rsp_bm));
if (rsp_slot_cnt) {
cmd->phase = vdev->l_phase1;
start_offset += ts->sta_slot * rsp_slot_cnt;
cmd->t_info.e.end_t = start_offset;
cnt++;
}
cl->total_cnt = cnt;
mac_sched_dump(cl, cnt);
mac_sched_load_cmd_list(vdev, cl);
mac_sched_enable_bp(vdev, 1);
}
void mac_sched_cco_set_nw_nego(mac_vdev_t *vdev, uint8_t enable)
{
mac_sched_ctx_t *ctx = vdev->sched_ctx;
ctx->nw_nego_en = !!enable;
}
void mac_sched_cco_bc_alert(mac_vdev_t *vdev)
{
uint64_t tmp;
uint8_t cco_started = 0;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
mac_rf_vdev_t *rf_vdev = get_rf_vdev_ptr(vdev->ref_pdev_id,
RF_PDEV_ID, vdev->rf_vdev_id);
if (vdev->is_up) {
IOT_ASSERT(ctx->curr_hw_cmd_list);
/* load next portion of the current command list */
if (mac_sched_load_cmd_list(vdev, ctx->curr_hw_cmd_list)) {
mac_pdev_t *pdev = get_pdev_ptr(vdev->ref_pdev_id);
mac_cco_check_spur_start_alert(&pdev->mac_check_spur_ctxt);
/* check bcsma pending */
mac_tx_flush_bcsma_pending_queue(&pdev->hwq_hdl);
/* current command list is done */
if (vdev->bcn_ctx.cco.started) {
cco_started = 1;
vdev->start_cfg.mac_bp_end_alert_func(
vdev->start_cfg.mac_callback_arg);
mac_sched_nn_tx_stop(vdev);
} else {
IOT_ASSERT(ctx->curr_hw_cmd_list->next_idx ==
ctx->curr_hw_cmd_list->total_cnt);
/* local device is not beaconing, put device in RX only mode */
tmp = mac_sched_get_ntb64(vdev) + \
MAC_MS_TO_NTB(MAC_BP_AHEAD_ALERT_DUR);
tmp = mac_cco_check_spur_get_intvl(&pdev->mac_check_spur_ctxt, \
tmp);
mac_sched_set_csma_only(vdev, tmp, 0);
}
mac_sched_nn_report_nid(vdev);
/* rf alert cmdlist application */
mac_rf_sched_cco_bc_alert(rf_vdev, cco_started);
} else {
/* make sure HW scheduler started */
mac_sched_enable_bp(vdev, 1);
}
}
}
void mac_dump_sched_dbg()
{
mac_pdev_t *pdev_t = get_pdev_ptr(PLC_PDEV_ID);
mac_vdev_t *vdev = pdev_t->vdev[0];
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_list_t *cl = ctx->curr_hw_cmd_list;
if (cl == NULL) {
return;
}
uint32_t i;
uint32_t cnt = cl->total_cnt;
iot_printf("%s total %lu, recusive %lu ----------------------\n",
__FUNCTION__, cnt, cl->recursive);
/* just need tdma cmd */
for (i = 0; i < min(3, cnt); i++) {
if (cl->cmd[i].t_info.e.r_flag) {
if (cl->cmd[i].t_info.e.s_flag) {
iot_printf("rs phase - %lu, start offset %lu, "
"end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.se.start_t,
cl->cmd[i].t_info.se.end_t, cl->cmd[i].tx_q_en_bm);
} else {
if (cl->cmd[i].t_info.r.rf_flag) {
iot_printf("rf phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.r.end_t,
cl->cmd[i].tx_q_en_bm);
}
if (cl->cmd[i].t_info.r.re_flag) {
iot_printf("rl phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.r.end_t,
cl->cmd[i].tx_q_en_bm);
}
if (!cl->cmd[i].t_info.r.rf_flag &&
!cl->cmd[i].t_info.r.re_flag) {
iot_printf("rm phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.r.end_t,
cl->cmd[i].tx_q_en_bm);
}
}
} else if (cl->cmd[i].t_info.e.s_flag) {
iot_printf("st phase - %lu, start offset %lu, end offset - %lu, "
"q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.se.start_t,
cl->cmd[i].t_info.se.end_t, cl->cmd[i].tx_q_en_bm);
} else {
iot_printf("e phase - %lu, end offset - %lu, q_bm %x\n",
cl->cmd[i].phase, cl->cmd[i].t_info.e.end_t,
cl->cmd[i].tx_q_en_bm);
}
}
iot_printf("------------------------------------------------------\n");
}
uint64_t mac_sched_get_saved_bp_start_ntb64(mac_vdev_t *vdev)
{
IOT_ASSERT(vdev);
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_list_t *cl = ctx->curr_hw_cmd_list;
IOT_ASSERT(cl);
uint64_t tmp = cl->start_ntb_h;
tmp <<= 32;
tmp |= cl->start_ntb;
return tmp;
}
#else /* PLC_SUPPORT_CCO_ROLE */
void mac_sched_nn_rx(mac_pdev_t *pdev, void *fc, uint32_t ntb, int8_t snr,
uint8_t is_rf, uint8_t band_id)
{
uint32_t proto = PHY_PROTO_TYPE_GET();
uint32_t nid = mac_get_nid_from_fc(proto, fc);
mac_vdev_t *vdev = pdev->vdev[0];
(void)ntb;
mac_sched_cco_snr_rx(vdev, nid, snr,
mac_get_nncco_self_ch_id(proto, fc),
mac_get_nncco_self_option(proto, fc), is_rf, band_id);
}
void mac_sched_nn_tx(mac_vdev_t *vdev)
{
(void)vdev;
}
#define mac_sched_cco_init(vdev)
#define mac_sched_cco_stop(vdev)
/* cco role device need to take care of both phase and band config */
uint64_t mac_sched_cco_set(mac_vdev_t *vdev, mac_bc_time_slot_t *ts,
uint64_t start_ntb64, uint64_t *next_start_ntb64)
{
(void)vdev;
(void)ts;
(void)next_start_ntb64;
IOT_ASSERT(0);
return start_ntb64;
}
void mac_sched_cco_bc_alert(mac_vdev_t *vdev)
{
(void)vdev;
/* for STA role only device, won't trigger here */
IOT_ASSERT(0);
}
void mac_sched_cco_set_nw_nego(mac_vdev_t *vdev, uint8_t enable)
{
(void)vdev;
(void)enable;
}
void mac_dump_sched_dbg()
{
return;
}
uint64_t mac_sched_get_saved_bp_start_ntb64(mac_vdev_t *vdev)
{
(void)vdev;
return 0;
}
#endif /* PLC_SUPPORT_CCO_ROLE */
/* sta device need to take care of band config */
void mac_sched_sta_set_htbus(mac_vdev_t *vdev, mac_bc_htbus_time_slot_t *ts,
uint32_t start_ntb)
{
tei_t dst_tei, dev_tei;
uint16_t cnt = 0;
uint8_t new_cmd_required = 0;
uint16_t start_offset = 0;
hw_sched_cmd_list_t *cl = NULL;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_t *cmd;
const uint8_t local_phase = vdev->l_phase1;
cl = mac_sched_alloc_cmd_list(vdev);
IOT_ASSERT(cl);
ctx->curr_hw_cmd_list = cl;
cl->start_ntb = start_ntb;
cmd = cl->cmd;
cmd->phase = local_phase;
cl->alloc_ntb = mac_sched_get_ntb(vdev);
cl->caller = 2;
/* prepare request command slot */
if (ts->cco_slot) {
start_offset += ts->cco_slot;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
dev_tei = vdev_get_tei(vdev);
if (!iot_bitmap_is_set(ts->rsp_bm, sizeof(ts->rsp_bm), dev_tei + 1)) {
start_offset += ts->sta_slot *
(uint16_t)iot_bitmap_cbs(ts->rsp_bm, sizeof(ts->rsp_bm));
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
} else {
dst_tei = (tei_t)iot_bitmap_ffs_and_c(ts->rsp_bm, sizeof(ts->rsp_bm));
while (dst_tei) {
dst_tei -= 1;
if (dst_tei == dev_tei) {
if (new_cmd_required) {
cnt++;
IOT_ASSERT(cnt < HW_SCHED_CMD_MAX_CNT);
cmd++;
cmd->phase = local_phase;
}
cmd->tx_q_en_bm = mac_sched_get_wb_bc_q(vdev, local_phase);
start_offset += ts->sta_slot;
cmd->t_info.e.end_t = start_offset;
cnt++;
IOT_ASSERT(cnt < HW_SCHED_CMD_MAX_CNT);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
} else {
start_offset += ts->sta_slot;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
dst_tei = (tei_t)iot_bitmap_ffs_and_c(
ts->rsp_bm, sizeof(ts->rsp_bm));
}
}
if (new_cmd_required) {
cnt++;
IOT_ASSERT(cnt < HW_SCHED_CMD_MAX_CNT);
}
cl->total_cnt = cnt;
mac_sched_dump(cl, cnt);
mac_sched_load_cmd_list(vdev, cl);
mac_sched_enable_bp(vdev, 1);
}
/* sta device need to take care of band config */
void mac_sched_sta_set(mac_vdev_t *vdev, mac_bc_time_slot_t *ts,
uint32_t start_ntb, uint8_t phase_a_tx, uint8_t phase_b_tx,
uint8_t phase_c_tx, uint8_t need_early_stop)
{
uint16_t i, cnt = 0;
uint8_t new_cmd_required = 0, allowed_tx_phase = 0;
uint16_t csma_slot_cnt, csma_frag, min_csma_frag, start_offset = 0;
hw_sched_cmd_list_t *cl = NULL;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_t *cmd, *prev_cmd;
const uint8_t local_phase = vdev->l_phase1;
uint32_t proto = PHY_PROTO_TYPE_GET();
tei_t self_tei = vdev_get_tei(vdev);
/* clear flag every time */
vdev->bcsma_slot_exist = 0;
/* judge dcsma is LID_BCSMA_FB_DETECT or not */
vdev->fb_lid_exist = (ts->d_csma_lid == LID_BCSMA_FB_DETECT);
if (phase_a_tx) {
allowed_tx_phase |= 1 << PLC_PHASE_A;
}
if (phase_b_tx) {
allowed_tx_phase |= 1 << PLC_PHASE_B;
}
if (phase_c_tx) {
allowed_tx_phase |= 1 << PLC_PHASE_C;
}
cl = mac_sched_alloc_cmd_list(vdev);
IOT_ASSERT(cl);
ctx->curr_hw_cmd_list = cl;
cl->start_ntb = start_ntb;
cmd = cl->cmd;
cmd->phase = local_phase;
//TODO: dbg for hw sched
cl->alloc_ntb = mac_sched_get_ntb(vdev);
cl->caller = 2;
/* prepare central beacon slot */
if (ts->bc_slot_dur) {
start_offset += ts->bc_slot_dur * ts->cco_bc_cnt;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
/* prepere proxy and discovery beacon slot */
if (ts->non_cco_bc_info.tei_valid && (self_tei != PLC_TEI_INVAL)) {
for (i = 0; i < ts->non_cco_bc_info.bc_cnt; i++) {
uint16_t tx_flag = ts->non_cco_bc_info.sta[i].tx_flag;
if (ts->non_cco_bc_info.sta[i].tei == self_tei &&
BEACON_TX_ONLY_RF != tx_flag) {
/* a new time slot required for local beacon tx */
if (new_cmd_required) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
}
cmd->tx_q_en_bm = mac_sched_get_wb_bc_q(vdev, local_phase);
start_offset += ts->bc_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
} else {
start_offset += ts->bc_slot_dur;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
}
} else if (ts->non_cco_bc_info.bc_cnt) {
start_offset += ts->bc_slot_dur * ts->non_cco_bc_info.bc_cnt;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
if (new_cmd_required) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
}
if (proto != PLC_PROTO_TYPE_SPG) {
/* prepare tdma slot. tdma is behind csma for SPG */
if (ts->tdma_slot_dur) {
cmd->phase = local_phase;
/* prepare tdma for cco device A, B, C phase */
if (ts->bc_slot_dur) {
start_offset += ts->tdma_slot_dur * ts->cco_bc_cnt;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
/* prepare tdma for pco and sta device */
for (i = 0; i < ts->non_cco_bc_info.bc_cnt; i++) {
if (ts->non_cco_bc_info.sta[i].tei == self_tei) {
/* a new time slot required for local beacon tx */
if (new_cmd_required) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
}
/* enalbe tx for the phase of local device, note that we always
* use phase a hwq for sta role device.
*/
cmd->tx_q_en_bm = mac_sched_get_tdma_q(vdev, PLC_PHASE_A);
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
} else {
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
}
}
if (new_cmd_required) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
}
}
/* prepare csma slot */
/* minimum time slot 10ms align */
min_csma_frag = (uint16_t)(iot_ceil(ts->bc_period / HW_SCHED_CMD_MAX_CNT,
10) * 10);
csma_frag = max(ts->csma_slot_frag_s, min_csma_frag);
csma_slot_cnt = mac_sched_split_csma(&ts->csma_info, csma_frag);
if (csma_slot_cnt) {
if (allowed_tx_phase & (1 << g_csma_slot[0].phase)) {
/* enable tx for the allowed phase, note that we always
* use phase a hwq for sta role device.
*/
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, PLC_PHASE_A);
}
cmd->phase = g_csma_slot[0].phase;
if (g_csma_slot[0].last) {
start_offset += g_slot_last[g_csma_slot[0].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < csma_slot_cnt; i++) {
if (g_csma_slot[i].phase != g_csma_slot[i - 1].phase) {
/* a new slot required for local device tx */
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = g_csma_slot[i].phase;
if (allowed_tx_phase & (1 << g_csma_slot[i].phase)) {
/* enable tx for the allowed phase. note that we
* always use phase a hwq for sta role device.
*/
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, PLC_PHASE_A);
}
}
if (g_csma_slot[i].last) {
start_offset += g_slot_last[g_csma_slot[i].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
}
if (cmd->tx_q_en_bm && MAC_SCHED_CSMA_GAP_MS) {
prev_cmd = cmd - 1;
if ((cmd->t_info.e.end_t - prev_cmd->t_info.e.end_t) >
MAC_SCHED_CSMA_GAP_MS) {
/* last slot has enough room to reserve the gap */
cmd->t_info.e.end_t -= MAC_SCHED_CSMA_GAP_MS;
prev_cmd = cmd;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->tx_q_en_bm = 0;
cmd->phase = prev_cmd->phase;
cmd->t_info.e.end_t = start_offset;
} else {
/* last sot has no enough room to reserve the gap, disable tx
* for the last slot.
*/
cmd->tx_q_en_bm = 0;
}
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
if (proto == PLC_PROTO_TYPE_SPG) {
/* prepare tdma slot. tdma is behind csma for SPG */
if (ts->tdma_slot_dur) {
cmd->phase = local_phase;
/* prepare tdma for cco device A, B, C phase */
if (ts->bc_slot_dur) {
start_offset += ts->tdma_slot_dur * ts->cco_bc_cnt;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
/* prepare tdma for pco and sta device */
for (i = 0; i < ts->non_cco_bc_info.bc_cnt; i++) {
if (ts->non_cco_bc_info.sta[i].tei == self_tei) {
/* a new time slot required for local beacon tx */
if (new_cmd_required) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
}
/* enalbe tx for the phase of local device, note that we
* always use phase a hwq for sta role device.
*/
cmd->tx_q_en_bm = mac_sched_get_tdma_q(vdev, PLC_PHASE_A);
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
} else {
start_offset += ts->tdma_slot_dur;
cmd->t_info.e.end_t = start_offset;
new_cmd_required = 1;
}
}
}
if (new_cmd_required) {
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = local_phase;
new_cmd_required = 0;
}
}
/* prepare dedicated csma slot */
csma_slot_cnt = mac_sched_split_csma(&ts->d_csma_info, csma_frag);
if (csma_slot_cnt) {
/* NOTE: if protocoal == SUPPORT_SOUTHERN_POWER_GRID, and dcsma slot.
* As long as there is a detection slot and
* lid == LID_BCSMA_START, sta allocates the slot,
* whether it is its own phase or not.
*/
if ((allowed_tx_phase & (1 << g_csma_slot[0].phase)) ||
(SUPPORT_SOUTHERN_POWER_GRID &&
(ts->d_csma_lid == LID_BCSMA_FB_DETECT))) {
/* enable tx for the allowed phase, note that we always
* use phase a hwq for sta role device.
*/
if (ts->d_csma_lid < LID_BCSMA_START) {
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, PLC_PHASE_A);
} else {
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev, PLC_PHASE_A);
vdev->bcsma_slot_exist = 1;
}
}
cmd->phase = g_csma_slot[0].phase;
if (g_csma_slot[0].last) {
start_offset += g_slot_last[g_csma_slot[0].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
for (i = 1; i < csma_slot_cnt; i++) {
if (g_csma_slot[i].phase != g_csma_slot[i - 1].phase) {
/* a new slot required for local device tx */
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
cmd->phase = g_csma_slot[i].phase;
if (allowed_tx_phase & (1 << g_csma_slot[i].phase)) {
/* enable tx for the allowed phase. note that we
* always use phase a hwq for sta role device.
*/
if (ts->d_csma_lid < LID_BCSMA_START) {
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
PLC_PHASE_A);
} else {
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev,
PLC_PHASE_A);
vdev->bcsma_slot_exist = 1;
}
}
}
if (g_csma_slot[i].last) {
start_offset += g_slot_last[g_csma_slot[i].phase - 1];
} else {
start_offset += csma_frag;
}
cmd->t_info.e.end_t = start_offset;
}
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
/* check cmd list */
if (start_offset != ts->bc_period) {
iot_printf("%s, cfg err, cfg ntb:%lu, bc pb:%lu\n",
__FUNCTION__, start_offset, ts->bc_period);
if (start_offset + MAC_SCHED_CSMA_GAP_MS < (uint16_t)ts->bc_period) {
start_offset = (uint16_t)ts->bc_period;
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev, PLC_PHASE_A);
cmd->phase = local_phase;
cmd->t_info.e.end_t = start_offset - MAC_SCHED_CSMA_GAP_MS;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
/* last cmd */
cmd->tx_q_en_bm = 0;
cmd->phase = local_phase;
cmd->t_info.e.end_t = start_offset;
cnt++;
if (cnt >= HW_SCHED_CMD_MAX_CNT)
IOT_ASSERT(0);
cmd++;
}
}
cl->total_cnt = cnt;
if (need_early_stop) {
cl->cmd[cl->total_cnt - 1].t_info.r.end_t -=
MAC_EARLY_STOP_CMDLIST_T_MS;
}
mac_sched_dump(cl, cnt);
mac_sched_load_cmd_list(vdev, cl);
mac_sched_enable_bp(vdev, 1);
}
void mac_sched_sta_bc_alert(mac_vdev_t *vdev)
{
uint64_t tmp;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
mac_rf_vdev_t *rf_vdev = get_rf_vdev_ptr(vdev->ref_pdev_id,
RF_PDEV_ID, vdev->rf_vdev_id);
mac_pdev_t *pdev;
/* load next portion of the current command list */
if (mac_sched_load_cmd_list(vdev, ctx->curr_hw_cmd_list)) {
/* near the end if current bp, let's try to reuse previous beacon
* period time slot to follow HW behavior if beacon period is
* available. otherwise, put device in RX only mode for next bp.
* sta role device will config the exact schedule for next bp after
* get the real beacon.
*/
//TODO: dbg for hw sched
#if (PLC_MAC_TX_DEBUG_LOG >= PLC_MAC_LOG_LEVEL_1)
iot_printf("mac hw sch list cnt = %d, ts:%d, reuse:%d\n",
mac_sched_get_cmd_list_cnt(vdev),
vdev->bcn_ctx.sta.allow_reuse_ts,
vdev->bcn_ctx.time_slot.allow_reuse);
#endif
uint32_t bp;
hw_sched_cmd_list_t *cl = ctx->curr_hw_cmd_list;
IOT_ASSERT(cl);
tmp = cl->start_ntb_h;
tmp <<= 32;
tmp |= cl->start_ntb;
if (cl->recursive) {
bp = cl->cmd[0].t_info.se.end_t;
} else {
bp = cl->cmd[cl->total_cnt - 1].t_info.r.end_t;
}
tmp += MAC_MS_TO_NTB(bp);
vdev->bcn_ctx.time_slot.bp_start_ntb = iot_uint64_lower32(tmp);
if (vdev->bcn_ctx.sta.allow_reuse_ts &&
vdev->bcn_ctx.time_slot.allow_reuse) {
/* flush tdma */
pdev = get_pdev_ptr(vdev->ref_pdev_id);
mac_tx_flush_all_tdma_queue(&pdev->hwq_hdl);
/* for time slot reused case, we only enable tx for local logical
* phase.
*/
switch (vdev->l_phase1) {
case PLC_PHASE_A:
vdev->bcn_ctx.sta.phase_a_tx = 1;
break;
case PLC_PHASE_B:
vdev->bcn_ctx.sta.phase_b_tx = 1;
break;
case PLC_PHASE_C:
vdev->bcn_ctx.sta.phase_c_tx = 1;
break;
default:
break;
}
mac_sched_sta_set(vdev, &vdev->bcn_ctx.time_slot,
vdev->bcn_ctx.time_slot.bp_start_ntb,
vdev->bcn_ctx.sta.phase_a_tx, vdev->bcn_ctx.sta.phase_b_tx,
vdev->bcn_ctx.sta.phase_c_tx, 0);
} else {
/* TODO: get real bcn period start ntb */
mac_sched_set_csma_only(vdev, tmp,
(uint8_t)vdev->bcn_ctx.time_slot.allow_reuse);
}
/* rf alert cmdlist application */
mac_rf_sched_sta_bc_alert(rf_vdev, &vdev->bcn_ctx.time_slot,
tmp,
vdev->bcn_ctx.sta.allow_reuse_ts,
vdev->bcn_ctx.time_slot.allow_reuse);
} else {
/* make sure HW scheduler started */
mac_sched_enable_bp(vdev, 1);
}
}
void mac_sched_stop(mac_vdev_t *vdev)
{
mac_sched_ctx_t *ctx = vdev->sched_ctx;
/* stop scheduler */
mac_sched_enable_bp(vdev, 0);
mac_pm_check_freq();
/* clean up command list */
mac_sched_free_cmd_list(vdev);
ctx->curr_hw_cmd_list = NULL;
/* clear pending isr */
mac_isr_clear(MAC_ISR_BC_ALERT_ID);
/* make sure there is no beacon period end ahead alert spur as
* we rely on it to switch status. after HW scheduler disabled
* and enabled again, it's possible there is a pending bp end
* ahead alert dsr in between.
*/
mac_dsr_clear(MAC_DSR_BC_ALERT_ID);
mac_sched_cco_stop(vdev);
}
/* set scheduler to work in csma only mode */
static void mac_sched_set_csma_only_intern(mac_vdev_t *vdev,
mac_bc_cmsa_si_t *csma, uint32_t sched_cnt, uint64_t start_ntb,
uint8_t enable_tx)
{
uint8_t i;
uint32_t tmp;
uint16_t start_offset, cnt;
hw_sched_cmd_list_t *cl = NULL;
mac_sched_ctx_t *ctx = vdev->sched_ctx;
hw_sched_cmd_t *cmd;
/* clear flag every time */
vdev->bcsma_slot_exist = 0;
/* rx only, LID_BCSMA_FB_DETECT is not exist */
vdev->fb_lid_exist = 0;
cl = mac_sched_alloc_cmd_list(vdev);
ctx->curr_hw_cmd_list = cl;
IOT_ASSERT(cl);
IOT_ASSERT(csma->phase_cnt);
IOT_ASSERT(sched_cnt);
cl->start_ntb = iot_uint64_lower32(start_ntb);
cl->start_ntb_h = iot_uint64_higher32(start_ntb);
cmd = cl->cmd;
cnt = 0;
start_offset = 0;
//TODO: dbg for hw sched
cl->alloc_ntb = mac_sched_get_ntb(vdev);
cl->caller = 3;
if (sched_cnt > 1) {
/* create recursive start command */
cmd->t_info.se.s_flag = 1;
cmd->t_info.se.r_flag = 1;
cmd->t_info.se.start_t = 0;
cmd++;
cnt++;
/* mark recursive first */
cmd->t_info.r.rf_flag = 1;
for (i = 0; i < csma->phase_cnt; i++) {
cmd->phase = csma->phase[i];
if (enable_tx) {
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
(uint8_t)cmd->phase);
}
#if PLC_SUPPORT_DBG_PKT_MODE
/* if and only if it's STA/PCO dev, and not blocking dbg pkt sch
* in rx only period
*/
else if (!vdev_get_block_dbg_pkt_4_rx_only(vdev) && \
((PLC_DBG_PKT_MODE_DEF_PHASE == PLC_PHASE_ALL) \
|| (cmd->phase == PLC_DBG_PKT_MODE_DEF_PHASE))) {
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev,
(uint8_t)cmd->phase);
vdev->bcsma_slot_exist = 1;
}
#endif
cmd->t_info.r.r_flag = 1;
start_offset += (uint16_t)csma->slot_dur[i];
/* recursive mode end field means duration,
* it's shared with offset when non-recursive mode
*/
cmd->t_info.r.end_t = csma->slot_dur[i];
cmd++;
cnt++;
}
/* mark recursive end */
cmd--;
cmd->t_info.r.re_flag = 1;
/* fill the end time of the recursive start command */
tmp = start_offset * sched_cnt;
/* make sure no wrap around */
IOT_ASSERT(tmp <= 0x7FFF);
start_offset = (uint16_t)tmp;
cl->cmd->t_info.se.end_t = start_offset;
cl->recursive = 1;
} else {
for (i = 0; i < csma->phase_cnt; i++) {
cmd->phase = csma->phase[i];
if (enable_tx) {
cmd->tx_q_en_bm = mac_sched_get_csma_q(vdev,
(uint8_t)cmd->phase);
}
#if PLC_SUPPORT_DBG_PKT_MODE
/* if and only if it's STA/PCO dev, and not blocking dbg pkt sch
* in rx only period
*/
else if (!vdev_get_block_dbg_pkt_4_rx_only(vdev) && \
((PLC_DBG_PKT_MODE_DEF_PHASE == PLC_PHASE_ALL) \
|| (cmd->phase == PLC_DBG_PKT_MODE_DEF_PHASE))) {
cmd->tx_q_en_bm = mac_sched_get_dcsma_q(vdev,
(uint8_t)cmd->phase);
vdev->bcsma_slot_exist = 1;
}
#endif
start_offset += (uint16_t)csma->slot_dur[i];
cmd->t_info.e.end_t = start_offset;
cmd++;
cnt++;
}
}
mac_sched_dump(cl, cnt);
/* for csma only mode, make sure cmd list is short enough */
IOT_ASSERT(cnt < HW_SHCED_CMD_DEPTH);
cl->total_cnt = cnt;
/* load command list of next beacon period */
mac_sched_load_cmd_list(vdev, cl);
/* start HW scheduler */
mac_sched_enable_bp(vdev, 1);
}
void mac_sched_set_csma_only(mac_vdev_t *vdev, uint64_t start_ntb,
uint8_t enable_tx)
{
mac_bc_cmsa_si_t csma;
uint32_t sched_cnt;
/* config HW scheduler to RX only mode */
if (mac_vdev_cfg_get_node_role(vdev) == PLC_DEV_ROLE_CCO &&
vdev->mac_vdev_cfg.p_phase_cnt > 1) {
/* set cco device to work in supported phases evenly */
csma.phase_cnt = 0;
if (vdev->l_phase1) {
csma.phase[csma.phase_cnt] = vdev->l_phase1;
csma.slot_dur[csma.phase_cnt] = MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
csma.phase_cnt++;
}
if (vdev->l_phase2) {
csma.phase[csma.phase_cnt] = vdev->l_phase2;
csma.slot_dur[csma.phase_cnt] = MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
csma.phase_cnt++;
}
if (vdev->l_phase3) {
csma.phase[csma.phase_cnt] = vdev->l_phase3;
csma.slot_dur[csma.phase_cnt] = MAC_SCHED_CCO_RX_ONLY_SLOT_DUR;
csma.phase_cnt++;
}
/* switch phase every 100 ms and repeat the whole session 10 times.
* totally phase_cnt * 100 * 10 = MAC_CSMA_ONLY_PEIROD_MS.
*/
sched_cnt = 10;
} else {
/* for one phase cco device and sta device */
csma.phase_cnt = 1;
if (vdev->l_phase1) {
csma.phase[0] = vdev->l_phase1;
} else {
csma.phase[0] = PLC_PHASE_A;
}
csma.slot_dur[0] = MAC_CSMA_ONLY_PEIROD_MS;
/* no need to switch the phase and no repeat required */
sched_cnt = 1;
}
mac_sched_set_csma_only_intern(vdev, &csma, sched_cnt, start_ntb,
enable_tx);
}
void mac_sched_init(mac_vdev_t *vdev)
{
vdev->sched_ctx = os_mem_malloc(PLC_MAC_SCHED_MID, sizeof(mac_sched_ctx_t));
IOT_ASSERT(vdev->sched_ctx);
mac_sched_cco_init(vdev);
}