/*! ********************************************************************************* * Copyright 2020-2021 NXP * All rights reserved. * * * This is the source file for the OS Abstraction layer for thread. * * SPDX-License-Identifier: BSD-3-Clause ********************************************************************************** */ /*! ********************************************************************************* ************************************************************************************* * Include ************************************************************************************* ********************************************************************************** */ #include "fsl_common.h" #include "fsl_os_abstraction.h" #include "tx_api.h" #include "tx_event_flags.h" #include #include "fsl_component_generic_list.h" #include "fsl_os_abstraction_threadx.h" /*! ********************************************************************************* ************************************************************************************* * Private macros ************************************************************************************* ********************************************************************************** */ /* Weak function. */ #if defined(__GNUC__) #define __WEAK_FUNC __attribute__((weak)) #elif defined(__ICCARM__) #define __WEAK_FUNC __weak #elif defined(__CC_ARM) || defined(__ARMCC_VERSION) #define __WEAK_FUNC __attribute__((weak)) #endif #define millisecToTicks(millisec) (((millisec)*TX_TIMER_TICKS_PER_SECOND + 999U) / 1000U) #ifdef DEBUG_ASSERT #define OS_ASSERT(condition) \ if (!(condition)) \ while (1) \ ; #else #define OS_ASSERT(condition) (void)(condition); #endif /*! @brief Converts milliseconds to ticks*/ #define MSEC_TO_TICK(msec) \ (((uint32_t)(msec) + 500uL / (uint32_t)TX_TIMER_TICKS_PER_SECOND) * (uint32_t)TX_TIMER_TICKS_PER_SECOND / 1000uL) #define TICKS_TO_MSEC(tick) ((uint32_t)((uint64_t)(tick)*1000uL / (uint64_t)TX_TIMER_TICKS_PER_SECOND)) /************************************************************************************ ************************************************************************************* * Private type definitions ************************************************************************************* ************************************************************************************/ typedef struct osa_thread_task { list_element_t link; TX_THREAD taskHandle; } osa_thread_task_t; typedef struct _osa_event_struct { TX_EVENT_FLAGS_GROUP handle; /* The event handle */ uint8_t autoClear; /*!< Auto clear or manual clear */ } osa_event_struct_t; /*! @brief State structure for thread osa manager. */ typedef struct _osa_state { #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) list_label_t taskList; #endif uint32_t basePriority; int32_t basePriorityNesting; uint32_t interruptDisableCount; } osa_state_t; /*! ********************************************************************************* ************************************************************************************* * Private prototypes ************************************************************************************* ********************************************************************************** */ /*! ********************************************************************************* ************************************************************************************* * Public memory declarations ************************************************************************************* ********************************************************************************** */ const uint8_t gUseRtos_c = USE_RTOS; // USE_RTOS = 0 for BareMetal and 1 for OS static TX_INTERRUPT_SAVE_AREA #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) static osa_state_t s_osaState = {0}; #endif /*! ********************************************************************************* ************************************************************************************* * Private memory declarations ************************************************************************************* ********************************************************************************** */ /*! ********************************************************************************* ************************************************************************************* * Public functions ************************************************************************************* ********************************************************************************** */ /*FUNCTION********************************************************************** * * Function Name : OSA_MemoryAllocate * Description : Reserves the requested amount of memory in bytes. * *END**************************************************************************/ void *OSA_MemoryAllocate(uint32_t length) { return NULL; } /*FUNCTION********************************************************************** * * Function Name : OSA_MemoryFree * Description : Frees the memory previously reserved. * *END**************************************************************************/ void OSA_MemoryFree(void *p) { } /*FUNCTION********************************************************************** * * Function Name : OSA_EnterCritical * Description : Enter critical. * *END**************************************************************************/ void OSA_EnterCritical(uint32_t *sr) { } /*FUNCTION********************************************************************** * * Function Name : OSA_ExitCritical * Description : Exit critical. * *END**************************************************************************/ void OSA_ExitCritical(uint32_t sr) { } /*FUNCTION********************************************************************** * * Function Name : OSA_Init * Description : This function is used to setup the basic services, it should * be called first in function main. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) void OSA_Init(void) { LIST_Init((&s_osaState.taskList), 0); s_osaState.basePriorityNesting = 0; s_osaState.interruptDisableCount = 0; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_Start * Description : This function is used to start RTOS scheduler. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) /* Configures _tx_initialize_low_level_ptr and tx_application_define_ptr */ extern void Prepare_ThreadX(void); void OSA_Start(void) { Prepare_ThreadX(); /* Start ThreadX */ tx_kernel_enter(); } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TaskGetCurrentHandle * Description : This function is used to get current active task's handler. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) osa_task_handle_t OSA_TaskGetCurrentHandle(void) { return NULL; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TaskYield * Description : When a task calls this function, it will give up CPU and put * itself to the tail of ready list. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) osa_status_t OSA_TaskYield(void) { return KOSA_StatusSuccess; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TaskGetPriority * Description : This function returns task's priority by task handler. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) osa_task_priority_t OSA_TaskGetPriority(osa_task_handle_t taskHandle) { return 0U; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TaskSetPriority * Description : This function sets task's priority by task handler. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) osa_status_t OSA_TaskSetPriority(osa_task_handle_t taskHandle, osa_task_priority_t taskPriority) { return KOSA_StatusSuccess; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TaskCreate * Description : This function is used to create a task and make it ready. * Param[in] : threadDef - Definition of the thread. * task_param - Parameter to pass to the new thread. * Return Thread handle of the new thread, or NULL if failed. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) osa_status_t OSA_TaskCreate(osa_task_handle_t taskHandle, const osa_task_def_t *thread_def, osa_task_param_t task_param) { assert(taskHandle); osa_status_t status = KOSA_StatusError; osa_thread_task_t *ptask = (osa_thread_task_t *)taskHandle; if (tx_thread_create(&ptask->taskHandle, /* task handle, allocated by application */ (char *)thread_def->tname, /* thread name */ (void (*)(ULONG))thread_def->pthread, /* entry function */ (ULONG)((ULONG *)task_param), /* entry input */ (void *)thread_def->tstack, /* stack start */ (ULONG)thread_def->stacksize, /* stack size */ PRIORITY_OSA_TO_THREAD(thread_def->tpriority), /* initial priority */ 0U, /* preempt threshold */ 0U, /* time slice */ true /* auto start */ ) == TX_SUCCESS) { OSA_InterruptDisable(); (void)LIST_AddTail(&s_osaState.taskList, (list_element_handle_t) & (ptask->link)); OSA_InterruptEnable(); status = KOSA_StatusSuccess; } return status; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TaskDestroy * Description : This function destroy a task. * Param[in] :taskHandle - Thread handle. * Return KOSA_StatusSuccess if the task is destroied, otherwise return KOSA_StatusError. * *END**************************************************************************/ #if (defined(FSL_OSA_TASK_ENABLE) && (FSL_OSA_TASK_ENABLE > 0U)) osa_status_t OSA_TaskDestroy(osa_task_handle_t taskHandle) { assert(taskHandle); osa_thread_task_t *ptask = (osa_thread_task_t *)taskHandle; osa_status_t status = KOSA_StatusSuccess; #if 0 uint16_t oldPriority; /*Change priority to avoid context switches*/ oldPriority = OSA_TaskGetPriority(OSA_TaskGetCurrentHandle()); (void)OSA_TaskSetPriority(OSA_TaskGetCurrentHandle(), OSA_PRIORITY_REAL_TIME); if (TX_SUCCESS != tx_thread_delete(&ptask->taskHandle)) { status = KOSA_StatusError; } (void)OSA_TaskSetPriority(OSA_TaskGetCurrentHandle(), oldPriority); OSA_InterruptDisable(); (void)LIST_RemoveElement(taskHandle); OSA_InterruptEnable(); #else if (TX_SUCCESS != tx_thread_delete(&ptask->taskHandle)) { status = KOSA_StatusError; } (void)LIST_RemoveElement(taskHandle); #endif return status; } #endif /*FUNCTION********************************************************************** * * Function Name : OSA_TimeDelay * Description : This function is used to suspend the active thread for the given number of milliseconds. * *END**************************************************************************/ void OSA_TimeDelay(uint32_t millisec) { (void)tx_thread_sleep(millisecToTicks(millisec)); } /*FUNCTION********************************************************************** * * Function Name : OSA_TimeGetMsec * Description : This function gets current time in milliseconds. * *END**************************************************************************/ uint32_t OSA_TimeGetMsec(void) { return TICKS_TO_MSEC(tx_time_get()); } /*FUNCTION********************************************************************** * * Function Name : OSA_SemaphoreCreate * Description : This function is used to create a semaphore. * Return : Semaphore handle of the new semaphore, or NULL if failed. * *END**************************************************************************/ osa_status_t OSA_SemaphoreCreate(osa_semaphore_handle_t semaphoreHandle, uint32_t initValue) { osa_status_t status = KOSA_StatusSuccess; assert(semaphoreHandle != NULL); if (TX_SUCCESS != tx_semaphore_create((TX_SEMAPHORE *)semaphoreHandle, NULL, (ULONG)initValue)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_SemaphoreDestroy * Description : This function is used to destroy a semaphore. * Return : KOSA_StatusSuccess if the semaphore is destroyed successfully, otherwise return KOSA_StatusError. * *END**************************************************************************/ osa_status_t OSA_SemaphoreDestroy(osa_semaphore_handle_t semaphoreHandle) { osa_status_t status = KOSA_StatusSuccess; assert(semaphoreHandle != NULL); if (TX_SUCCESS != tx_semaphore_delete((TX_SEMAPHORE *)semaphoreHandle)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_SemaphoreWait * Description : This function checks the semaphore's counting value, if it is * positive, decreases it and returns KOSA_StatusSuccess, otherwise, timeout * will be used for wait. The parameter timeout indicates how long should wait * in milliseconds. Pass osaWaitForever_c to wait indefinitely, pass 0 will * return KOSA_StatusTimeout immediately if semaphore is not positive. * This function returns KOSA_StatusSuccess if the semaphore is received, returns * KOSA_StatusTimeout if the semaphore is not received within the specified * 'timeout', returns KOSA_StatusError if any errors occur during waiting. * *END**************************************************************************/ osa_status_t OSA_SemaphoreWait(osa_semaphore_handle_t semaphoreHandle, uint32_t millisec) { osa_status_t status = KOSA_StatusSuccess; ULONG timeoutTicks; assert(semaphoreHandle != NULL); /* Convert timeout from millisecond to tick. */ if (millisec == osaWaitForever_c) { timeoutTicks = TX_WAIT_FOREVER; } else if (millisec == osaWaitNone_c) { timeoutTicks = TX_NO_WAIT; } else { timeoutTicks = MSEC_TO_TICK(millisec); } if (TX_SUCCESS != tx_semaphore_get((TX_SEMAPHORE *)semaphoreHandle, timeoutTicks)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_SemaphorePost * Description : This function is used to wake up one task that wating on the * semaphore. If no task is waiting, increase the semaphore. The function returns * KOSA_StatusSuccess if the semaphre is post successfully, otherwise returns * KOSA_StatusError. * *END**************************************************************************/ osa_status_t OSA_SemaphorePost(osa_semaphore_handle_t semaphoreHandle) { osa_status_t status = KOSA_StatusSuccess; assert(semaphoreHandle != NULL); if (TX_SUCCESS != tx_semaphore_put((TX_SEMAPHORE *)semaphoreHandle)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_MutexCreate * Description : This function is used to create a mutex. * Return : KOSA_StatusSuccess if mutex is created successfully, otherwise returns * KOSA_StatusError. * *END**************************************************************************/ osa_status_t OSA_MutexCreate(osa_mutex_handle_t mutexHandle) { assert(mutexHandle); osa_status_t status = KOSA_StatusSuccess; #ifndef CPU2 if (TX_SUCCESS != tx_mutex_create((TX_MUTEX *)mutexHandle, NULL, 0U)) { status = KOSA_StatusError; } #endif return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_MutexLock * Description : This function checks the mutex's status, if it is unlocked, * lock it and returns KOSA_StatusSuccess, otherwise, wait for the mutex. * This function returns KOSA_StatusSuccess if the mutex is obtained, returns * KOSA_StatusError if any errors occur during waiting. If the mutex has been * locked, pass 0 as timeout will return KOSA_StatusTimeout immediately. * *END**************************************************************************/ osa_status_t OSA_MutexLock(osa_mutex_handle_t mutexHandle, uint32_t millisec) { assert(mutexHandle); osa_status_t status = KOSA_StatusSuccess; #ifndef CPU2 uint32_t timeoutTicks; /* Convert timeout from millisecond to tick. */ if (millisec == osaWaitForever_c) { timeoutTicks = TX_WAIT_FOREVER; } else { timeoutTicks = MSEC_TO_TICK(millisec); } if (TX_SUCCESS != tx_mutex_get((TX_MUTEX *)mutexHandle, timeoutTicks)) { status = KOSA_StatusError; } #endif return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_MutexUnlock * Description : This function is used to unlock a mutex. * *END**************************************************************************/ osa_status_t OSA_MutexUnlock(osa_mutex_handle_t mutexHandle) { assert(mutexHandle); osa_status_t status = KOSA_StatusSuccess; #ifndef CPU2 if (TX_SUCCESS != tx_mutex_put((TX_MUTEX *)mutexHandle)) { status = KOSA_StatusError; } #endif return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_MutexDestroy * Description : This function is used to destroy a mutex. * Return : KOSA_StatusSuccess if the lock object is destroyed successfully, otherwise return KOSA_StatusError. * *END**************************************************************************/ osa_status_t OSA_MutexDestroy(osa_mutex_handle_t mutexHandle) { assert(mutexHandle); osa_status_t status = KOSA_StatusSuccess; if (TX_SUCCESS != tx_mutex_delete((TX_MUTEX *)mutexHandle)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_EventCreate * Description : This function is used to create a event object. * Return : Event handle of the new event, or NULL if failed. * *END**************************************************************************/ osa_status_t OSA_EventCreate(osa_event_handle_t eventHandle, uint8_t autoClear) { osa_event_struct_t *pEventStruct = (osa_event_struct_t *)eventHandle; osa_status_t status = KOSA_StatusSuccess; assert(eventHandle != NULL); pEventStruct->autoClear = autoClear; if (TX_SUCCESS != tx_event_flags_create(&pEventStruct->handle, NULL)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_EventSet * Description : Set one or more event flags of an event object. * Return : KOSA_StatusSuccess if set successfully, KOSA_StatusError if failed. * *END**************************************************************************/ osa_status_t OSA_EventSet(osa_event_handle_t eventHandle, osa_event_flags_t flagsToSet) { osa_event_struct_t *pEventStruct = (osa_event_struct_t *)eventHandle; osa_status_t status = KOSA_StatusSuccess; assert(eventHandle != NULL); if (TX_SUCCESS != tx_event_flags_set(&pEventStruct->handle, (ULONG)flagsToSet, TX_OR)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_EventClear * Description : Clear one or more event flags of an event object. * Return :KOSA_StatusSuccess if clear successfully, KOSA_StatusError if failed. * *END**************************************************************************/ osa_status_t OSA_EventClear(osa_event_handle_t eventHandle, osa_event_flags_t flagsToClear) { osa_event_struct_t *pEventStruct = (osa_event_struct_t *)eventHandle; osa_status_t status = KOSA_StatusSuccess; assert(eventHandle != NULL); if (TX_SUCCESS != tx_event_flags_set(&pEventStruct->handle, (ULONG)~flagsToClear, TX_AND)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_EventGet * Description : This function is used to get event's flags that specified by prameter * flagsMask, and the flags (user specified) are obatianed by parameter pFlagsOfEvent. So * you should pass the parameter 0xffffffff to specify you want to check all. * Return :KOSA_StatusSuccess if event flags were successfully got, KOSA_StatusError if failed. * *END**************************************************************************/ osa_status_t OSA_EventGet(osa_event_handle_t eventHandle, osa_event_flags_t flagsMask, osa_event_flags_t *pFlagsOfEvent) { osa_event_struct_t *pEventStruct = (osa_event_struct_t *)eventHandle; osa_status_t status = KOSA_StatusSuccess; ULONG flags; assert(eventHandle != NULL); if (NULL == pFlagsOfEvent) { return KOSA_StatusError; } if (TX_SUCCESS != tx_event_flags_info_get(&pEventStruct->handle, NULL, &flags, NULL, NULL, NULL)) { status = KOSA_StatusError; } else { *pFlagsOfEvent = (osa_event_flags_t)flags & flagsMask; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_EventWait * Description : This function checks the event's status, if it meets the wait * condition, return KOSA_StatusSuccess, otherwise, timeout will be used for * wait. The parameter timeout indicates how long should wait in milliseconds. * Pass osaWaitForever_c to wait indefinitely, pass 0 will return the value * KOSA_StatusTimeout immediately if wait condition is not met. The event flags * will be cleared if the event is auto clear mode. Flags that wakeup waiting * task could be obtained from the parameter setFlags. * This function returns KOSA_StatusSuccess if wait condition is met, returns * KOSA_StatusTimeout if wait condition is not met within the specified * 'timeout', returns KOSA_StatusError if any errors occur during waiting. * *END**************************************************************************/ osa_status_t OSA_EventWait(osa_event_handle_t eventHandle, osa_event_flags_t flagsToWait, uint8_t waitAll, uint32_t millisec, osa_event_flags_t *pSetFlags) { osa_event_struct_t *pEventStruct = (osa_event_struct_t *)eventHandle; uint32_t timeoutTicks; osa_event_flags_t flagsSave; UINT options = 0; UINT status = 0; assert(eventHandle != NULL); /* Clean FreeRTOS cotrol flags */ flagsToWait = flagsToWait & 0x00FFFFFFU; /* Convert timeout from millisecond to tick. */ if (millisec == osaWaitForever_c) { timeoutTicks = TX_WAIT_FOREVER; } else if (millisec == osaWaitNone_c) { timeoutTicks = TX_NO_WAIT; } else { timeoutTicks = MSEC_TO_TICK(millisec); } if (1U == pEventStruct->autoClear) { options |= TX_EVENT_FLAGS_CLEAR_MASK; } if (1U == waitAll) { options |= TX_EVENT_FLAGS_AND_MASK; } status = tx_event_flags_get(&pEventStruct->handle, flagsToWait, options, (ULONG *)&flagsSave, timeoutTicks); if (status != TX_SUCCESS && status != TX_NO_EVENTS) { return KOSA_StatusError; } flagsSave &= (osa_event_flags_t)flagsToWait; if (NULL != pSetFlags) { *pSetFlags = (osa_event_flags_t)flagsSave; } if (0U != flagsSave) { return KOSA_StatusSuccess; } else { return KOSA_StatusTimeout; } } /*FUNCTION********************************************************************** * * Function Name : OSA_EventDestroy * Description : This function is used to destroy a event object. Return * KOSA_StatusSuccess if the event object is destroyed successfully, otherwise * return KOSA_StatusError. * *END**************************************************************************/ osa_status_t OSA_EventDestroy(osa_event_handle_t eventHandle) { osa_event_struct_t *pEventStruct = (osa_event_struct_t *)eventHandle; osa_status_t status = KOSA_StatusSuccess; assert(eventHandle != NULL); if (TX_SUCCESS != tx_event_flags_delete(&pEventStruct->handle)) { status = KOSA_StatusError; } return status; } /*FUNCTION********************************************************************** * * Function Name : OSA_InterruptEnable * Description : self explanatory. * *END**************************************************************************/ void OSA_InterruptEnable(void) { #if 0 if (0U != __get_IPSR()) { if (1 == s_osaState.basePriorityNesting) { portCLEAR_INTERRUPT_MASK_FROM_ISR(s_osaState.basePriority); } if (s_osaState.basePriorityNesting > 0) { s_osaState.basePriorityNesting--; } } else { portEXIT_CRITICAL(); } #endif } /*FUNCTION********************************************************************** * * Function Name : OSA_InterruptDisable * Description : self explanatory. * *END**************************************************************************/ void OSA_InterruptDisable(void) { #if 0 if (0U != __get_IPSR()) { if (0 == s_osaState.basePriorityNesting) { s_osaState.basePriority = portSET_INTERRUPT_MASK_FROM_ISR(); } s_osaState.basePriorityNesting++; } else { portENTER_CRITICAL(); } #endif } /*FUNCTION********************************************************************** * * Function Name : OSA_EnableIRQGlobal * Description : enable interrupts using PRIMASK register. * *END**************************************************************************/ void OSA_EnableIRQGlobal(void) { TX_RESTORE } /*FUNCTION********************************************************************** * * Function Name : OSA_DisableIRQGlobal * Description : disable interrupts using PRIMASK register. * *END**************************************************************************/ void OSA_DisableIRQGlobal(void) { TX_DISABLE } /*FUNCTION********************************************************************** * * Function Name : OSA_InstallIntHandler * Description : This function is used to install interrupt handler. * *END**************************************************************************/ void OSA_InstallIntHandler(uint32_t IRQNumber, void (*handler)(void)) { #if defined(__IAR_SYSTEMS_ICC__) _Pragma("diag_suppress = Pm138") #endif #if defined(ENABLE_RAM_VECTOR_TABLE) (void) InstallIRQHandler((IRQn_Type)IRQNumber, (uint32_t) * (uint32_t *)&handler); #endif /* ENABLE_RAM_VECTOR_TABLE. */ #if defined(__IAR_SYSTEMS_ICC__) _Pragma("diag_remark = PM138") #endif }