1 /* 2 * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 8 /* 9 * Warning: funky preprocessor hackery ahead. Including these headers will generate two 10 * functions, which names are defined by the preprocessor macros 11 * PORTMUX_AQUIRE_MUX_FN_NAME and PORTMUX_RELEASE_MUX_FN_NAME. In order to do the compare 12 * and exchange function, they will use whatever PORTMUX_COMPARE_SET_FN_NAME resolves to. 13 * 14 * In some scenarios, this header is included *twice* in portmux_impl.h: one time 15 * for the 'normal' mux code which uses a compare&exchange routine, another time 16 * to generate code for a second set of these routines that use a second mux 17 * (in internal ram) to fake a compare&exchange on a variable in external memory. 18 */ 19 20 21 22 static inline bool __attribute__( ( always_inline ) ) 23 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG PORTMUX_AQUIRE_MUX_FN_NAME(portMUX_TYPE * mux,int timeout_cycles,const char * fnName,int line)24 PORTMUX_AQUIRE_MUX_FN_NAME( portMUX_TYPE * mux, 25 int timeout_cycles, 26 const char * fnName, 27 int line ) 28 { 29 #else 30 PORTMUX_AQUIRE_MUX_FN_NAME( portMUX_TYPE * mux, int timeout_cycles ) 31 { 32 #endif 33 34 35 #if !CONFIG_FREERTOS_UNICORE 36 uint32_t res; 37 portBASE_TYPE coreID, otherCoreID; 38 uint32_t ccount_start; 39 bool set_timeout = timeout_cycles > portMUX_NO_TIMEOUT; 40 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 41 if( !set_timeout ) 42 { 43 timeout_cycles = 10000; /* Always set a timeout in debug mode */ 44 set_timeout = true; 45 } 46 #endif 47 48 if( set_timeout ) /* Timeout */ 49 { 50 RSR( CCOUNT, ccount_start ); 51 } 52 53 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 54 uint32_t owner = mux->owner; 55 56 #if ( ESP_IDF_VERSION < ESP_IDF_VERSION_VAL( 4, 2, 0 ) ) 57 if( ( owner != portMUX_FREE_VAL ) && ( owner != CORE_ID_PRO ) && ( owner != CORE_ID_APP ) ) 58 #else 59 if( ( owner != portMUX_FREE_VAL ) && ( owner != CORE_ID_REGVAL_PRO ) && ( owner != CORE_ID_REGVAL_APP ) ) 60 #endif 61 { 62 ets_printf( "ERROR: vPortCPUAcquireMutex: mux %p is uninitialized (0x%X)! Called from %s line %d.\n", mux, owner, fnName, line ); 63 mux->owner = portMUX_FREE_VAL; 64 } 65 #endif /* ifdef CONFIG_FREERTOS_PORTMUX_DEBUG */ 66 67 /* Spin until we own the core */ 68 69 RSR( PRID, coreID ); 70 71 /* Note: coreID is the full 32 bit core ID (CORE_ID_PRO/CORE_ID_APP), 72 * not the 0/1 value returned by xPortGetCoreID() 73 */ 74 #if ( ESP_IDF_VERSION < ESP_IDF_VERSION_VAL( 4, 2, 0 ) ) 75 otherCoreID = CORE_ID_XOR_SWAP ^ coreID; 76 #else 77 otherCoreID = CORE_ID_REGVAL_XOR_SWAP ^ coreID; 78 #endif 79 80 do 81 { 82 /* mux->owner should be one of portMUX_FREE_VAL, CORE_ID_PRO, 83 * CORE_ID_APP: 84 * 85 * - If portMUX_FREE_VAL, we want to atomically set to 'coreID'. 86 * - If "our" coreID, we can drop through immediately. 87 * - If "otherCoreID", we spin here. 88 */ 89 res = coreID; 90 PORTMUX_COMPARE_SET_FN_NAME( &mux->owner, portMUX_FREE_VAL, &res ); 91 92 if( res != otherCoreID ) 93 { 94 break; /* mux->owner is "our" coreID */ 95 } 96 97 if( set_timeout ) 98 { 99 uint32_t ccount_now; 100 RSR( CCOUNT, ccount_now ); 101 102 if( ccount_now - ccount_start > ( unsigned ) timeout_cycles ) 103 { 104 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 105 ets_printf( "Timeout on mux! last non-recursive lock %s line %d, curr %s line %d\n", mux->lastLockedFn, mux->lastLockedLine, fnName, line ); 106 ets_printf( "Owner 0x%x count %d\n", mux->owner, mux->count ); 107 #endif 108 return false; 109 } 110 } 111 } while( 1 ); 112 113 assert( res == coreID || res == portMUX_FREE_VAL ); /* any other value implies memory corruption or uninitialized mux */ 114 assert( ( res == portMUX_FREE_VAL ) == ( mux->count == 0 ) ); /* we're first to lock iff count is zero */ 115 assert( mux->count < 0xFF ); /* Bad count value implies memory corruption */ 116 117 /* now we own it, we can increment the refcount */ 118 mux->count++; 119 120 121 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 122 if( res == portMUX_FREE_VAL ) /*initial lock */ 123 { 124 mux->lastLockedFn = fnName; 125 mux->lastLockedLine = line; 126 } 127 else 128 { 129 ets_printf( "Recursive lock: count=%d last non-recursive lock %s line %d, curr %s line %d\n", mux->count - 1, 130 mux->lastLockedFn, mux->lastLockedLine, fnName, line ); 131 } 132 #endif /* CONFIG_FREERTOS_PORTMUX_DEBUG */ 133 #endif /* CONFIG_FREERTOS_UNICORE */ 134 return true; 135 } 136 137 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 138 static inline void PORTMUX_RELEASE_MUX_FN_NAME( portMUX_TYPE * mux, 139 const char * fnName, 140 int line ) 141 { 142 #else 143 static inline void PORTMUX_RELEASE_MUX_FN_NAME( portMUX_TYPE * mux ) 144 { 145 #endif 146 147 148 #if !CONFIG_FREERTOS_UNICORE 149 portBASE_TYPE coreID; 150 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 151 const char * lastLockedFn = mux->lastLockedFn; 152 int lastLockedLine = mux->lastLockedLine; 153 mux->lastLockedFn = fnName; 154 mux->lastLockedLine = line; 155 uint32_t owner = mux->owner; 156 157 #if ( ESP_IDF_VERSION < ESP_IDF_VERSION_VAL( 4, 2, 0 ) ) 158 if( ( owner != portMUX_FREE_VAL ) && ( owner != CORE_ID_PRO ) && ( owner != CORE_ID_APP ) ) 159 #else 160 if( ( owner != portMUX_FREE_VAL ) && ( owner != CORE_ID_REGVAL_PRO ) && ( owner != CORE_ID_REGVAL_APP ) ) 161 #endif 162 { 163 ets_printf( "ERROR: vPortCPUReleaseMutex: mux %p is invalid (0x%x)!\n", mux, mux->owner ); 164 } 165 #endif /* ifdef CONFIG_FREERTOS_PORTMUX_DEBUG */ 166 167 #if CONFIG_FREERTOS_PORTMUX_DEBUG || !defined( NDEBUG ) 168 RSR( PRID, coreID ); 169 #endif 170 171 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG 172 if( coreID != mux->owner ) 173 { 174 ets_printf( "ERROR: vPortCPUReleaseMutex: mux %p was already unlocked!\n", mux ); 175 ets_printf( "Last non-recursive unlock %s line %d, curr unlock %s line %d\n", lastLockedFn, lastLockedLine, fnName, line ); 176 } 177 #endif 178 179 assert( coreID == mux->owner ); /* This is a mutex we didn't lock, or it's corrupt */ 180 181 mux->count--; 182 183 if( mux->count == 0 ) 184 { 185 mux->owner = portMUX_FREE_VAL; 186 } 187 else 188 { 189 assert( mux->count < 0x100 ); /* Indicates memory corruption */ 190 #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE 191 ets_printf( "Recursive unlock: count=%d last locked %s line %d, curr %s line %d\n", mux->count, lastLockedFn, lastLockedLine, fnName, line ); 192 #endif 193 } 194 #endif //!CONFIG_FREERTOS_UNICORE 195 } 196