2024-10-15 22:25:54 +02:00
/*
* FreeRTOS Kernel V10 .3 .1
* 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 .
*
* http : //www.FreeRTOS.org
* http : //aws.amazon.com/freertos
*
* 1 tab = = 4 spaces !
*/
# include "FreeRTOS.h"
# include "task.h"
# include "croutine.h"
/* Remove the whole file is co-routines are not being used. */
# if( configUSE_CO_ROUTINES != 0 )
/*
* Some kernel aware debuggers require data to be viewed to be global , rather
* than file scope .
*/
# ifdef portREMOVE_STATIC_QUALIFIER
# define static
# endif
/* Lists for ready and blocked co-routines. --------------------*/
static List_t pxReadyCoRoutineLists [ configMAX_CO_ROUTINE_PRIORITIES ] ; /*< Prioritised ready co-routines. */
static List_t xDelayedCoRoutineList1 ; /*< Delayed co-routines. */
static List_t xDelayedCoRoutineList2 ; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */
static List_t * pxDelayedCoRoutineList ; /*< Points to the delayed co-routine list currently being used. */
static List_t * pxOverflowDelayedCoRoutineList ; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */
static List_t xPendingReadyCoRoutineList ; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */
/* Other file private variables. --------------------------------*/
CRCB_t * pxCurrentCoRoutine = NULL ;
static UBaseType_t uxTopCoRoutineReadyPriority = 0 ;
static TickType_t xCoRoutineTickCount = 0 , xLastTickCount = 0 , xPassedTicks = 0 ;
/* The initial state of the co-routine when it is created. */
# define corINITIAL_STATE ( 0 )
/*
* Place the co - routine represented by pxCRCB into the appropriate ready queue
* for the priority . It is inserted at the end of the list .
*
* This macro accesses the co - routine ready lists and therefore must not be
* used from within an ISR .
*/
# define prvAddCoRoutineToReadyQueue( pxCRCB ) \
{ \
if ( pxCRCB - > uxPriority > uxTopCoRoutineReadyPriority ) \
{ \
uxTopCoRoutineReadyPriority = pxCRCB - > uxPriority ; \
} \
vListInsertEnd ( ( List_t * ) & ( pxReadyCoRoutineLists [ pxCRCB - > uxPriority ] ) , & ( pxCRCB - > xGenericListItem ) ) ; \
}
/*
* Utility to ready all the lists used by the scheduler . This is called
* automatically upon the creation of the first co - routine .
*/
static void prvInitialiseCoRoutineLists ( void ) ;
/*
* Co - routines that are readied by an interrupt cannot be placed directly into
* the ready lists ( there is no mutual exclusion ) . Instead they are placed in
* in the pending ready list in order that they can later be moved to the ready
* list by the co - routine scheduler .
*/
static void prvCheckPendingReadyList ( void ) ;
/*
* Macro that looks at the list of co - routines that are currently delayed to
* see if any require waking .
*
* Co - routines are stored in the queue in the order of their wake time -
* meaning once one co - routine has been found whose timer has not expired
* we need not look any further down the list .
*/
static void prvCheckDelayedList ( void ) ;
/*-----------------------------------------------------------*/
BaseType_t xCoRoutineCreate ( crCOROUTINE_CODE pxCoRoutineCode , UBaseType_t uxPriority , UBaseType_t uxIndex )
{
BaseType_t xReturn ;
CRCB_t * pxCoRoutine ;
/* Allocate the memory that will store the co-routine control block. */
pxCoRoutine = ( CRCB_t * ) pvPortMalloc ( sizeof ( CRCB_t ) ) ;
if ( pxCoRoutine )
{
/* If pxCurrentCoRoutine is NULL then this is the first co-routine to
be created and the co - routine data structures need initialising . */
if ( pxCurrentCoRoutine = = NULL )
{
pxCurrentCoRoutine = pxCoRoutine ;
prvInitialiseCoRoutineLists ( ) ;
}
/* Check the priority is within limits. */
if ( uxPriority > = configMAX_CO_ROUTINE_PRIORITIES )
{
uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1 ;
}
/* Fill out the co-routine control block from the function parameters. */
pxCoRoutine - > uxState = corINITIAL_STATE ;
pxCoRoutine - > uxPriority = uxPriority ;
pxCoRoutine - > uxIndex = uxIndex ;
pxCoRoutine - > pxCoRoutineFunction = pxCoRoutineCode ;
/* Initialise all the other co-routine control block parameters. */
vListInitialiseItem ( & ( pxCoRoutine - > xGenericListItem ) ) ;
vListInitialiseItem ( & ( pxCoRoutine - > xEventListItem ) ) ;
/* Set the co-routine control block as a link back from the ListItem_t.
This is so we can get back to the containing CRCB from a generic item
in a list . */
listSET_LIST_ITEM_OWNER ( & ( pxCoRoutine - > xGenericListItem ) , pxCoRoutine ) ;
listSET_LIST_ITEM_OWNER ( & ( pxCoRoutine - > xEventListItem ) , pxCoRoutine ) ;
/* Event lists are always in priority order. */
listSET_LIST_ITEM_VALUE ( & ( pxCoRoutine - > xEventListItem ) , ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) ) ;
/* Now the co-routine has been initialised it can be added to the ready
list at the correct priority . */
prvAddCoRoutineToReadyQueue ( pxCoRoutine ) ;
xReturn = pdPASS ;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ;
}
return xReturn ;
}
/*-----------------------------------------------------------*/
void vCoRoutineAddToDelayedList ( TickType_t xTicksToDelay , List_t * pxEventList )
{
TickType_t xTimeToWake ;
/* Calculate the time to wake - this may overflow but this is
not a problem . */
xTimeToWake = xCoRoutineTickCount + xTicksToDelay ;
/* We must remove ourselves from the ready list before adding
ourselves to the blocked list as the same list item is used for
both lists . */
( void ) uxListRemove ( ( ListItem_t * ) & ( pxCurrentCoRoutine - > xGenericListItem ) ) ;
/* The list item will be inserted in wake time order. */
listSET_LIST_ITEM_VALUE ( & ( pxCurrentCoRoutine - > xGenericListItem ) , xTimeToWake ) ;
if ( xTimeToWake < xCoRoutineTickCount )
{
/* Wake time has overflowed. Place this item in the
overflow list . */
vListInsert ( ( List_t * ) pxOverflowDelayedCoRoutineList , ( ListItem_t * ) & ( pxCurrentCoRoutine - > xGenericListItem ) ) ;
}
else
{
/* The wake time has not overflowed, so we can use the
current block list . */
vListInsert ( ( List_t * ) pxDelayedCoRoutineList , ( ListItem_t * ) & ( pxCurrentCoRoutine - > xGenericListItem ) ) ;
}
if ( pxEventList )
{
/* Also add the co-routine to an event list. If this is done then the
function must be called with interrupts disabled . */
vListInsert ( pxEventList , & ( pxCurrentCoRoutine - > xEventListItem ) ) ;
}
}
/*-----------------------------------------------------------*/
static void prvCheckPendingReadyList ( void )
{
/* Are there any co-routines waiting to get moved to the ready list? These
are co - routines that have been readied by an ISR . The ISR cannot access
the ready lists itself . */
while ( listLIST_IS_EMPTY ( & xPendingReadyCoRoutineList ) = = pdFALSE )
{
CRCB_t * pxUnblockedCRCB ;
/* The pending ready list can be accessed by an ISR. */
portDISABLE_INTERRUPTS ( ) ;
{
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY ( ( & xPendingReadyCoRoutineList ) ) ;
( void ) uxListRemove ( & ( pxUnblockedCRCB - > xEventListItem ) ) ;
}
portENABLE_INTERRUPTS ( ) ;
( void ) uxListRemove ( & ( pxUnblockedCRCB - > xGenericListItem ) ) ;
prvAddCoRoutineToReadyQueue ( pxUnblockedCRCB ) ;
}
}
/*-----------------------------------------------------------*/
static void prvCheckDelayedList ( void )
{
CRCB_t * pxCRCB ;
xPassedTicks = xTaskGetTickCount ( ) - xLastTickCount ;
while ( xPassedTicks )
{
xCoRoutineTickCount + + ;
xPassedTicks - - ;
/* If the tick count has overflowed we need to swap the ready lists. */
if ( xCoRoutineTickCount = = 0 )
{
List_t * pxTemp ;
/* Tick count has overflowed so we need to swap the delay lists. If there are
any items in pxDelayedCoRoutineList here then there is an error ! */
pxTemp = pxDelayedCoRoutineList ;
pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList ;
pxOverflowDelayedCoRoutineList = pxTemp ;
}
/* See if this tick has made a timeout expire. */
while ( listLIST_IS_EMPTY ( pxDelayedCoRoutineList ) = = pdFALSE )
{
pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY ( pxDelayedCoRoutineList ) ;
if ( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE ( & ( pxCRCB - > xGenericListItem ) ) )
{
/* Timeout not yet expired. */
break ;
}
portDISABLE_INTERRUPTS ( ) ;
{
/* The event could have occurred just before this critical
section . If this is the case then the generic list item will
have been moved to the pending ready list and the following
line is still valid . Also the pvContainer parameter will have
been set to NULL so the following lines are also valid . */
( void ) uxListRemove ( & ( pxCRCB - > xGenericListItem ) ) ;
/* Is the co-routine waiting on an event also? */
if ( pxCRCB - > xEventListItem . pxContainer )
{
( void ) uxListRemove ( & ( pxCRCB - > xEventListItem ) ) ;
}
}
portENABLE_INTERRUPTS ( ) ;
prvAddCoRoutineToReadyQueue ( pxCRCB ) ;
}
}
xLastTickCount = xCoRoutineTickCount ;
}
/*-----------------------------------------------------------*/
void vCoRoutineSchedule ( void )
{
/* See if any co-routines readied by events need moving to the ready lists. */
prvCheckPendingReadyList ( ) ;
/* See if any delayed co-routines have timed out. */
prvCheckDelayedList ( ) ;
/* Find the highest priority queue that contains ready co-routines. */
while ( listLIST_IS_EMPTY ( & ( pxReadyCoRoutineLists [ uxTopCoRoutineReadyPriority ] ) ) )
{
if ( uxTopCoRoutineReadyPriority = = 0 )
{
/* No more co-routines to check. */
return ;
}
- - uxTopCoRoutineReadyPriority ;
}
/* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines
of the same priority get an equal share of the processor time . */
listGET_OWNER_OF_NEXT_ENTRY ( pxCurrentCoRoutine , & ( pxReadyCoRoutineLists [ uxTopCoRoutineReadyPriority ] ) ) ;
/* Call the co-routine. */
( pxCurrentCoRoutine - > pxCoRoutineFunction ) ( pxCurrentCoRoutine , pxCurrentCoRoutine - > uxIndex ) ;
return ;
}
/*-----------------------------------------------------------*/
static void prvInitialiseCoRoutineLists ( void )
{
UBaseType_t uxPriority ;
for ( uxPriority = 0 ; uxPriority < configMAX_CO_ROUTINE_PRIORITIES ; uxPriority + + )
{
vListInitialise ( ( List_t * ) & ( pxReadyCoRoutineLists [ uxPriority ] ) ) ;
}
vListInitialise ( ( List_t * ) & xDelayedCoRoutineList1 ) ;
vListInitialise ( ( List_t * ) & xDelayedCoRoutineList2 ) ;
vListInitialise ( ( List_t * ) & xPendingReadyCoRoutineList ) ;
/* Start with pxDelayedCoRoutineList using list1 and the
pxOverflowDelayedCoRoutineList using list2 . */
pxDelayedCoRoutineList = & xDelayedCoRoutineList1 ;
pxOverflowDelayedCoRoutineList = & xDelayedCoRoutineList2 ;
}
/*-----------------------------------------------------------*/
BaseType_t xCoRoutineRemoveFromEventList ( const List_t * pxEventList )
{
CRCB_t * pxUnblockedCRCB ;
BaseType_t xReturn ;
/* This function is called from within an interrupt. It can only access
event lists and the pending ready list . This function assumes that a
check has already been made to ensure pxEventList is not empty . */
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY ( pxEventList ) ;
( void ) uxListRemove ( & ( pxUnblockedCRCB - > xEventListItem ) ) ;
vListInsertEnd ( ( List_t * ) & ( xPendingReadyCoRoutineList ) , & ( pxUnblockedCRCB - > xEventListItem ) ) ;
if ( pxUnblockedCRCB - > uxPriority > = pxCurrentCoRoutine - > uxPriority )
{
xReturn = pdTRUE ;
}
else
{
xReturn = pdFALSE ;
}
return xReturn ;
}
# endif /* configUSE_CO_ROUTINES == 0 */