/*****************************************************************************/ /** * @file link_layer.c * @author FanHao (hao_fan_bit_ee@163.com) * @brief DF Linklayer implementation file. * @version 0.1 * @date 2023-05-06 * * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n * Lab of Communication and Network * ******************************************************************************/ /** * @addtogroup LinkLayer * @{ */ /***************************** Include Files *********************************/ #include #include #include "link_layer.h" #include "link_layer_types.h" #include "node_list.h" #include "msg_def.h" #include "error.h" #include "common.h" #include "util.h" #include "circ_queue.h" #include "../time_ctrl/time_ctrl.h" #ifndef EMULATION #include "../config/app_config.h" #endif /************************** Constant Definitions *****************************/ #define NET_START_TIME_INFTY UINT32_MAX #define LINK_BROCK_THRESHOLD L2_SLOTS_PER_FRAME // 0.5 seconds /************************** Variable Definitions *****************************/ /***************** Macros (Inline Functions) Definitions *********************/ /**************************** Type Definitions *******************************/ /************************** Function Prototypes ******************************/ static void L2_RcdShift(L2_RCD *rcd, uint32_t netxVal); static void UpdateMainSate(L2_INST *instPtr, L2_NODE_STATE newSate); static void L2_SetSlotNum(L2_INST *instPtr, uint32_t newSlotNum); static void L2_SetFrameNum(L2_INST *instPtr, uint32_t newFrameNum); static void L2_UpdateSlotNum(L2_INST *instPtr); static void L2_DataAvailUpdate(L2_INST *instPtr, bool status); static void L2_FrameStateUpdate(L2_INST *instPtr); /** * @brief Shift L2 status record.(newval->next->curr->last) */ inline void L2_RcdShift(L2_RCD *rcd, uint32_t netxVal) { ASSERT(rcd != NULL); rcd->last = rcd->curr; rcd->curr = rcd->next; rcd->next = netxVal; } /** * @brief helper function to update linklayer state */ static inline void UpdateMainSate(L2_INST *instPtr, L2_NODE_STATE newSate) { ASSERT(instPtr != NULL); DBG_INFO("MainStateUpdate: %" PRId8 " -> %" PRId8 "\n", instPtr->mainState, newSate); instPtr->nextState = newSate; } /** * @brief helper function to set slot number. */ static void L2_SetSlotNum(L2_INST *instPtr, uint32_t newSlotNum) { ASSERT(instPtr != NULL); DBG_INFO("Set new slot number to %" PRIu32 "\n", newSlotNum); instPtr->slotNum.next = newSlotNum; } /** * @brief helper function to set frame number */ static void L2_SetFrameNum(L2_INST *instPtr, uint32_t newFrameNum) { ASSERT(instPtr != NULL); DBG_INFO("Set new frame number to %" PRIu32 "\n", newFrameNum); instPtr->frameNum.next = newFrameNum; } /** * @brief Update frame number */ static inline void L2_UpdateSlotNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); uint32_t slotCnt; // Shift record L2_RcdShift(&(instPtr->frameNum), instPtr->frameNum.next); L2_RcdShift(&(instPtr->slotNum), instPtr->slotNum.next); // FIXME: Consider time wrapping // Compute and set new slot and frame num slotCnt = TimeCtrl_GetIntCnt(instPtr->config.tmcInstPtr) + 1; instPtr->frameNum.next = slotCnt / L2_SLOTS_PER_FRAME - instPtr->netStartTime; instPtr->slotNum.next = slotCnt % L2_SLOTS_PER_FRAME; } /** * @brief Update l2 frame buffer state * infer that if there is a new valid l2 frame up. */ static inline void L2_DataAvailUpdate(L2_INST *instPtr, bool status) { ASSERT(instPtr != NULL); instPtr->dataAvail = status; } /*****************************************************************************/ /** * @brief Update L2 frame state, check if there is any valid L2 frame data in * data buffer, update receive frame status. * * @param instPtr a pointer to linklayer instance * * @return NONE. * ******************************************************************************/ static void L2_FrameStateUpdate(L2_INST *instPtr) { ASSERT(instPtr != NULL); PHY_INST *phyRef = instPtr->config.phyInstPtr; bool isValid; isValid = false; // Update frame state. if (Phy_IsFrameValid(phyRef) == false) { L2_DataAvailUpdate(instPtr, false); } else { DBG_INFO("A frame received, checking data validation...\n"); DBG_INFO("Last frame type is: %" PRId32 ".\n", instPtr->frame.last); Phy_RetrieveRecvData(phyRef, instPtr->recvPktData); instPtr->log.frameRecv++; L2_MSG_COMMON *msg = (L2_MSG_COMMON *) instPtr->recvPktData; L2_FRAME_TYPE recvType = msg->msgType; // Update frame state. switch (recvType) { case L2_FRAME_ADV: isValid = L2_CrcCheck(instPtr->recvPktData, L2_GetFrameLen(L2_FRAME_ADV, L2_SVC_NONE), L2_CRC8_31); instPtr->log.advRecvCnt++; instPtr->log.advCorrectCnt = isValid ? instPtr->log.advCorrectCnt + 1 : instPtr->log.advCorrectCnt; break; case L2_FRAME_ACK: isValid = L2_CrcCheck(instPtr->recvPktData, L2_GetFrameLen(L2_FRAME_ACK, L2_SVC_NONE), L2_CRC8_31); instPtr->log.ackRecvCnt++; instPtr->log.ackCorrectCnt = isValid ? instPtr->log.ackCorrectCnt + 1 : instPtr->log.ackCorrectCnt; break; case L2_FRAME_DIS: isValid = L2_CrcCheck(instPtr->recvPktData, L2_GetFrameLen(L2_FRAME_DIS, L2_SVC_NONE), L2_CRC8_31); instPtr->log.disRecvCnt++; instPtr->log.disCorrectCnt = isValid ? instPtr->log.disCorrectCnt + 1 : instPtr->log.disCorrectCnt; break; case L2_FRAME_MNG: isValid = L2_CrcCheck(instPtr->recvPktData, L2_GetFrameLen(L2_FRAME_MNG, L2_SVC_NONE), L2_CRC8_31); instPtr->log.mngRecvCnt++; instPtr->log.mngCorrectCnt = isValid ? instPtr->log.mngCorrectCnt + 1 : instPtr->log.mngCorrectCnt; break; case L2_FRAME_RTT: isValid = L2_CrcCheck(instPtr->recvPktData, L2_GetFrameLen(L2_FRAME_RTT, L2_SVC_NONE), L2_CRC8_31); instPtr->log.rttRecvCnt++; instPtr->log.rttCorrectCnt = isValid ? instPtr->log.rttCorrectCnt + 1 : instPtr->log.rttCorrectCnt; break; case L2_FRAME_SVC: #ifndef DATALINK_ENABLE_BERTEST isValid = L2_CrcCheck(instPtr->recvPktData, L2_GetFrameLen(L2_FRAME_SVC, instPtr->config.serviceRate), L2_CRC8_31); #else isValid = true; #endif instPtr->log.svcRecvCnt++; instPtr->log.svcCorrectCnt = isValid ? instPtr->log.svcCorrectCnt + 1 : instPtr->log.svcCorrectCnt; break; default: isValid = false; break; } if (isValid) { instPtr->log.frameCorrect++; L2_DataAvailUpdate(instPtr, true); } else { L2_DataAvailUpdate(instPtr, false); } } } /** * @addtogroup LinkLayer_Utility * @{ */ /** * @brief Get the current node ID. */ inline uint8_t L2_GetNodeId(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->myInfo.nodeId; } /** * @brief Get the current cluster ID. */ inline uint8_t L2_GetClusterId(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->myInfo.clustId; } /** * @brief Get the head node ID. */ uint8_t L2_GetHeadId(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->myInfo.headId; } /** * @brief Get the current node type. */ inline L2_NODE_TYPE L2_GetNodeType(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->myInfo.nodeType; } /** * @brief Get the current linklayer state. */ inline L2_NODE_STATE L2_GetMainState(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->mainState; } /** * @brief Check if there is a valid l2 frame. */ inline bool L2_isStarted(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->isEnable; } /** * @brief Check if there is a valid l2 frame. */ inline bool L2_isDataAvail(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->dataAvail; } /** * @brief Check if node is connected to the network */ inline bool L2_isCxn2Net(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->cxn2Net; } /** * @brief Get corresponding frame length in byte of L2 frame. */ inline uint32_t L2_GetFrameLen(L2_FRAME_TYPE l2FrameType, L2_SVC_RATE l2SvcRate) { return Phy_GetFrameLen(L2_L2frame2L1frame(l2FrameType, l2SvcRate)); } /** * @brief Get last frame number. */ inline uint32_t L2_GetLastFrameNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->frameNum.last; } /** * @brief Get last slot number. */ inline uint32_t L2_GetLastSlotNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->slotNum.last; } /** * @brief Get the frame type of last slot. */ inline L2_FRAME_TYPE L2_GetLastFrameType(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->frame.last; } /** * @brief Get the tx/rx state of last slot. */ inline L2_TOR_TYPE L2_GetLastTor(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->tor.last; } /** * @brief Get current frame number. */ inline uint32_t L2_GetCurrentFrameNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->frameNum.curr; } /** * @brief Get current slot number. */ inline uint32_t L2_GetCurrentSlotNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->slotNum.curr; } /** * @brief Get the frame type of current slot. */ inline L2_FRAME_TYPE L2_GetCurrentFrameType(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->frame.curr; } /** * @brief Get the tx/rx state of current slot. */ inline L2_TOR_TYPE L2_GetCurrentTor(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->tor.curr; } /** * @brief Get next frame number. */ inline uint32_t L2_GetNextFrameNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->frameNum.next; } /** * @brief Get next slot number. */ inline uint32_t L2_GetNextSlotNum(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->slotNum.next; } /** * @brief Get the frame type of next slot. */ inline L2_FRAME_TYPE L2_GetNextFrameType(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->frame.next; } /** * @brief Get the tx/rx state of next slot. */ inline L2_TOR_TYPE L2_GetNextTor(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->tor.next; } /** * @brief Get service rate of the node. */ inline L2_SVC_RATE L2_GetServiceRate(L2_INST *instPtr) { ASSERT(instPtr != NULL); return instPtr->config.serviceRate; } /*****************************************************************************/ /** * @brief Convert L2 frame type to L1 frame type * * @param l2Frame * @param l2SvcRate * * @return L1 frame type. * ******************************************************************************/ L1_FRAME_TYPE L2_L2frame2L1frame(L2_FRAME_TYPE l2Frame, L2_SVC_RATE l2SvcRate) { switch (l2Frame) { case L2_FRAME_ADV: return L1_FRAME_ADV_24MS; case L2_FRAME_ACK: return L1_FRAME_ACK_8MS; case L2_FRAME_DIS: case L2_FRAME_MNG: case L2_FRAME_RTT: return L1_FRAME_128KBPS_4MS; case L2_FRAME_SVC: switch (l2SvcRate) { case L2_SVC_128K: return L1_FRAME_128KBPS_4MS; case L2_SVC_512K: return L1_FRAME_512KBPS_4MS; case L2_SVC_2M: return L1_FRAME_2MBPS_4MS; default: return L1_FRAME_NONE; } default: return L1_FRAME_NONE; } } /*****************************************************************************/ /** * @brief Get L2 service packet length based on service rate * * @param l2SvcRate * * @return Packet length. * ******************************************************************************/ size_t L2_GetSvcPacketLen(L2_SVC_RATE l2SvcRate) { switch (l2SvcRate) { case L2_SVC_128K: return sizeof(L2_MSG_SVC_128K); case L2_SVC_512K: return sizeof(L2_MSG_SVC_512K); case L2_SVC_2M: return sizeof(L2_MSG_SVC_2M); default: DBG_ERROR("Unsupported service rate: %" PRId8 ".\n", l2SvcRate); return EPARAM; } } /*****************************************************************************/ /** * @brief Get L2 service payload length based on service rate * * @param l2SvcRate * * @return Payload length. * ******************************************************************************/ size_t L2_GetSvcPayloadLen(L2_SVC_RATE l2SvcRate) { switch (l2SvcRate) { case L2_SVC_128K: return L2_PAYLOAD_LEN_SVC_128K; case L2_SVC_512K: return L2_PAYLOAD_LEN_SVC_512K; case L2_SVC_2M: return L2_PAYLOAD_LEN_SVC_2M; default: DBG_ERROR("Unsupported service rate: %" PRId8 ".\n", l2SvcRate); return EPARAM; } } /*****************************************************************************/ /** * @brief Convert antenna broadcast id to antenna index * * @param antId * * @return Physical antenna index. * ******************************************************************************/ PHY_ANT_INDEX L2_AntId2AntIndex(uint8_t antId) { switch (antId) { case L2_BROADCAST_ID1: return PHY_ANT_INDEX_1; case L2_BROADCAST_ID2: return PHY_ANT_INDEX_2; case L2_BROADCAST_ID3: return PHY_ANT_INDEX_3; default: return PHY_ANT_INDEX_NONE; } } /** @} LinkLayer_Utility*/ /** * @defgroup LinkLayer_Main * @{ */ /*****************************************************************************/ /** * @brief Initialize link layer fsm instance, allocate memory to storage fsm * data. Apply the default values to fsm. * * @param instPtr a pointer to physical layer controller instance * @param config a pointer to configurations * * @return SUCCESS * Other error code. * ******************************************************************************/ int32_t Linklayer_Init(L2_INST **instPtr, const L2_CONFIG *config) { ASSERT(instPtr != NULL); ASSERT(config != NULL); int32_t Status; if (config->phyInstPtr == NULL) { return EDINVAL; } if (config->tmcInstPtr == NULL) { return EDINVAL; } (*instPtr) = (L2_INST *)malloc(sizeof(L2_INST)); if (*instPtr == NULL) { DBG_ERROR("Allocate memory for L2_InstPtr failed.\n"); return ENOMEM; } // Clear random content memset(*instPtr, 0x00, sizeof(L2_INST)); (*instPtr)->sendPktData = NULL; (*instPtr)->recvPktData = NULL; (*instPtr)->schdEventPool = NULL; (*instPtr)->nodeList = NULL; (*instPtr)->scanFSM = NULL; (*instPtr)->serviceFSM = NULL; memcpy(&((*instPtr)->config), config, sizeof(L2_CONFIG)); // Packet buffer (*instPtr)->sendPktData = calloc(L2_PACK_LEN_MAX / sizeof(uint64_t), sizeof(uint64_t)); if ((*instPtr)->sendPktData == NULL) { DBG_ERROR("Allocate memory for sendPktData failed.\n"); Linklayer_Destroy(*instPtr); return ENOMEM; } (*instPtr)->recvPktData = calloc(L2_PACK_LEN_MAX / sizeof(uint64_t), sizeof(uint64_t)); if ((*instPtr)->recvPktData == NULL) { DBG_ERROR("Allocate memory for recvPktData failed.\n"); Linklayer_Destroy(*instPtr); return ENOMEM; } // Reset basic info (*instPtr)->netStartTime = NET_START_TIME_INFTY; (*instPtr)->rtneConfig.last = NULL; (*instPtr)->rtneConfig.curr = NULL; (*instPtr)->rtneConfig.next = NULL; (*instPtr)->isEnable = false; (*instPtr)->cxn2Net = (config->myType == L2_NODE_HEAD) ? true : false; (*instPtr)->dataAvail = false; (*instPtr)->frameNum.last = 0; (*instPtr)->frameNum.curr = 0; (*instPtr)->frameNum.next = 0; (*instPtr)->slotNum.last = 0; (*instPtr)->slotNum.curr = 0; (*instPtr)->slotNum.next = 0; (*instPtr)->frame.last = L2_FRAME_NONE; (*instPtr)->frame.curr = L2_FRAME_NONE; (*instPtr)->frame.next = L2_FRAME_NONE; (*instPtr)->tor.last = L2_TOR_SUSPEND; (*instPtr)->tor.curr = L2_TOR_SUSPEND; (*instPtr)->tor.next = L2_TOR_SUSPEND; (*instPtr)->rtneConfig.rtneTable = initTable; (*instPtr)->rtneConfig.rtnePrd = ARRAY_SIZE(initTable); (*instPtr)->mainState = L2_INIT_STATE; (*instPtr)->nextState = L2_INIT_STATE; (*instPtr)->myInfo.nodeId = config->myId; (*instPtr)->myInfo.clustId = config->clustId; (*instPtr)->myInfo.headId = config->headId; (*instPtr)->myInfo.nodeType = config->myType; (*instPtr)->myInfo.pos.x = 0; (*instPtr)->myInfo.pos.y = 0; (*instPtr)->myInfo.pos.z = 0; (*instPtr)->myInfo.speed.x = 0; (*instPtr)->myInfo.speed.y = 0; (*instPtr)->myInfo.speed.z = 0; (*instPtr)->schdEventPool = NULL; (*instPtr)->nodeList = NULL; Status = L2_CreateSchedEventPool(&((*instPtr)->schdEventPool)); if (Status != SUCCESS) { DBG_ERROR("L2_CreateSchedEventPool runs failed, with error code: %" PRId32 ".\n", Status); Linklayer_Destroy(*instPtr); return Status; } Status = L2_NodeList_Init(&((*instPtr)->nodeList)); if (Status != SUCCESS) { DBG_ERROR("NodeList_Init runs failed, with error code: %" PRId32 ".\n", Status); Linklayer_Destroy(*instPtr); return Status; } Status = L2_Scan_Init(&((*instPtr)->scanFSM)); if (Status != SUCCESS) { Linklayer_Destroy(*instPtr); return Status; } Status = L2_Service_Init(&((*instPtr)->serviceFSM)); if (Status != SUCCESS) { Linklayer_Destroy(*instPtr); return Status; } return SUCCESS; } /*****************************************************************************/ /** * @brief Reconfigure link layer instance, reset all segment and reconfigure * accordingly. * * @param instPtr a pointer to physical layer controller instance * @param config a pointer to configurations * * @return SUCCESS * Other error code. * ******************************************************************************/ int32_t Linklayer_Reconfig(L2_INST *instPtr, const L2_CONFIG *config) { ASSERT(instPtr != NULL); ASSERT(config != NULL); if (config->phyInstPtr == NULL) { return EDINVAL; } if (config->tmcInstPtr == NULL) { return EDINVAL; } memcpy(&(instPtr->config), config, sizeof(L2_CONFIG)); // Reset basic info memset(&(instPtr->log), 0x00, sizeof(L2_LOG)); instPtr->netStartTime = UINT32_MAX; instPtr->rtneConfig.last = NULL; instPtr->rtneConfig.curr = NULL; instPtr->rtneConfig.next = NULL; instPtr->cxn2Net = (config->myType == L2_NODE_HEAD) ? true : false; instPtr->dataAvail = false; instPtr->frameNum.last = 0; instPtr->frameNum.curr = 0; instPtr->frameNum.next = 0; instPtr->slotNum.last = 0; instPtr->slotNum.curr = 0; instPtr->slotNum.next = 0; instPtr->frame.last = L2_FRAME_NONE; instPtr->frame.curr = L2_FRAME_NONE; instPtr->frame.next = L2_FRAME_NONE; instPtr->tor.last = L2_TOR_SUSPEND; instPtr->tor.curr = L2_TOR_SUSPEND; instPtr->tor.next = L2_TOR_SUSPEND; instPtr->rtneConfig.rtneTable = initTable; instPtr->rtneConfig.rtnePrd = ARRAY_SIZE(initTable); instPtr->mainState = L2_INIT_STATE; instPtr->nextState = L2_INIT_STATE; instPtr->myInfo.nodeId = config->myId; instPtr->myInfo.clustId = config->clustId; instPtr->myInfo.headId = config->headId; instPtr->myInfo.nodeType = config->myType; instPtr->myInfo.pos.x = 0; instPtr->myInfo.pos.y = 0; instPtr->myInfo.pos.z = 0; instPtr->myInfo.speed.x = 0; instPtr->myInfo.speed.y = 0; instPtr->myInfo.speed.z = 0; // Reset according instance L2_ClearSchedEventPool(instPtr->schdEventPool); L2_NodeList_Clear(instPtr->nodeList); L2_Scan_Reset(instPtr->scanFSM); L2_Service_Reset(instPtr->serviceFSM); return SUCCESS; } /*****************************************************************************/ /** * @brief Completely destroy link layer instance, free all allocated memory. * * @param instPtr a pointer to physical layer controller instance * * @return NONE * ******************************************************************************/ void Linklayer_Destroy(L2_INST *instPtr) { if (instPtr == NULL) { return; } free(instPtr->sendPktData); free(instPtr->recvPktData); // Destroy FSMs and nodeList L2_DestroySchedEventPool(instPtr->schdEventPool); L2_NodeList_Destroy(instPtr->nodeList); L2_Scan_Destroy(instPtr->scanFSM); L2_Service_Destroy(instPtr->serviceFSM); free(instPtr); } /*****************************************************************************/ /** * @brief Start link layer processing. * * @param instPtr a pointer to physical layer controller instance * * @return NONE * ******************************************************************************/ int32_t Linklayer_Start(L2_INST *instPtr) { ASSERT(instPtr != NULL); // Set netStartTime at first call, i.e. at the beginning of next time unit. if (instPtr->netStartTime == NET_START_TIME_INFTY) { instPtr->netStartTime = TimeCtrl_GetIntCnt(instPtr->config.tmcInstPtr) / L2_SLOTS_PER_FRAME + 1; } instPtr->isEnable = true; return SUCCESS; } /*****************************************************************************/ /** * @brief Halt link layer. * * @param instPtr a pointer to physical layer controller instance * * @return NONE * ******************************************************************************/ int32_t Linklayer_Halt(L2_INST *instPtr) { ASSERT(instPtr != NULL); instPtr->isEnable = false; return SUCCESS; } /*****************************************************************************/ /** * @brief Handle l2 event called by other model. * * @param instPtr a pointer to physical layer controller instance * @param l2Event * * @return SUCCESS - Event has been successfully handled. * FAIL - Event cannot be recognized or error occurred while process. * ******************************************************************************/ int32_t Linklayer_EventHandler(L2_INST *instPtr, const L2_EVENT *l2Event) { switch (l2Event->event) { case L2_EV_SET_FRAME_NUM: { DBG_INFO("Event L2_EV_FRAME_NO_UPDATE asserted....\n"); L2_SetFrameNum(instPtr, *((uint32_t *)l2Event->msg)); break; } case L2_EV_SET_SLOT_NUM: { DBG_INFO("Event L2_EV_SLOT_NO_UPDATE asserted....\n"); L2_SetSlotNum(instPtr, *((uint32_t *)l2Event->msg)); break; } case L2_EV_MAIN_STATE_UPDATE: { DBG_INFO("Event L2_EV_MAIN_STATE_UPDATE asserted....\n"); UpdateMainSate(instPtr, *((L2_NODE_STATE *)l2Event->msg)); break; } case L2_EV_SCAN_PROC_DONE: { DBG_INFO("Event L2_EV_SCAN_PROC_DONE asserted....\n"); break; } case L2_EV_NET_ADV_UPDATE: { DBG_INFO("Event L2_EV_NET_ADV_UPDATE asserted....\n"); if (L2_GetNodeType(instPtr) != L2_NODE_HEAD) { instPtr->netStartTime = *((uint32_t *)l2Event->msg); } else { DBG_WARNING("Invalid call from head node."); } break; } case L2_EV_NET_SETUP_DONE: { DBG_INFO("Event L2_EV_NET_SETUP_DONE asserted....\n"); if (L2_GetNodeType(instPtr) != L2_NODE_HEAD) { instPtr->cxn2Net = true; UpdateMainSate(instPtr, L2_NORMAL_STATE); } break; } case L2_EV_TIME_SYNC: { DBG_INFO("Event L2_EV_TIME_SYNC asserted....\n"); break; } case L2_EV_LOST_CXN: { DBG_INFO("Event L2_EV_LOST_CXN asserted....\n"); if (L2_GetNodeType(instPtr) == L2_NODE_HEAD) { DBG_ERROR("Illegal event, only None_Head node can assert L2_EV_LOST_CXN.\n"); break; } DBG_WARNING("Lost connection with head node [%" PRId8 "].\n", L2_GetHeadId(instPtr)); L2_Scan_Reset(instPtr->scanFSM); L2_Service_Reset(instPtr->serviceFSM); instPtr->cxn2Net = false; UpdateMainSate(instPtr, L2_INIT_STATE); instPtr->log.reconnectCnt++; break; } case L2_EV_LOST_NODE: { DBG_INFO("Event L2_EV_LOST_NODE asserted....\n"); L2_Scan_Reset(instPtr->scanFSM); L2_Service_Reset(instPtr->serviceFSM); UpdateMainSate(instPtr, L2_INIT_STATE); // Log reconnect instPtr->log.reconnectCnt++; break; } case L2_ROUTINE_FINISH: { DBG_INFO("Event L2_ROUTINE_FINISH asserted....\n"); if (instPtr->nextState == L2_INIT_STATE) { // Reset scan net FMS for next scan process. L2_Scan_Reset(instPtr->scanFSM); UpdateMainSate(instPtr, L2_INIT_STATE); instPtr->rtneConfig.rtneTable = initTable; instPtr->rtneConfig.rtnePrd = ARRAY_SIZE(initTable); } else if (instPtr->nextState == L2_NORMAL_STATE) { UpdateMainSate(instPtr, L2_NORMAL_STATE); instPtr->rtneConfig.rtneTable = normalTable[L2_DEFAULT_NORMAL_TABLE]; instPtr->rtneConfig.rtnePrd = ARRAY_SIZE(normalTable[L2_DEFAULT_NORMAL_TABLE]); } else { DBG_WARNING("Unsupported routine state [%" PRId8 "], " "Continue with previous routine table.\n", instPtr->nextState); } break; } case L2_ROUTINE_UPDATE: { DBG_INFO("Event L2_ROUTINE_UPDATE asserted....\n"); L2_RTNE_CONFIG *newRtneConfig = (L2_RTNE_CONFIG *)l2Event->msg; instPtr->rtneConfig.rtneTable = newRtneConfig->rtneTable; instPtr->rtneConfig.rtnePrd = newRtneConfig->rtnePrd; break; } default: { DBG_WARNING("UNKNOWN Event %" PRId8 " asserted.\n", l2Event->event); break; } } return SUCCESS; } /*****************************************************************************/ /** * @brief Handle l2 packet correctly received. * * @param instPtr a pointer to physical layer controller instance * * @return Status code. * ******************************************************************************/ int32_t Linklayer_PktHandler(L2_INST *instPtr) { ASSERT(instPtr != NULL); int32_t Status; if (instPtr->dataAvail == false) { return FAILURE; } L2_MSG_COMMON *msg = (L2_MSG_COMMON *) instPtr->recvPktData; L2_FRAME_TYPE frameType = msg->msgType; switch (frameType) { case L2_FRAME_ADV: case L2_FRAME_ACK: Status = L2_Scan_PktHandler(instPtr, instPtr->recvPktData); break; case L2_FRAME_DIS: case L2_FRAME_MNG: case L2_FRAME_RTT: case L2_FRAME_SVC: Status = L2_Service_PktHandler(instPtr, instPtr->recvPktData); break; default: DBG_WARNING("L2FrameHandler: UNKNOWN L2-FrameType %" PRId8 "\n", frameType); Status = FAILURE; break; } return Status; } /** * @brief Utility function to update node ttl. */ static int32_t IntcNodeTTL(NODE_ENTRY *node, const void *param) { ASSERT(node != NULL); node->ttlCnt += *((uint32_t *)param); return SUCCESS; } /** * @brief Utility function to check connection to node is still valid. */ #ifdef DATALINK_ENABLE_AUTORECONNECT static bool IsNodeConnected(NODE_ENTRY *node) { ASSERT(node != NULL); DBG_INFO("Node: %" PRIu8 ", TTL: %" PRIu32 ".\n", node->nodeInfo.nodeId, node->ttlCnt); if (node->ttlCnt < LINK_BROCK_THRESHOLD) { return true; } else { DBG_WARNING("Lost Connection to NODE %" PRIu8 ".\n", node->nodeInfo.nodeId); return false; } } #endif /*****************************************************************************/ /** * @brief Schedule next slot based on time allocate slice. * * @param instPtr a pointer to physical layer controller instance * * @return SUCCESS * Other error code. * ******************************************************************************/ static int32_t Linklayer_Routine(L2_INST *instPtr) { ASSERT(instPtr != NULL); int32_t Status; uint32_t tableIndx, entryIndx; // Determine which routine entry is going to be performed. tableIndx = L2_GetNextFrameNum(instPtr) % instPtr->rtneConfig.rtnePrd; entryIndx = L2_GetNextSlotNum(instPtr); L2_RTNE_TABLE_ENTRY rtneEntry = {0}; // Shift routine entry record. instPtr->rtneConfig.last = instPtr->rtneConfig.curr; instPtr->rtneConfig.curr = instPtr->rtneConfig.next; instPtr->rtneConfig.next = &(instPtr->rtneConfig.rtneTable[tableIndx][entryIndx]); rtneEntry = instPtr->rtneConfig.rtneTable[tableIndx][entryIndx]; DBG_INFO("## Routine: %" PRIu32 "-%" PRIu32 ", Routine period: %" PRIu32 ".\n", tableIndx, entryIndx, instPtr->rtneConfig.rtnePrd); switch (rtneEntry.slotType) { case L2_SLOT_IDLE: // Skip a 2 ms slot. DBG_INFO("Skipping idle slot (2 ms)...\n"); Status = SUCCESS; break; case L2_SLOT_ADVERTISE: case L2_SLOT_ACKNOWLEDGE: Status = L2_Scan_Routine(instPtr, &rtneEntry); break; case L2_SLOT_DISTRIBUTE: case L2_SLOT_MANAGE: case L2_SLOT_RTT: case L2_SLOT_SERVICE: Status = L2_Service_Routine(instPtr, &rtneEntry); break; default: DBG_ERROR("UNKNOWN slot type [%" PRId8 "].\n", rtneEntry.slotType); return EPARAM; } // check if routine is finished. if (entryIndx >= L2_SLOTS_PER_FRAME - 1 && tableIndx >= instPtr->rtneConfig.rtnePrd - 1) { L2_EVENT rtneFinishEvent = {L2_ROUTINE_FINISH, &(instPtr->rtneConfig)}; AssertL2Event(instPtr, &rtneFinishEvent); } // Update TTL uint32_t increment = 1; L2_IterEditNode(instPtr->nodeList, &increment, IntcNodeTTL); #ifdef DATALINK_ENABLE_AUTORECONNECT if (L2_GetMainState(instPtr) != L2_INIT_STATE) { L2_IterDeleteNode(instPtr->nodeList, IsNodeConnected); if (L2_GetNodeType(instPtr) == L2_NODE_MEMBER && (L2_GetNodeById(instPtr->nodeList, instPtr->myInfo.headId) == NULL)) { // reset scan process L2_EVENT l2Event; // Assert Net-Setup Done. l2Event.event = L2_EV_LOST_CXN; l2Event.msg = NULL; AssertL2Event(instPtr, &l2Event); } } #endif return Status; } /*****************************************************************************/ /** * @brief Execute a link layer fsm process. * * @param instPtr a pointer to physical layer controller instance * * @return SUCCESS * Other error code. * ******************************************************************************/ int32_t Linklayer_Proc(L2_INST *instPtr) { ASSERT(instPtr != NULL); int32_t Status; PHY_INST *phyRef = instPtr->config.phyInstPtr; TMC_INST *tmcRef = instPtr->config.tmcInstPtr; if (instPtr->isEnable == false) { DBG_INFO("######## Link Layer is not Enabled ########\n"); return SUCCESS; } // FIXME: Consider time wrapping. uint32_t nextTimeUnit = (TimeCtrl_GetIntCnt(tmcRef) + 1) / L2_SLOTS_PER_FRAME; if (nextTimeUnit < instPtr->netStartTime) { DBG_WARNING("Linklayer not start yet, next time unit: %" PRIu32 ", start time unit: %" PRIu32 ".\n", nextTimeUnit, instPtr->netStartTime); return EIGNORE; } // Update slot number. L2_UpdateSlotNum(instPtr); DBG_INFO("######## Current frameNum: %" PRIu32 ", SlotNum: %" PRIu32 " ########\n", instPtr->frameNum.curr, instPtr->slotNum.curr); // Sync System info SysMonitor_GetSysinfo(&(instPtr->sysInfo)); // Update frame status. L2_FrameStateUpdate(instPtr); // Handle frame received in last slot. if (L2_isDataAvail(instPtr) == true) { if (Linklayer_PktHandler(instPtr) != SUCCESS) { DBG_ERROR("Failed to handle last received frame: %" PRId8 ".\n", L2_GetLastFrameType(instPtr)); } } // Run Link layer routine, setup next slot procedure. if (L2_GetNodeType(instPtr) == L2_NODE_HEAD) { Phy_SetMode(phyRef, PHY_MODE_POLL); Status = Linklayer_Routine(instPtr); } else { // L2_NODE_MEMBER if (L2_isCxn2Net(instPtr)) { Phy_SetMode(phyRef, PHY_MODE_POLL); Status = Linklayer_Routine(instPtr); } else { Phy_SetMode(phyRef, PHY_MODE_LISTEN); Status = L2_Scan_Proc(instPtr); } } // Check if previous runs has error occurred. if (Status != SUCCESS) { return Status; } // Check and execute scheduled event. L2_EventPoolHandler(instPtr); // Update link layer state. instPtr->mainState = instPtr->nextState; instPtr->linkLog.tuNum = instPtr->frameNum.curr; instPtr->linkLog.slotNum = (uint8_t)instPtr->slotNum.curr; instPtr->linkLog.nodeType = (L2_GetNodeType(instPtr) == L2_NODE_HEAD) ? 0xAA : 0xCC; instPtr->linkLog.cxnStatus = (L2_isCxn2Net(instPtr)) ? 0x11 : 0x00; instPtr->linkLog.linkStage = (instPtr->mainState == L2_INIT_STATE) ? 0x11 : 0x22; instPtr->linkLog.recxnCnt = instPtr->log.reconnectCnt; instPtr->linkLog.antIndex = instPtr->config.phyInstPtr->antIndex.curr; instPtr->linkLog.advTxCnt = (uint16_t)instPtr->log.advTxCnt; instPtr->linkLog.advRecvCnt = (uint16_t)instPtr->log.advRecvCnt; instPtr->linkLog.advCorrectCnt = (uint16_t)instPtr->log.advCorrectCnt; instPtr->linkLog.ackTxCnt = instPtr->log.ackTxCnt; instPtr->linkLog.ackRecvCnt = instPtr->log.ackRecvCnt; instPtr->linkLog.ackCorrectCnt = instPtr->log.ackCorrectCnt; instPtr->linkLog.disTxCnt = instPtr->log.disTxCnt; instPtr->linkLog.disRecvCnt = instPtr->log.disRecvCnt; instPtr->linkLog.disCorrectCnt = instPtr->log.disCorrectCnt; instPtr->linkLog.mngTxCnt = instPtr->log.mngTxCnt; instPtr->linkLog.mngRecvCnt = instPtr->log.mngRecvCnt; instPtr->linkLog.mngCorrectCnt = instPtr->log.mngCorrectCnt; instPtr->linkLog.rttTxCnt = instPtr->log.rttTxCnt; instPtr->linkLog.rttRecvCnt = instPtr->log.rttRecvCnt; instPtr->linkLog.rttCorrectCnt = instPtr->log.rttCorrectCnt; instPtr->linkLog.svcTxCnt = instPtr->log.svcTxCnt; instPtr->linkLog.svcRecvCnt = instPtr->log.svcRecvCnt; instPtr->linkLog.svcCorrectCnt = instPtr->log.svcCorrectCnt; instPtr->linkLog.timeDev = (instPtr->log.timeDev > INT32_MAX) ? INT32_MAX : ((instPtr->log.timeDev < INT32_MIN) ? INT32_MIN : (int32_t)instPtr->log.timeDev); instPtr->linkLog.sendId = instPtr->log.txLog.dataId; instPtr->linkLog.recvId = instPtr->log.rxLog.dataId; SysMonitor_UpdateLinklayerLog(&(instPtr->linkLog)); return SUCCESS; } /*****************************************************************************/ /** * @brief Layer 2 setup next slot operation, update current operating frame. * * @param instPtr a pointer to linklayer instance * @param txrxMode tx/rx state for next slot * @param frameType frame type of next slot. * * @return SUCCESS/FAILURE * ******************************************************************************/ int32_t L2_SetupNextSlot(L2_INST *instPtr, L2_TOR_TYPE txrxMode, L2_FRAME_TYPE frameType) { ASSERT(instPtr != NULL); PHY_INST *phyRef = instPtr->config.phyInstPtr; int32_t Status; if (frameType < L2_FRAME_ADV || frameType > L2_FRAME_SVC) { DBG_WARNING("L2_SetupNextSlot(): UNKNOWN L2-FrameType %" PRId8 "\n", frameType); return FAILURE; } // logging if (txrxMode == L2_TOR_TX) { instPtr->log.frameTx++; switch (frameType) { case L2_FRAME_ADV: instPtr->log.advTxCnt++; break; case L2_FRAME_ACK: instPtr->log.ackTxCnt++; break; case L2_FRAME_DIS: instPtr->log.disTxCnt++; break; case L2_FRAME_MNG: instPtr->log.mngTxCnt++; break; case L2_FRAME_RTT: instPtr->log.rttTxCnt++; break; case L2_FRAME_SVC: instPtr->log.svcTxCnt++; break; default: break; } } else if (txrxMode == L2_TOR_RX) { instPtr->log.frameRx++; switch (frameType) { case L2_FRAME_ADV: instPtr->log.advRxCnt++; break; case L2_FRAME_ACK: instPtr->log.ackRxCnt++; break; case L2_FRAME_DIS: instPtr->log.disRxCnt++; break; case L2_FRAME_MNG: instPtr->log.mngRxCnt++; break; case L2_FRAME_RTT: instPtr->log.rttRxCnt++; break; case L2_FRAME_SVC: instPtr->log.svcRxCnt++; break; default: break; } } #if _DEBUG_LEVEL >= 3 char *torStr = NULL; char *frameStr = NULL; switch (txrxMode) { case L2_TOR_SUSPEND: torStr = "SUSPEND"; break; case L2_TOR_TX: torStr = "TX"; break; case L2_TOR_RX: torStr = "RX"; break; } switch (frameType) { case L2_FRAME_ADV: frameStr = "Advertisement"; break; case L2_FRAME_ACK: frameStr = "Acknowledgement"; break; case L2_FRAME_DIS: frameStr = "Distribution"; break; case L2_FRAME_MNG: frameStr = "Management"; break; case L2_FRAME_RTT: frameStr = "Round Trip Time"; break; case L2_FRAME_SVC: frameStr = "Service"; break; default: frameStr = "UNKNOWN Frame"; break; } DBG_INFO("# Setup Next Slot # [%" PRIu32 "-%" PRIu32 "], %s, %s.\n", instPtr->frameNum.next, instPtr->slotNum.next, torStr, frameStr); #endif // Update frame and tx/rx mode record. // Frame types shift (last <- curr <- next) L2_RcdShift(&(instPtr->frame), frameType); L2_RcdShift(&(instPtr->tor), txrxMode); TIME_WORD pollTime = {0, 0}; pollTime.int_part = TimeCtrl_GetIntCnt(instPtr->config.tmcInstPtr) + 1; Status = Phy_SetTimeWord(phyRef, &pollTime, PHY_TIME_WORD_POLL); if (Status != SUCCESS) { DBG_ERROR("Physical set poll time failed, with error code: %" PRId32 "\n", Status); } PHY_TOR_TYPE tor; switch (txrxMode) { case L2_TOR_SUSPEND: tor = PHY_TOR_SUSPEND; break; case L2_TOR_TX: tor = PHY_TOR_TX; break; case L2_TOR_RX: tor = PHY_TOR_RX; break; default: DBG_ERROR("Invalid txrx mode :%" PRId8 ".\n", txrxMode); return EPARAM; } Status = Phy_SetupNextSlot(phyRef, tor, L2_L2frame2L1frame(frameType, instPtr->config.serviceRate), instPtr->sendPktData); if (Status != SUCCESS) { DBG_ERROR("Physical setup failed, with error code: %" PRId32 "\n", Status); } return Status; } /** @} LinkLayer_Main*/ /** @} LinkLayer*/