|
| 1 | +/* |
| 2 | + Copyright (C) 2016-2017 Espressif Shanghai PTE LTD |
| 3 | + Copyright (C) 2015 Real Time Engineers Ltd. |
| 4 | +
|
| 5 | + All rights reserved |
| 6 | +
|
| 7 | + FreeRTOS is free software; you can redistribute it and/or modify it under |
| 8 | + the terms of the GNU General Public License (version 2) as published by the |
| 9 | + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. |
| 10 | +
|
| 11 | + *************************************************************************** |
| 12 | + >>! NOTE: The modification to the GPL is included to allow you to !<< |
| 13 | + >>! distribute a combined work that includes FreeRTOS without being !<< |
| 14 | + >>! obliged to provide the source code for proprietary components !<< |
| 15 | + >>! outside of the FreeRTOS kernel. !<< |
| 16 | + *************************************************************************** |
| 17 | +
|
| 18 | + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY |
| 19 | + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 20 | + FOR A PARTICULAR PURPOSE. Full license text is available on the following |
| 21 | + link: http://www.freertos.org/a00114.html |
| 22 | +*/ |
| 23 | + |
| 24 | + |
| 25 | +/* |
| 26 | + Warning: funky preprocessor hackery ahead. Including these headers will generate two |
| 27 | + functions, which names are defined by the preprocessor macros |
| 28 | + PORTMUX_AQUIRE_MUX_FN_NAME and PORTMUX_RELEASE_MUX_FN_NAME. In order to do the compare |
| 29 | + and exchange function, they will use whatever PORTMUX_COMPARE_SET_FN_NAME resolves to. |
| 30 | +
|
| 31 | + In some scenarios, this header is included *twice* in portmux_impl.h: one time |
| 32 | + for the 'normal' mux code which uses a compare&exchange routine, another time |
| 33 | + to generate code for a second set of these routines that use a second mux |
| 34 | + (in internal ram) to fake a compare&exchange on a variable in external memory. |
| 35 | +*/ |
| 36 | + |
| 37 | + |
| 38 | + |
| 39 | +static inline bool __attribute__((always_inline)) |
| 40 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 41 | +PORTMUX_AQUIRE_MUX_FN_NAME(portMUX_TYPE *mux, int timeout_cycles, const char *fnName, int line) { |
| 42 | +#else |
| 43 | +PORTMUX_AQUIRE_MUX_FN_NAME(portMUX_TYPE *mux, int timeout_cycles) { |
| 44 | +#endif |
| 45 | + |
| 46 | + |
| 47 | +#if !CONFIG_FREERTOS_UNICORE |
| 48 | + uint32_t res; |
| 49 | + portBASE_TYPE coreID, otherCoreID; |
| 50 | + uint32_t ccount_start; |
| 51 | + bool set_timeout = timeout_cycles > portMUX_NO_TIMEOUT; |
| 52 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 53 | + if (!set_timeout) { |
| 54 | + timeout_cycles = 10000; // Always set a timeout in debug mode |
| 55 | + set_timeout = true; |
| 56 | + } |
| 57 | +#endif |
| 58 | + if (set_timeout) { // Timeout |
| 59 | + RSR(CCOUNT, ccount_start); |
| 60 | + } |
| 61 | + |
| 62 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 63 | + uint32_t owner = mux->owner; |
| 64 | + if (owner != portMUX_FREE_VAL && owner != CORE_ID_PRO && owner != CORE_ID_APP) { |
| 65 | + ets_printf("ERROR: vPortCPUAcquireMutex: mux %p is uninitialized (0x%X)! Called from %s line %d.\n", mux, owner, fnName, line); |
| 66 | + mux->owner=portMUX_FREE_VAL; |
| 67 | + } |
| 68 | +#endif |
| 69 | + |
| 70 | + /* Spin until we own the core */ |
| 71 | + |
| 72 | + RSR(PRID, coreID); |
| 73 | + /* Note: coreID is the full 32 bit core ID (CORE_ID_PRO/CORE_ID_APP), |
| 74 | + not the 0/1 value returned by xPortGetCoreID() |
| 75 | + */ |
| 76 | + otherCoreID = CORE_ID_XOR_SWAP ^ coreID; |
| 77 | + do { |
| 78 | + /* mux->owner should be one of portMUX_FREE_VAL, CORE_ID_PRO, |
| 79 | + CORE_ID_APP: |
| 80 | +
|
| 81 | + - If portMUX_FREE_VAL, we want to atomically set to 'coreID'. |
| 82 | + - If "our" coreID, we can drop through immediately. |
| 83 | + - If "otherCoreID", we spin here. |
| 84 | + */ |
| 85 | + res = coreID; |
| 86 | + PORTMUX_COMPARE_SET_FN_NAME(&mux->owner, portMUX_FREE_VAL, &res); |
| 87 | + |
| 88 | + if (res != otherCoreID) { |
| 89 | + break; // mux->owner is "our" coreID |
| 90 | + } |
| 91 | + |
| 92 | + if (set_timeout) { |
| 93 | + uint32_t ccount_now; |
| 94 | + RSR(CCOUNT, ccount_now); |
| 95 | + if (ccount_now - ccount_start > (unsigned)timeout_cycles) { |
| 96 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 97 | + ets_printf("Timeout on mux! last non-recursive lock %s line %d, curr %s line %d\n", mux->lastLockedFn, mux->lastLockedLine, fnName, line); |
| 98 | + ets_printf("Owner 0x%x count %d\n", mux->owner, mux->count); |
| 99 | +#endif |
| 100 | + return false; |
| 101 | + } |
| 102 | + } |
| 103 | + } while (1); |
| 104 | + |
| 105 | + assert(res == coreID || res == portMUX_FREE_VAL); /* any other value implies memory corruption or uninitialized mux */ |
| 106 | + assert((res == portMUX_FREE_VAL) == (mux->count == 0)); /* we're first to lock iff count is zero */ |
| 107 | + assert(mux->count < 0xFF); /* Bad count value implies memory corruption */ |
| 108 | + |
| 109 | + /* now we own it, we can increment the refcount */ |
| 110 | + mux->count++; |
| 111 | + |
| 112 | + |
| 113 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 114 | + if (res==portMUX_FREE_VAL) { //initial lock |
| 115 | + mux->lastLockedFn=fnName; |
| 116 | + mux->lastLockedLine=line; |
| 117 | + } else { |
| 118 | + ets_printf("Recursive lock: count=%d last non-recursive lock %s line %d, curr %s line %d\n", mux->count-1, |
| 119 | + mux->lastLockedFn, mux->lastLockedLine, fnName, line); |
| 120 | + } |
| 121 | +#endif /* CONFIG_FREERTOS_PORTMUX_DEBUG */ |
| 122 | +#endif /* CONFIG_FREERTOS_UNICORE */ |
| 123 | + return true; |
| 124 | +} |
| 125 | + |
| 126 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 127 | +static inline void PORTMUX_RELEASE_MUX_FN_NAME(portMUX_TYPE *mux, const char *fnName, int line) { |
| 128 | +#else |
| 129 | +static inline void PORTMUX_RELEASE_MUX_FN_NAME(portMUX_TYPE *mux) { |
| 130 | +#endif |
| 131 | + |
| 132 | + |
| 133 | +#if !CONFIG_FREERTOS_UNICORE |
| 134 | + portBASE_TYPE coreID; |
| 135 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 136 | + const char *lastLockedFn=mux->lastLockedFn; |
| 137 | + int lastLockedLine=mux->lastLockedLine; |
| 138 | + mux->lastLockedFn=fnName; |
| 139 | + mux->lastLockedLine=line; |
| 140 | + uint32_t owner = mux->owner; |
| 141 | + if (owner != portMUX_FREE_VAL && owner != CORE_ID_PRO && owner != CORE_ID_APP) { |
| 142 | + ets_printf("ERROR: vPortCPUReleaseMutex: mux %p is invalid (0x%x)!\n", mux, mux->owner); |
| 143 | + } |
| 144 | +#endif |
| 145 | + |
| 146 | +#if CONFIG_FREERTOS_PORTMUX_DEBUG || !defined(NDEBUG) |
| 147 | + RSR(PRID, coreID); |
| 148 | +#endif |
| 149 | + |
| 150 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG |
| 151 | + if (coreID != mux->owner) { |
| 152 | + ets_printf("ERROR: vPortCPUReleaseMutex: mux %p was already unlocked!\n", mux); |
| 153 | + ets_printf("Last non-recursive unlock %s line %d, curr unlock %s line %d\n", lastLockedFn, lastLockedLine, fnName, line); |
| 154 | + } |
| 155 | +#endif |
| 156 | + |
| 157 | + assert(coreID == mux->owner); // This is a mutex we didn't lock, or it's corrupt |
| 158 | + assert(mux->count > 0); // Indicates memory corruption |
| 159 | + assert(mux->count < 0x100); // Indicates memory corruption |
| 160 | + |
| 161 | + mux->count--; |
| 162 | + if(mux->count == 0) { |
| 163 | + mux->owner = portMUX_FREE_VAL; |
| 164 | + } |
| 165 | +#ifdef CONFIG_FREERTOS_PORTMUX_DEBUG_RECURSIVE |
| 166 | + else { |
| 167 | + ets_printf("Recursive unlock: count=%d last locked %s line %d, curr %s line %d\n", mux->count, lastLockedFn, lastLockedLine, fnName, line); |
| 168 | + } |
| 169 | +#endif |
| 170 | +#endif //!CONFIG_FREERTOS_UNICORE |
| 171 | +} |
0 commit comments