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