/* | |
* Copyright (C) 2016-2017 Espressif Shanghai PTE LTD | |
* Copyright (C) 2015 Real Time Engineers Ltd. | |
* | |
* All rights reserved | |
* | |
* FreeRTOS is free software; you can redistribute it and/or modify it under | |
* the terms of the GNU General Public License (version 2) as published by the | |
* Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. | |
* | |
*************************************************************************** | |
* >>! NOTE: The modification to the GPL is included to allow you to !<< | |
* >>! distribute a combined work that includes FreeRTOS without being !<< | |
* >>! obliged to provide the source code for proprietary components !<< | |
* >>! outside of the FreeRTOS kernel. !<< | |
*************************************************************************** | |
* | |
* FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY | |
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
* FOR A PARTICULAR PURPOSE. Full license text is available on the following | |
* link: http://www.freertos.org/a00114.html | |
*/ | |
/* | |
* Warning: funky preprocessor hackery ahead. Including these headers will generate two | |
* functions, which names are defined by the preprocessor macros | |
* PORTMUX_AQUIRE_MUX_FN_NAME and PORTMUX_RELEASE_MUX_FN_NAME. In order to do the compare | |
* and exchange function, they will use whatever PORTMUX_COMPARE_SET_FN_NAME resolves to. | |
* | |
* In some scenarios, this header is included *twice* in portmux_impl.h: one time | |
* for the 'normal' mux code which uses a compare&exchange routine, another time | |
* to generate code for a second set of these routines that use a second mux | |
* (in internal ram) to fake a compare&exchange on a variable in external memory. | |
*/ | |
static inline bool __attribute__( ( always_inline ) ) | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
PORTMUX_AQUIRE_MUX_FN_NAME( portMUX_TYPE * mux, | |
int timeout_cycles, | |
const char * fnName, | |
int line ) | |
{ | |
#else | |
PORTMUX_AQUIRE_MUX_FN_NAME( portMUX_TYPE * mux, int timeout_cycles ) | |
{ | |
#endif | |
#if !CONFIG_FREERTOS_UNICORE | |
uint32_t res; | |
portBASE_TYPE coreID, otherCoreID; | |
uint32_t ccount_start; | |
bool set_timeout = timeout_cycles > portMUX_NO_TIMEOUT; | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
if( !set_timeout ) | |
{ | |
timeout_cycles = 10000; /* Always set a timeout in debug mode */ | |
set_timeout = true; | |
} | |
#endif | |
if( set_timeout ) /* Timeout */ | |
{ | |
RSR( CCOUNT, ccount_start ); | |
} | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
uint32_t owner = mux->owner; | |
if( ( owner != portMUX_FREE_VAL ) && ( owner != CORE_ID_PRO ) && ( owner != CORE_ID_APP ) ) | |
{ | |
ets_printf( "ERROR: vPortCPUAcquireMutex: mux %p is uninitialized (0x%X)! Called from %s line %d.\n", mux, owner, fnName, line ); | |
mux->owner = portMUX_FREE_VAL; | |
} | |
#endif | |
/* Spin until we own the core */ | |
RSR( PRID, coreID ); | |
/* Note: coreID is the full 32 bit core ID (CORE_ID_PRO/CORE_ID_APP), | |
* not the 0/1 value returned by xPortGetCoreID() | |
*/ | |
otherCoreID = CORE_ID_XOR_SWAP ^ coreID; | |
do | |
{ | |
/* mux->owner should be one of portMUX_FREE_VAL, CORE_ID_PRO, | |
* CORE_ID_APP: | |
* | |
* - If portMUX_FREE_VAL, we want to atomically set to 'coreID'. | |
* - If "our" coreID, we can drop through immediately. | |
* - If "otherCoreID", we spin here. | |
*/ | |
res = coreID; | |
PORTMUX_COMPARE_SET_FN_NAME( &mux->owner, portMUX_FREE_VAL, &res ); | |
if( res != otherCoreID ) | |
{ | |
break; /* mux->owner is "our" coreID */ | |
} | |
if( set_timeout ) | |
{ | |
uint32_t ccount_now; | |
RSR( CCOUNT, ccount_now ); | |
if( ccount_now - ccount_start > ( unsigned ) timeout_cycles ) | |
{ | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
ets_printf( "Timeout on mux! last non-recursive lock %s line %d, curr %s line %d\n", mux->lastLockedFn, mux->lastLockedLine, fnName, line ); | |
ets_printf( "Owner 0x%x count %d\n", mux->owner, mux->count ); | |
#endif | |
return false; | |
} | |
} | |
} while( 1 ); | |
assert( res == coreID || res == portMUX_FREE_VAL ); /* any other value implies memory corruption or uninitialized mux */ | |
assert( ( res == portMUX_FREE_VAL ) == ( mux->count == 0 ) ); /* we're first to lock iff count is zero */ | |
assert( mux->count < 0xFF ); /* Bad count value implies memory corruption */ | |
/* now we own it, we can increment the refcount */ | |
mux->count++; | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
if( res == portMUX_FREE_VAL ) /*initial lock */ | |
{ | |
mux->lastLockedFn = fnName; | |
mux->lastLockedLine = line; | |
} | |
else | |
{ | |
ets_printf( "Recursive lock: count=%d last non-recursive lock %s line %d, curr %s line %d\n", mux->count - 1, | |
mux->lastLockedFn, mux->lastLockedLine, fnName, line ); | |
} | |
#endif /* CONFIG_FREERTOS_PORTMUX_DEBUG */ | |
#endif /* CONFIG_FREERTOS_UNICORE */ | |
return true; | |
} | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
static inline void PORTMUX_RELEASE_MUX_FN_NAME( portMUX_TYPE * mux, | |
const char * fnName, | |
int line ) | |
{ | |
#else | |
static inline void PORTMUX_RELEASE_MUX_FN_NAME( portMUX_TYPE * mux ) | |
{ | |
#endif | |
#if !CONFIG_FREERTOS_UNICORE | |
portBASE_TYPE coreID; | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
const char * lastLockedFn = mux->lastLockedFn; | |
int lastLockedLine = mux->lastLockedLine; | |
mux->lastLockedFn = fnName; | |
mux->lastLockedLine = line; | |
uint32_t owner = mux->owner; | |
if( ( owner != portMUX_FREE_VAL ) && ( owner != CORE_ID_PRO ) && ( owner != CORE_ID_APP ) ) | |
{ | |
ets_printf( "ERROR: vPortCPUReleaseMutex: mux %p is invalid (0x%x)!\n", mux, mux->owner ); | |
} | |
#endif /* ifdef CONFIG_FREERTOS_PORTMUX_DEBUG */ | |
#if CONFIG_FREERTOS_PORTMUX_DEBUG || !defined( NDEBUG ) | |
RSR( PRID, coreID ); | |
#endif | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG | |
if( coreID != mux->owner ) | |
{ | |
ets_printf( "ERROR: vPortCPUReleaseMutex: mux %p was already unlocked!\n", mux ); | |
ets_printf( "Last non-recursive unlock %s line %d, curr unlock %s line %d\n", lastLockedFn, lastLockedLine, fnName, line ); | |
} | |
#endif | |
assert( coreID == mux->owner ); /* This is a mutex we didn't lock, or it's corrupt */ | |
mux->count--; | |
if( mux->count == 0 ) | |
{ | |
mux->owner = portMUX_FREE_VAL; | |
} | |
else | |
{ | |
assert( mux->count < 0x100 ); /* Indicates memory corruption */ | |
#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE | |
ets_printf( "Recursive unlock: count=%d last locked %s line %d, curr %s line %d\n", mux->count, lastLockedFn, lastLockedLine, fnName, line ); | |
#endif | |
} | |
#endif //!CONFIG_FREERTOS_UNICORE | |
} |