2023-04-20 23:08:27 -04:00
/*
* SPDX - FileCopyrightText : 2020 Amazon . com , Inc . or its affiliates
*
* SPDX - License - Identifier : MIT
*/
/*
* FreeRTOS Kernel V10 .4 .3
* Copyright ( C ) 2020 Amazon . com , Inc . or its affiliates . All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a copy of
* this software and associated documentation files ( the " Software " ) , to deal in
* the Software without restriction , including without limitation the rights to
* use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of
* the Software , and to permit persons to whom the Software is furnished to do so ,
* subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY , FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER
* IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*
* https : //www.FreeRTOS.org
* https : //github.com/FreeRTOS
*
*/
/*-----------------------------------------------------------
* Implementation of functions defined in portable . h for the Posix port .
*
* Each task has a pthread which eases use of standard debuggers
* ( allowing backtraces of tasks etc ) . Threads for tasks that are not
* running are blocked in sigwait ( ) .
*
* Task switch is done by resuming the thread for the next task by
* signaling the condition variable and then waiting on a condition variable
* with the current thread .
*
* The timer interrupt uses SIGALRM and care is taken to ensure that
* the signal handler runs only on the thread for the current task .
*
* Use of part of the standard C library requires care as some
* functions can take pthread mutexes internally which can result in
* deadlocks as the FreeRTOS kernel can switch tasks while they ' re
* holding a pthread mutex .
*
* stdio ( printf ( ) and friends ) should be called from a single task
* only or serialized with a FreeRTOS primitive such as a binary
* semaphore or mutex .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include <errno.h>
# include <pthread.h>
# include <signal.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/time.h>
# include <sys/times.h>
# include <time.h>
# include <unistd.h>
# include <assert.h>
/* Scheduler includes. */
# include "esp_heap_caps.h"
# include "FreeRTOS.h"
# include "task.h"
# include "esp_task.h"
# include "timers.h"
# include "utils/wait_for_event.h"
# include "esp_log.h"
/*-----------------------------------------------------------*/
# define SIG_RESUME SIGUSR1
typedef struct THREAD
{
pthread_t pthread ;
TaskFunction_t pxCode ;
void * pvParams ;
BaseType_t xDying ;
struct event * ev ;
} Thread_t ;
/*
* The additional per - thread data is stored at the beginning of the
* task ' s stack .
*/
static inline Thread_t * prvGetThreadFromTask ( TaskHandle_t xTask )
{
StackType_t * pxTopOfStack = * ( StackType_t * * ) xTask ;
return ( Thread_t * ) ( pxTopOfStack + 1 ) ;
}
/*-----------------------------------------------------------*/
static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT ;
static sigset_t xAllSignals ;
static sigset_t xSchedulerOriginalSignalMask ;
static pthread_t hMainThread = ( pthread_t ) NULL ;
// These are saved as part of a thread's state in prvSwitchThread()
static volatile portBASE_TYPE uxCriticalNestingIDF = 0 ; /* Track nesting calls for IDF style critical sections. FreeRTOS critical section nesting is maintained in the TCB. */
static volatile UBaseType_t uxInterruptNesting = 0 ; /* Tracks if we are currently in an interrupt. */
static volatile portBASE_TYPE uxInterruptLevel = 0 ; /* Tracks the current level (i.e., interrupt mask) */
/*-----------------------------------------------------------*/
static portBASE_TYPE xSchedulerEnd = pdFALSE ;
/*-----------------------------------------------------------*/
static void prvSetupSignalsAndSchedulerPolicy ( void ) ;
static void prvSetupTimerInterrupt ( void ) ;
static void * prvWaitForStart ( void * pvParams ) ;
static void prvSwitchThread ( Thread_t * xThreadToResume ,
Thread_t * xThreadToSuspend ) ;
static void prvSuspendSelf ( Thread_t * thread ) ;
static void prvResumeThread ( Thread_t * xThreadId ) ;
static void vPortSystemTickHandler ( int sig ) ;
static void vPortStartFirstTask ( void ) ;
/*-----------------------------------------------------------*/
static void prvFatalError ( const char * pcCall , int iErrno )
{
fprintf ( stderr , " %s: %s \n " , pcCall , strerror ( iErrno ) ) ;
abort ( ) ;
}
/*
* See header file for description .
*/
2023-07-31 10:59:41 -04:00
StackType_t * pxPortInitialiseStack ( StackType_t * pxTopOfStack ,
StackType_t * pxEndOfStack ,
TaskFunction_t pxCode ,
void * pvParameters )
2023-04-20 23:08:27 -04:00
{
Thread_t * thread ;
pthread_attr_t xThreadAttributes ;
size_t ulStackSize ;
int iRet ;
( void ) pthread_once ( & hSigSetupThread , prvSetupSignalsAndSchedulerPolicy ) ;
/*
* Store the additional thread data at the start of the stack .
*/
thread = ( Thread_t * ) ( pxTopOfStack + 1 ) - 1 ;
2023-07-31 10:59:41 -04:00
pxTopOfStack = ( StackType_t * ) thread - 1 ;
2023-04-20 23:08:27 -04:00
ulStackSize = ( pxTopOfStack + 1 - pxEndOfStack ) * sizeof ( * pxTopOfStack ) ;
thread - > pxCode = pxCode ;
thread - > pvParams = pvParameters ;
thread - > xDying = pdFALSE ;
pthread_attr_init ( & xThreadAttributes ) ;
pthread_attr_setstack ( & xThreadAttributes , pxEndOfStack , ulStackSize ) ;
thread - > ev = event_create ( ) ;
BaseType_t prev_intr_level = xPortSetInterruptMask ( ) ;
iRet = pthread_create ( & thread - > pthread , & xThreadAttributes ,
prvWaitForStart , thread ) ;
if ( iRet )
{
prvFatalError ( " pthread_create " , iRet ) ;
}
vPortClearInterruptMask ( prev_intr_level ) ;
return pxTopOfStack ;
}
/*-----------------------------------------------------------*/
void vPortStartFirstTask ( void )
{
Thread_t * pxFirstThread = prvGetThreadFromTask ( xTaskGetCurrentTaskHandle ( ) ) ;
/* Start the first task. */
prvResumeThread ( pxFirstThread ) ;
}
/*-----------------------------------------------------------*/
/*
* See header file for description .
*/
portBASE_TYPE xPortStartScheduler ( void )
{
int iSignal ;
sigset_t xSignals ;
hMainThread = pthread_self ( ) ;
/* Start the timer that generates the tick ISR(SIGALRM).
Interrupts are disabled here already . */
prvSetupTimerInterrupt ( ) ;
/* Start the first task. */
vPortStartFirstTask ( ) ;
/* Wait until signaled by vPortEndScheduler(). */
sigemptyset ( & xSignals ) ;
sigaddset ( & xSignals , SIG_RESUME ) ;
while ( ! xSchedulerEnd )
{
sigwait ( & xSignals , & iSignal ) ;
}
/* Cancel the Idle task and free its resources */
# if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
vPortCancelThread ( xTaskGetIdleTaskHandle ( ) ) ;
# endif
# if ( configUSE_TIMERS == 1 )
/* Cancel the Timer task and free its resources */
vPortCancelThread ( xTimerGetTimerDaemonTaskHandle ( ) ) ;
# endif /* configUSE_TIMERS */
/* Restore original signal mask. */
( void ) pthread_sigmask ( SIG_SETMASK , & xSchedulerOriginalSignalMask , NULL ) ;
return 0 ;
}
/*-----------------------------------------------------------*/
void vPortEndScheduler ( void )
{
struct itimerval itimer ;
struct sigaction sigtick ;
Thread_t * xCurrentThread ;
/* Stop the timer and ignore any pending SIGALRMs that would end
* up running on the main thread when it is resumed . */
itimer . it_value . tv_sec = 0 ;
itimer . it_value . tv_usec = 0 ;
itimer . it_interval . tv_sec = 0 ;
itimer . it_interval . tv_usec = 0 ;
( void ) setitimer ( ITIMER_REAL , & itimer , NULL ) ;
sigtick . sa_flags = 0 ;
sigtick . sa_handler = SIG_IGN ;
sigemptyset ( & sigtick . sa_mask ) ;
sigaction ( SIGALRM , & sigtick , NULL ) ;
/* Signal the scheduler to exit its loop. */
xSchedulerEnd = pdTRUE ;
( void ) pthread_kill ( hMainThread , SIG_RESUME ) ;
xCurrentThread = prvGetThreadFromTask ( xTaskGetCurrentTaskHandle ( ) ) ;
prvSuspendSelf ( xCurrentThread ) ;
}
/*-----------------------------------------------------------*/
static void vPortDisableInterrupts ( void )
{
pthread_sigmask ( SIG_BLOCK , & xAllSignals , NULL ) ;
}
/*-----------------------------------------------------------*/
static void vPortEnableInterrupts ( void )
{
pthread_sigmask ( SIG_UNBLOCK , & xAllSignals , NULL ) ;
}
/*-----------------------------------------------------------*/
void vPortEnterCriticalIDF ( void )
{
if ( uxCriticalNestingIDF = = 0 & & uxInterruptLevel = = 0 )
{
vPortDisableInterrupts ( ) ;
}
uxCriticalNestingIDF + + ;
}
/*-----------------------------------------------------------*/
void vPortExitCriticalIDF ( void )
{
uxCriticalNestingIDF - - ;
/* If we have reached 0 then re-enable the interrupts. */
if ( uxCriticalNestingIDF = = 0 & & uxInterruptLevel = = 0 )
{
vPortEnableInterrupts ( ) ;
}
}
/*-----------------------------------------------------------*/
void vPortYieldFromISR ( void )
{
Thread_t * xThreadToSuspend ;
Thread_t * xThreadToResume ;
xThreadToSuspend = prvGetThreadFromTask ( xTaskGetCurrentTaskHandle ( ) ) ;
vTaskSwitchContext ( xPortGetCoreID ( ) ) ;
xThreadToResume = prvGetThreadFromTask ( xTaskGetCurrentTaskHandle ( ) ) ;
prvSwitchThread ( xThreadToResume , xThreadToSuspend ) ;
}
/*-----------------------------------------------------------*/
void vPortYield ( void )
{
BaseType_t prev_intr_level = xPortSetInterruptMask ( ) ;
vPortYieldFromISR ( ) ;
vPortClearInterruptMask ( prev_intr_level ) ;
}
/*-----------------------------------------------------------*/
/* In SMP code, the disable/enable interrupt macros are calling the set/get interrupt mask functions below.
Hence , we need to call vPortDisableInterrupts ( ) and vPortEnableInterrupts ( ) , otherwise interrupts
are never disabled / enabled . */
portBASE_TYPE xPortSetInterruptMask ( void )
{
if ( uxInterruptLevel = = 0 & & uxCriticalNestingIDF = = 0 ) {
vPortDisableInterrupts ( ) ;
}
portBASE_TYPE prev_intr_level = uxInterruptLevel ;
uxInterruptLevel + + ;
return prev_intr_level ;
}
/*-----------------------------------------------------------*/
void vPortClearInterruptMask ( portBASE_TYPE xMask )
{
// Only reenable interrupts if xMask is 0
uxInterruptLevel = xMask ;
if ( uxInterruptLevel = = 0 & & uxCriticalNestingIDF = = 0 ) {
vPortEnableInterrupts ( ) ;
}
}
/*-----------------------------------------------------------*/
static uint64_t prvGetTimeNs ( void )
{
struct timespec t ;
clock_gettime ( CLOCK_MONOTONIC , & t ) ;
return t . tv_sec * 1000000000ull + t . tv_nsec ;
}
static uint64_t prvStartTimeNs ;
/* commented as part of the code below in vPortSystemTickHandler,
* to adjust timing according to full demo requirements */
/* static uint64_t prvTickCount; */
/*
* Setup the systick timer to generate the tick interrupts at the required
* frequency .
*/
void prvSetupTimerInterrupt ( void )
{
struct itimerval itimer ;
int iRet ;
/* Initialise the structure with the current timer information. */
iRet = getitimer ( ITIMER_REAL , & itimer ) ;
if ( iRet )
{
prvFatalError ( " getitimer " , errno ) ;
}
/* Set the interval between timer events. */
itimer . it_interval . tv_sec = 0 ;
itimer . it_interval . tv_usec = portTICK_RATE_MICROSECONDS ;
/* Set the current count-down. */
itimer . it_value . tv_sec = 0 ;
itimer . it_value . tv_usec = portTICK_RATE_MICROSECONDS ;
/* Set-up the timer interrupt. */
iRet = setitimer ( ITIMER_REAL , & itimer , NULL ) ;
if ( iRet )
{
prvFatalError ( " setitimer " , errno ) ;
}
prvStartTimeNs = prvGetTimeNs ( ) ;
}
/*-----------------------------------------------------------*/
static void vPortSystemTickHandler ( int sig )
{
Thread_t * pxThreadToSuspend ;
Thread_t * pxThreadToResume ;
BaseType_t xSwitchRequired ;
/* uint64_t xExpectedTicks; */
// Handling a timer signal, so we are currently in an interrupt.
uxInterruptNesting + + ;
# if ( configUSE_PREEMPTION == 1 )
pxThreadToSuspend = prvGetThreadFromTask ( xTaskGetCurrentTaskHandle ( ) ) ;
# endif
/* Tick Increment, accounting for any lost signals or drift in
* the timer . */
/*
* Comment code to adjust timing according to full demo requirements
* xExpectedTicks = ( prvGetTimeNs ( ) - prvStartTimeNs )
* / ( portTICK_RATE_MICROSECONDS * 1000 ) ;
* do { */
xSwitchRequired = xTaskIncrementTick ( ) ;
/* prvTickCount++;
* } while ( prvTickCount < xExpectedTicks ) ;
*/
# if ( configUSE_PREEMPTION == 1 )
if ( xSwitchRequired = = pdTRUE ) {
/* Select Next Task. */
vTaskSwitchContext ( xPortGetCoreID ( ) ) ;
pxThreadToResume = prvGetThreadFromTask ( xTaskGetCurrentTaskHandle ( ) ) ;
prvSwitchThread ( pxThreadToResume , pxThreadToSuspend ) ;
}
# else
( void ) xSwitchRequired ;
# endif
// Returning from the timer signal handler, so we are exiting the interrupt.
uxInterruptNesting - - ;
}
/*-----------------------------------------------------------*/
void vPortThreadDying ( void * pxTaskToDelete , volatile BaseType_t * pxPendYield )
{
Thread_t * pxThread = prvGetThreadFromTask ( pxTaskToDelete ) ;
pxThread - > xDying = pdTRUE ;
}
void vPortCancelThread ( void * pxTaskToDelete )
{
Thread_t * pxThreadToCancel = prvGetThreadFromTask ( pxTaskToDelete ) ;
/*
* The thread has already been suspended so it can be safely cancelled .
*/
pthread_cancel ( pxThreadToCancel - > pthread ) ;
pthread_join ( pxThreadToCancel - > pthread , NULL ) ;
event_delete ( pxThreadToCancel - > ev ) ;
}
/*-----------------------------------------------------------*/
static void * prvWaitForStart ( void * pvParams )
{
Thread_t * pxThread = pvParams ;
prvSuspendSelf ( pxThread ) ;
/* Resumed for the first time, thus this thread didn't previously call
* prvSwitchThread ( ) . So we need to initialise the state variables for this
* thread . */
uxCriticalNestingIDF = 0 ;
uxInterruptNesting = 0 ;
uxInterruptLevel = 0 ;
vPortEnableInterrupts ( ) ;
/* Call the task's entry point. */
pxThread - > pxCode ( pxThread - > pvParams ) ;
/* A function that implements a task must not exit or attempt to return to
* its caller as there is nothing to return to . If a task wants to exit it
* should instead call vTaskDelete ( NULL ) . Artificially force an assert ( )
* to be triggered if configASSERT ( ) is defined , so application writers can
* catch the error . */
configASSERT ( pdFALSE ) ;
return NULL ;
}
/*-----------------------------------------------------------*/
static void prvSwitchThread ( Thread_t * pxThreadToResume ,
Thread_t * pxThreadToSuspend )
{
BaseType_t uxSavedCriticalNestingIDF ;
BaseType_t uxSavedInterruptNesting ;
BaseType_t uxSavedInterruptLevel ;
if ( pxThreadToSuspend ! = pxThreadToResume )
{
/* It is possible for prvSwitchThread() to be called...
* - while inside an ISR ( i . e . , via vPortSystemTickHandler ( ) or vPortYieldFromISR ( ) )
* - while interrupts are disabled or in a critical section ( i . e . , via vPortYield ( ) )
*
* So we need to save the various count variables as part of the thread ' s context .
* They are restored when the pthread switches back . */
uxSavedCriticalNestingIDF = uxCriticalNestingIDF ;
uxSavedInterruptNesting = uxInterruptNesting ;
uxSavedInterruptLevel = uxInterruptLevel ;
prvResumeThread ( pxThreadToResume ) ;
if ( pxThreadToSuspend - > xDying )
{
pthread_exit ( NULL ) ;
}
prvSuspendSelf ( pxThreadToSuspend ) ;
uxCriticalNestingIDF = uxSavedCriticalNestingIDF ;
uxInterruptNesting = uxSavedInterruptNesting ;
uxInterruptLevel = uxSavedInterruptLevel ;
}
}
/*-----------------------------------------------------------*/
static void prvSuspendSelf ( Thread_t * thread )
{
/*
* Suspend this thread by waiting for a pthread_cond_signal event .
*
* A suspended thread must not handle signals ( interrupts ) so
* all signals must be blocked by calling this from :
*
* - Inside a critical section ( vPortEnterCritical ( ) /
* vPortExitCritical ( ) ) .
*
* - From a signal handler that has all signals masked .
*
* - A thread with all signals blocked with pthread_sigmask ( ) .
*/
event_wait ( thread - > ev ) ;
}
/*-----------------------------------------------------------*/
static void prvResumeThread ( Thread_t * xThreadId )
{
if ( pthread_self ( ) ! = xThreadId - > pthread )
{
event_signal ( xThreadId - > ev ) ;
}
}
/*-----------------------------------------------------------*/
static void prvSetupSignalsAndSchedulerPolicy ( void )
{
struct sigaction sigresume , sigtick ;
int iRet ;
hMainThread = pthread_self ( ) ;
/* Initialise common signal masks. */
sigfillset ( & xAllSignals ) ;
/* Don't block SIGINT so this can be used to break into GDB while
* in a critical section . */
sigdelset ( & xAllSignals , SIGINT ) ;
/*
* Block all signals in this thread so all new threads
* inherits this mask .
*
* When a thread is resumed for the first time , all signals
* will be unblocked .
*/
( void ) pthread_sigmask ( SIG_SETMASK , & xAllSignals ,
& xSchedulerOriginalSignalMask ) ;
/* SIG_RESUME is only used with sigwait() so doesn't need a
handler . */
sigresume . sa_flags = 0 ;
sigresume . sa_handler = SIG_IGN ;
sigfillset ( & sigresume . sa_mask ) ;
sigtick . sa_flags = 0 ;
sigtick . sa_handler = vPortSystemTickHandler ;
sigfillset ( & sigtick . sa_mask ) ;
iRet = sigaction ( SIG_RESUME , & sigresume , NULL ) ;
if ( iRet )
{
prvFatalError ( " sigaction " , errno ) ;
}
iRet = sigaction ( SIGALRM , & sigtick , NULL ) ;
if ( iRet )
{
prvFatalError ( " sigaction " , errno ) ;
}
}
/*-----------------------------------------------------------*/
unsigned long ulPortGetRunTime ( void )
{
struct tms xTimes ;
times ( & xTimes ) ;
return ( unsigned long ) xTimes . tms_utime ;
}
/*-----------------------------------------------------------*/
bool portVALID_TCB_MEM ( const void * ptr )
{
return true ;
}
bool portVALID_STACK_MEM ( const void * ptr )
{
return true ;
}
/*-----------------------------------------------------------*/
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED ;
portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED ;
static const char * TAG = " port " ;
/* When configSUPPORT_STATIC_ALLOCATION is set to 1 the application writer can
* use a callback function to optionally provide the memory required by the idle
* and timer tasks . This is the stack that will be used by the timer task . It is
* declared here , as a global , so it can be checked by a test that is implemented
* in a different file . */
StackType_t uxTimerTaskStack [ configTIMER_TASK_STACK_DEPTH ] ;
BaseType_t xPortCheckIfInISR ( void )
{
return ( uxInterruptNesting = = 0 ) ? pdFALSE : pdTRUE ;
}
void app_main ( void ) ;
static void main_task ( void * args )
{
app_main ( ) ;
vTaskDelete ( NULL ) ;
}
int main ( int argc , const char * * argv )
{
// This makes sure that stdio is flushed after each '\n' so that idf.py monitor
// reads the program output on time.
setvbuf ( stdout , NULL , _IOLBF , 0 ) ;
usleep ( 1000 ) ;
portBASE_TYPE res ;
# if ( configNUM_CORES > 1 )
res = xTaskCreateAffinitySet ( & main_task , " main " ,
ESP_TASK_MAIN_STACK , NULL ,
ESP_TASK_MAIN_PRIO , ESP_TASK_MAIN_CORE , NULL ) ;
# else
res = xTaskCreate ( & main_task , " main " ,
ESP_TASK_MAIN_STACK , NULL ,
ESP_TASK_MAIN_PRIO , NULL ) ;
# endif
assert ( res = = pdTRUE ) ;
( void ) res ;
ESP_LOGI ( TAG , " Starting SMP scheduler. " ) ;
vTaskStartScheduler ( ) ;
// This line should never be reached
assert ( false ) ;
}
void esp_vApplicationIdleHook ( void )
{
/* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
* to 1 in FreeRTOSConfig . h . It will be called on each iteration of the idle
* task . It is essential that code added to this hook function never attempts
* to block in any way ( for example , call xQueueReceive ( ) with a block time
* specified , or call vTaskDelay ( ) ) . If application tasks make use of the
* vTaskDelete ( ) API function to delete themselves then it is also important
* that vApplicationIdleHook ( ) is permitted to return to its calling function ,
* because it is the responsibility of the idle task to clean up memory
* allocated by the kernel to any task that has since deleted itself . */
usleep ( 15000 ) ;
}
void esp_vApplicationTickHook ( void ) { }
# if ( configUSE_TICK_HOOK > 0 )
void vApplicationTickHook ( void )
{
esp_vApplicationTickHook ( ) ;
}
# endif
# if ( configSUPPORT_STATIC_ALLOCATION == 1 )
/* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an
* implementation of vApplicationGetIdleTaskMemory ( ) to provide the memory that is
* used by the Idle task . */
void vApplicationGetIdleTaskMemory ( StaticTask_t * * ppxIdleTaskTCBBuffer ,
StackType_t * * ppxIdleTaskStackBuffer ,
uint32_t * pulIdleTaskStackSize )
{
/* If the buffers to be provided to the Idle task are declared inside this
* function then they must be declared static - otherwise they will be allocated on
* the stack and so not exists after this function exits . */
static StaticTask_t xIdleTaskTCB ;
static StackType_t uxIdleTaskStack [ configMINIMAL_STACK_SIZE ] ;
/* Pass out a pointer to the StaticTask_t structure in which the Idle task's
* state will be stored . */
* ppxIdleTaskTCBBuffer = & xIdleTaskTCB ;
/* Pass out the array that will be used as the Idle task's stack. */
* ppxIdleTaskStackBuffer = uxIdleTaskStack ;
/* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
* Note that , as the array is necessarily of type StackType_t ,
* configMINIMAL_STACK_SIZE is specified in words , not bytes . */
* pulIdleTaskStackSize = configMINIMAL_STACK_SIZE ;
}
# endif // configSUPPORT_STATIC_ALLOCATION == 1
/*-----------------------------------------------------------*/
# if ( configSUPPORT_STATIC_ALLOCATION == 1 )
/* configUSE_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
* application must provide an implementation of vApplicationGetTimerTaskMemory ( )
* to provide the memory that is used by the Timer service task . */
void vApplicationGetTimerTaskMemory ( StaticTask_t * * ppxTimerTaskTCBBuffer ,
StackType_t * * ppxTimerTaskStackBuffer ,
uint32_t * pulTimerTaskStackSize )
{
/* If the buffers to be provided to the Timer task are declared inside this
* function then they must be declared static - otherwise they will be allocated on
* the stack and so not exists after this function exits . */
static StaticTask_t xTimerTaskTCB ;
/* Pass out a pointer to the StaticTask_t structure in which the Timer
* task ' s state will be stored . */
* ppxTimerTaskTCBBuffer = & xTimerTaskTCB ;
/* Pass out the array that will be used as the Timer task's stack. */
* ppxTimerTaskStackBuffer = uxTimerTaskStack ;
/* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
* Note that , as the array is necessarily of type StackType_t ,
* configMINIMAL_STACK_SIZE is specified in words , not bytes . */
* pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH ;
}
# endif // configSUPPORT_STATIC_ALLOCATION == 1
void vPortTakeLock ( portMUX_TYPE * lock )
{
spinlock_acquire ( lock , portMUX_NO_TIMEOUT ) ;
}
void vPortReleaseLock ( portMUX_TYPE * lock )
{
spinlock_release ( lock ) ;
}
# define FREERTOS_SMP_MALLOC_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)
void * pvPortMalloc ( size_t xSize )
{
return heap_caps_malloc ( xSize , FREERTOS_SMP_MALLOC_CAPS ) ;
}
void vPortFree ( void * pv )
{
heap_caps_free ( pv ) ;
}
void __attribute__ ( ( weak ) ) vApplicationStackOverflowHook ( TaskHandle_t xTask , char * pcTaskName )
{
# define ERR_STR1 "***ERROR*** A stack overflow in task "
# define ERR_STR2 " has been detected."
const char * str [ ] = { ERR_STR1 , pcTaskName , ERR_STR2 } ;
char buf [ sizeof ( ERR_STR1 ) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof ( ERR_STR2 ) + 1 /* null char */ ] = { 0 } ;
char * dest = buf ;
for ( int i = 0 ; i < sizeof ( str ) / sizeof ( str [ 0 ] ) ; i + + ) {
dest = strcat ( dest , str [ i ] ) ;
}
printf ( " %s \n " , buf ) ;
abort ( ) ;
}
// ------- Thread Local Storage Pointers Deletion Callbacks -------
# if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
void vPortTLSPointersDelCb ( void * pxTCB )
{
/* Typecast pxTCB to StaticTask_t type to access TCB struct members.
* pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB .
*/
StaticTask_t * tcb = ( StaticTask_t * ) pxTCB ;
/* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */
TlsDeleteCallbackFunction_t * pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * ) ( & ( tcb - > pvDummy15 [ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) ) ;
/* We need to iterate over half the depth of the pvThreadLocalStoragePointers area
* to access all TLS pointers and their respective TLS deletion callbacks .
*/
for ( int x = 0 ; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ; x + + ) {
if ( pvThreadLocalStoragePointersDelCallback [ x ] ! = NULL ) { //If del cb is set
// We skip the check if the callback is executable as that is difficult to determine for different
// platforms (compare xtensa and riscv code).
pvThreadLocalStoragePointersDelCallback [ x ] ( x , tcb - > pvDummy15 [ x ] ) ; //Call del cb
}
}
}
# endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS
void vPortCleanUpTCB ( void * pxTCB )
{
# if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS )
/* Call TLS pointers deletion callbacks */
vPortTLSPointersDelCb ( pxTCB ) ;
# endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */
vPortCancelThread ( pxTCB ) ;
}