/*****************************************************************************/ /** * @file schdevent.c * @author FanHao (hao_fan_bit_ee@163.com) * @brief DF Linklayer Event scheduling 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 * @{ */ /** * @addtogroup LinkLayer_Scheduler * @{ */ /***************************** Include Files *********************************/ #include #include "schdevent.h" #include "link_layer.h" #include "link_layer_types.h" #include "error.h" #include "clist.h" /***************** Macros (Inline Functions) Definitions *********************/ /**************************** Type Definitions *******************************/ /************************** Variable Definitions *****************************/ static L2_SCHD_EVENT util_schdevent; /************************** Function Prototypes ******************************/ /** * @brief helper function to free schedule events. */ static void schdevent_free(void *val) { if (val == NULL) { return; } free(((L2_SCHD_EVENT *)val)->l2Event.msg); free(val); } /** * @brief helper function to match events by ids. */ static int8_t schdevent_compare(void *a, void *b) { ASSERT(a == NULL); ASSERT(b != NULL); register uint32_t idA, idB; idA = ((L2_SCHD_EVENT *)a)->eventId; idB = ((L2_SCHD_EVENT *)b)->eventId; if (idA == idB) { return 0; } else if (idA < idB) { return -1; } else { return 1; } } /*****************************************************************************/ /** * @brief Creat a schedule event pool. * * @param schdPool a pointer to schedule pool instance. * * @return SUCCESS * ******************************************************************************/ int32_t L2_CreateSchedEventPool(L2_SCHD_POOL **schdPool) { (*schdPool) = malloc(sizeof(L2_SCHD_POOL)); if (*schdPool == NULL) { DBG_ERROR("Failed to allocated memory for schdPool.\n"); return ENOMEM; } (*schdPool)->schdList = clist_new(); if ((*schdPool)->schdList == NULL) { DBG_ERROR("Failed to allocated memory for schdPool->schdList.\n"); free(*schdPool); return ENOMEM; } (*schdPool)->schdList->free = schdevent_free; (*schdPool)->schdList->compare = schdevent_compare; (*schdPool)->envCnt = 0; return SUCCESS; } /*****************************************************************************/ /** * @brief Clear schedule event pool, delete all events in pool and * free allocated memory. * * @param schdPool a pointer to schedule pool instance. * * @return SUCCESS * ******************************************************************************/ void L2_ClearSchedEventPool(L2_SCHD_POOL *schdPool) { ASSERT(schdPool != NULL); clist_clear(schdPool->schdList); schdPool->envCnt = 0; } /*****************************************************************************/ /** * @brief Completely destroy schedule event pool. * * @param schdPool a pointer to schedule pool instance. * * @return SUCCESS * ******************************************************************************/ void L2_DestroySchedEventPool(L2_SCHD_POOL *schdPool) { if (schdPool == NULL) { return; } clist_destroy(schdPool->schdList); free(schdPool); } /*****************************************************************************/ /** * @brief Creat a new schedule event, register event in schedule event pool. * * @param schdPool a pointer to schedule pool instance. * @param event event type of the new schedule event. * @param isVolatile event type of the new schedule event. * * @return NULL error occurred * Pointer to schedule event. * ******************************************************************************/ L2_SCHD_EVENT *L2_CreatNewSchdEvent(L2_SCHD_POOL *schdPool, L2_EVENT_TYPE event, bool isVolatile) { ASSERT(schdPool != NULL); if (event < L2_EV_SCAN_PROC_DONE || event > L2_ROUTINE_UPDATE) { DBG_ERROR("Unknown event type: %" PRId8 ".\n", event); return NULL; } L2_SCHD_EVENT *schdEvent = (L2_SCHD_EVENT *)malloc(sizeof(L2_SCHD_EVENT)); if (schdEvent == NULL) { DBG_ERROR("Failed to allocate schedule event.\n"); return NULL; } clist_node_t *node = clist_node_new(schdEvent); if (node == NULL) { DBG_ERROR("Failed to allocate schedule event list node.\n"); free(schdEvent); return NULL; } schdEvent->eventId = schdPool->envCnt++; schdEvent->occurCnt = 0; schdEvent->l2Event.event = event; schdEvent->l2Event.msg = NULL; schdEvent->isSchd = false; schdEvent->isVolatile = isVolatile; schdEvent->schdSlot = L2_SLOTNO_NONE; schdEvent->schdFrame = L2_FRAMENO_NONE; clist_rpush(schdPool->schdList, node); return schdEvent; } /*****************************************************************************/ /** * @brief Get a schedule event by its id. * * @param schdPool a pointer to schedule pool instance. * @param id ID of the event * * @return NULL * Pointer to the schedule event. * ******************************************************************************/ L2_SCHD_EVENT *L2_GetEventById(L2_SCHD_POOL *schdPool, uint32_t id) { ASSERT(schdPool != NULL); util_schdevent.eventId = id; clist_node_t *node = clist_find(schdPool->schdList, &util_schdevent); if (node != NULL) { DBG_WARNING("Event with id=%" PRIu32 ", dose not exist.\n", id); return NULL; } return ((L2_SCHD_EVENT *)node->val); } /*****************************************************************************/ /** * @brief Delete a schedule event, remove the event from event pool, * release memory. * * @param schdPool a pointer to schedule pool instance. * @param id ID of the event * * @return SUCCESS * error code * ******************************************************************************/ int32_t L2_DeleteSchdEventById(L2_SCHD_POOL *schdPool, uint32_t id) { ASSERT(schdPool != NULL); util_schdevent.eventId = id; clist_node_t *node = clist_find(schdPool->schdList, &util_schdevent); if (node != NULL) { DBG_ERROR("Event with id=%" PRIu32 ", dose not exist.\n", id); return EIGNORE; } clist_remove(schdPool->schdList, node); return SUCCESS; } /*****************************************************************************/ /** * @brief Schedule a event at a specific time(indexed by frame and slot number) * * @param instPtr a pointer to linklayer instance * @param schdEvent pointer to the schedule event. * @param frameNum schedule frame number * @param slotNum schedule slot number * * @return FAILURE, event has already been scheduled/schedule time not allowed. * SUCCESS * ******************************************************************************/ int32_t L2_ScheduleEventAt(L2_INST *instPtr, L2_SCHD_EVENT *schdEvent, uint32_t frameNum, uint32_t slotNum) { ASSERT(schdEvent != NULL); if (L2_isEventScheduled(schdEvent) == true) { DBG_ERROR("Event %" PRIu32 " has already been scheduled.\n", schdEvent->eventId); return FAILURE; } if (frameNum <= L2_GetCurrentFrameNum(instPtr) && slotNum < L2_GetCurrentSlotNum(instPtr)) { DBG_ERROR("Cannot schedule event %" PRIu32 " to the past(current: [%" PRIu32 "-%" PRIu32 "], schedule: [%" PRIu32 "-%" PRIu32 "]).\n", schdEvent->eventId, L2_GetCurrentFrameNum(instPtr), L2_GetCurrentSlotNum(instPtr), frameNum, slotNum); return FAILURE; } DBG_INFO("Schedule event (type:%" PRIu8 ") [%" PRIu32 "] at %" PRIu32 "-%" PRIu32 ".\n", schdEvent->l2Event.event, schdEvent->eventId, frameNum, slotNum); schdEvent->isSchd = true; schdEvent->schdFrame = frameNum; schdEvent->schdSlot = slotNum; return SUCCESS; } /*****************************************************************************/ /** * @brief Schedule a event after a specific time period. * * @param instPtr a pointer to linklayer instance * @param schdEvent pointer to the schedule event. * @param numSlots how many slot later the event should be scheduled * * @return FAILURE, event has already been scheduled/schedule time not allowed. * SUCCESS * ******************************************************************************/ int32_t L2_ScheduleEventAfter(L2_INST *instPtr, L2_SCHD_EVENT *schdEvent, uint32_t numSlots) { ASSERT(schdEvent != NULL); if (L2_isEventScheduled(schdEvent) == true) { DBG_ERROR("Event %" PRIu32 " has already been scheduled.\n", schdEvent->eventId); return FAILURE; } uint32_t frameNum = L2_GetCurrentFrameNum(instPtr) + (numSlots / L2_SLOTS_PER_FRAME); uint32_t slotNum = L2_GetCurrentSlotNum(instPtr) + (numSlots % L2_SLOTS_PER_FRAME); return L2_ScheduleEventAt(instPtr, schdEvent, frameNum, slotNum); } /*****************************************************************************/ /** * @brief Reschedule a event at a specific time(indexed by frame and slot number) * * @param instPtr a pointer to linklayer instance * @param schdEvent pointer to the schedule event. * @param frameNum schedule frame number * @param slotNum schedule slot number * * @return FAILURE, event has already been scheduled/schedule time not allowed. * SUCCESS * ******************************************************************************/ int32_t L2_RescheduleEventAt(L2_INST *instPtr, L2_SCHD_EVENT *schdEvent, uint32_t frameNum, uint32_t slotNum) { ASSERT(schdEvent != NULL); if (schdEvent->isSchd == false) { DBG_WARNING("Event %" PRIu32 " has not been scheduled yet.\n", schdEvent->eventId); } schdEvent->isSchd = false; return L2_ScheduleEventAt(instPtr, schdEvent, frameNum, slotNum); } /*****************************************************************************/ /** * @brief Reschedule a event after a specific time period. * * @param instPtr a pointer to linklayer instance * @param schdEvent pointer to the schedule event. * @param numSlots how many slot later the event should be scheduled * * @return FAILURE, event has already been scheduled/schedule time not allowed. * SUCCESS * ******************************************************************************/ int32_t L2_RescheduleEventAfter(L2_INST *instPtr, L2_SCHD_EVENT *schdEvent, uint32_t numSlots) { ASSERT(schdEvent != NULL); if (schdEvent->isSchd == false) { DBG_WARNING("Event %" PRIu32 " has not been scheduled yet.\n", schdEvent->eventId); } return L2_ScheduleEventAfter(instPtr, schdEvent, numSlots); } /*****************************************************************************/ /** * @brief Check and excute schedule event in event pool illiterate. * * @param instPtr a pointer to linklayer instance * * @return NONE. * ******************************************************************************/ void L2_EventPoolHandler(L2_INST *instPtr) { ASSERT(instPtr != NULL); clist_node_t *node; L2_SCHD_EVENT *event; L2_SCHD_POOL *pool = instPtr->schdEventPool; clist_iterator_t *iter = clist_iterator_new(pool->schdList, LIST_HEAD); if (iter == NULL) { DBG_ERROR("Failed to initialize schedule event pool iteration.\n"); return; } while ((node = clist_iterator_next(iter))) { event = (L2_SCHD_EVENT *)node->val; if (L2_isEventScheduled(event)) { if (event->schdFrame == L2_GetCurrentFrameNum(instPtr) && event->schdSlot == L2_GetCurrentSlotNum(instPtr)) { DBG_INFO("Asserting scheduled event [%" PRIu32 "].\n", event->eventId); (void)AssertL2Event(instPtr, &(event->l2Event)); if (event->isVolatile == true) { DBG_INFO("Deleting scheduled event [%" PRIu32 "], since it is volatile.\n", event->eventId); clist_remove(pool->schdList, node); } else { event->occurCnt++; } } } } clist_iterator_destroy(iter); } /*****************************************************************************/ /** * @brief Append extra message content to specific schedule event. * * @param schdEvent pointer to the schedule event. * msg pointer to the append content. * * @return SUCCESS. * error code. * ******************************************************************************/ inline void L2_SchdEventAppendMsg(L2_SCHD_EVENT *schdEvent, void *msg) { ASSERT(schdEvent != NULL); schdEvent->l2Event.msg = msg; } /*****************************************************************************/ /** * @brief Cancel a scheduled event. * * @param schdEvent pointer to the schedule event. * * @return SUCCESS. * ******************************************************************************/ inline int32_t L2_CancelEvent(L2_SCHD_EVENT *schdEvent) { ASSERT(schdEvent != NULL); if (schdEvent->isSchd == false) { DBG_WARNING("Event %" PRIu32 " has not been scheduled yet.\n", schdEvent->eventId); } schdEvent->isSchd = false; return SUCCESS; } /*****************************************************************************/ /** * @brief Check if the event was scheduled. * * @param schdEvent pointer to the schedule event. * * @return true. * false. * ******************************************************************************/ inline bool L2_isEventScheduled(const L2_SCHD_EVENT *schdEvent) { ASSERT(schdEvent != NULL); return schdEvent->isSchd; } /** @} LinkLayer_Scheduler*/ /** @} LinkLayer*/