| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							| 
									
										
										
										
											2016-10-25 22:12:07 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | //     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							| 
									
										
										
										
											2016-10-25 22:12:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <sys/lock.h>
 | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | #include <stdlib.h>
 | 
					
						
							| 
									
										
										
										
											2016-10-25 22:12:07 +08:00
										 |  |  | #include <sys/reent.h>
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | #include "esp_attr.h"
 | 
					
						
							|  |  |  | #include "soc/cpu.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | #include "freertos/FreeRTOS.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | #include "freertos/semphr.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | #include "freertos/portmacro.h"
 | 
					
						
							|  |  |  | #include "freertos/task.h"
 | 
					
						
							| 
									
										
										
										
											2017-02-27 16:34:19 +08:00
										 |  |  | #include "freertos/portable.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | /* Notes on our newlib lock implementation:
 | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |  * - Use FreeRTOS mutex semaphores as locks. | 
					
						
							|  |  |  |  * - lock_t is int, but we store an xSemaphoreHandle there. | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |  * - Locks are no-ops until the FreeRTOS scheduler is running. | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |  * - Due to this, locks need to be lazily initialised the first time | 
					
						
							|  |  |  |  *   they are acquired. Initialisation/deinitialisation of locks is | 
					
						
							|  |  |  |  *   protected by lock_init_spinlock. | 
					
						
							|  |  |  |  * - Race conditions around lazy initialisation (via lock_acquire) are | 
					
						
							|  |  |  |  *   protected against. | 
					
						
							|  |  |  |  * - Anyone calling lock_close is reponsible for ensuring noone else | 
					
						
							|  |  |  |  *   is holding the lock at this time. | 
					
						
							|  |  |  |  * - Race conditions between lock_close & lock_init (for the same lock) | 
					
						
							|  |  |  |  *   are the responsibility of the caller. | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  | static portMUX_TYPE lock_init_spinlock = portMUX_INITIALIZER_UNLOCKED; | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-29 21:30:28 +10:00
										 |  |  | /* Initialize the given lock by allocating a new mutex semaphore
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |    as the _lock_t value. | 
					
						
							| 
									
										
										
										
											2017-06-29 21:30:28 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |    Called by _lock_init*, also called by _lock_acquire* to lazily initialize locks that might have | 
					
						
							|  |  |  |    been initialised (to zero only) before the RTOS scheduler started. | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | */ | 
					
						
							|  |  |  | static void IRAM_ATTR lock_init_generic(_lock_t *lock, uint8_t mutex_type) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     portENTER_CRITICAL(&lock_init_spinlock); | 
					
						
							|  |  |  |     if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) { | 
					
						
							|  |  |  |         /* nothing to do until the scheduler is running */ | 
					
						
							|  |  |  |         portEXIT_CRITICAL(&lock_init_spinlock); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     if (*lock) { | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |          /* Lock already initialised (either we didn't check earlier,
 | 
					
						
							|  |  |  |           or it got initialised while we were waiting for the | 
					
						
							|  |  |  |           spinlock.) */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         /* Create a new semaphore
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |            this is a bit of an API violation, as we're calling the | 
					
						
							|  |  |  |            private function xQueueCreateMutex(x) directly instead of | 
					
						
							|  |  |  |            the xSemaphoreCreateMutex / xSemaphoreCreateRecursiveMutex | 
					
						
							|  |  |  |            wrapper functions... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            The better alternative would be to pass pointers to one of | 
					
						
							|  |  |  |            the two xSemaphoreCreate___Mutex functions, but as FreeRTOS | 
					
						
							|  |  |  |            implements these as macros instead of inline functions | 
					
						
							|  |  |  |            (*party like it's 1998!*) it's not possible to do this | 
					
						
							|  |  |  |            without writing wrappers. Doing it this way seems much less | 
					
						
							|  |  |  |            spaghetti-like. | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  |         xSemaphoreHandle new_sem = xQueueCreateMutex(mutex_type); | 
					
						
							|  |  |  |         if (!new_sem) { | 
					
						
							|  |  |  |             abort(); /* No more semaphores available or OOM */ | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         *lock = (_lock_t)new_sem; | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     portEXIT_CRITICAL(&lock_init_spinlock); | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | void IRAM_ATTR _lock_init(_lock_t *lock) { | 
					
						
							| 
									
										
										
										
											2017-06-29 21:30:28 +10:00
										 |  |  |     *lock = 0; // In case lock's memory is uninitialized
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     lock_init_generic(lock, queueQUEUE_TYPE_MUTEX); | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | void IRAM_ATTR _lock_init_recursive(_lock_t *lock) { | 
					
						
							| 
									
										
										
										
											2017-06-29 21:30:28 +10:00
										 |  |  |     *lock = 0; // In case lock's memory is uninitialized
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     lock_init_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX); | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  | /* Free the mutex semaphore pointed to by *lock, and zero it out.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |    Note that FreeRTOS doesn't account for deleting mutexes while they | 
					
						
							|  |  |  |    are held, and neither do we... so take care not to delete newlib | 
					
						
							|  |  |  |    locks while they may be held by other tasks! | 
					
						
							| 
									
										
										
										
											2017-06-29 21:30:28 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |    Also, deleting a lock in this way will cause it to be lazily | 
					
						
							|  |  |  |    re-initialised if it is used again. Caller has to avoid doing | 
					
						
							|  |  |  |    this! | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | */ | 
					
						
							|  |  |  | void IRAM_ATTR _lock_close(_lock_t *lock) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     portENTER_CRITICAL(&lock_init_spinlock); | 
					
						
							|  |  |  |     if (*lock) { | 
					
						
							|  |  |  |         xSemaphoreHandle h = (xSemaphoreHandle)(*lock); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | #if (INCLUDE_xSemaphoreGetMutexHolder == 1)
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         configASSERT(xSemaphoreGetMutexHolder(h) == NULL); /* mutex should not be held */ | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         vSemaphoreDelete(h); | 
					
						
							| 
									
										
										
										
											2016-08-25 14:27:36 +08:00
										 |  |  |         *lock = 0; | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     portEXIT_CRITICAL(&lock_init_spinlock); | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-07 15:14:58 +11:00
										 |  |  | void _lock_close_recursive(_lock_t *lock) __attribute__((alias("_lock_close"))); | 
					
						
							| 
									
										
										
										
											2018-09-26 20:46:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  | /* Acquire the mutex semaphore for lock. wait up to delay ticks.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |    mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | static int IRAM_ATTR lock_acquire_generic(_lock_t *lock, uint32_t delay, uint8_t mutex_type) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     xSemaphoreHandle h = (xSemaphoreHandle)(*lock); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     if (!h) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) { | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |             return 0; /* locking is a no-op before scheduler is up, so this "succeeds" */ | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* lazy initialise lock - might have had a static initializer in newlib (that we don't use),
 | 
					
						
							|  |  |  |            or _lock_init might have been called before the scheduler was running... */ | 
					
						
							|  |  |  |         lock_init_generic(lock, mutex_type); | 
					
						
							| 
									
										
										
										
											2016-08-25 14:27:36 +08:00
										 |  |  |         h = (xSemaphoreHandle)(*lock); | 
					
						
							|  |  |  |         configASSERT(h != NULL); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     BaseType_t success; | 
					
						
							| 
									
										
										
										
											2017-02-27 16:34:19 +08:00
										 |  |  |     if (xPortInIsrContext()) { | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         /* In ISR Context */ | 
					
						
							|  |  |  |         if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { | 
					
						
							|  |  |  |             abort(); /* recursive mutexes make no sense in ISR context */ | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         BaseType_t higher_task_woken = false; | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         success = xSemaphoreTakeFromISR(h, &higher_task_woken); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         if (!success && delay > 0) { | 
					
						
							|  |  |  |             abort(); /* Tried to block on mutex from ISR, couldn't... rewrite your program to avoid libc interactions in ISRs! */ | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         if (higher_task_woken) { | 
					
						
							|  |  |  |             portYIELD_FROM_ISR(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         /* In task context */ | 
					
						
							|  |  |  |         if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |             success = xSemaphoreTakeRecursive(h, delay); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |             success = xSemaphoreTake(h, delay); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return (success == pdTRUE) ? 0 : -1; | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | void IRAM_ATTR _lock_acquire(_lock_t *lock) { | 
					
						
							|  |  |  |     lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_MUTEX); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void IRAM_ATTR _lock_acquire_recursive(_lock_t *lock) { | 
					
						
							|  |  |  |     lock_acquire_generic(lock, portMAX_DELAY, queueQUEUE_TYPE_RECURSIVE_MUTEX); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int IRAM_ATTR _lock_try_acquire(_lock_t *lock) { | 
					
						
							|  |  |  |     return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_MUTEX); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int IRAM_ATTR _lock_try_acquire_recursive(_lock_t *lock) { | 
					
						
							|  |  |  |     return lock_acquire_generic(lock, 0, queueQUEUE_TYPE_RECURSIVE_MUTEX); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  | /* Release the mutex semaphore for lock.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |    mutex_type is queueQUEUE_TYPE_RECURSIVE_MUTEX or queueQUEUE_TYPE_MUTEX | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | static void IRAM_ATTR lock_release_generic(_lock_t *lock, uint8_t mutex_type) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |     xSemaphoreHandle h = (xSemaphoreHandle)(*lock); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     if (h == NULL) { | 
					
						
							|  |  |  |         /* This is probably because the scheduler isn't running yet,
 | 
					
						
							|  |  |  |            or the scheduler just started running and some code was | 
					
						
							|  |  |  |            "holding" a not-yet-initialised lock... */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-27 16:34:19 +08:00
										 |  |  |     if (xPortInIsrContext()) { | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { | 
					
						
							|  |  |  |             abort(); /* indicates logic bug, it shouldn't be possible to lock recursively in ISR */ | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         BaseType_t higher_task_woken = false; | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |         xSemaphoreGiveFromISR(h, &higher_task_woken); | 
					
						
							|  |  |  |         if (higher_task_woken) { | 
					
						
							|  |  |  |             portYIELD_FROM_ISR(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         if (mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |             xSemaphoreGiveRecursive(h); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2016-08-24 20:49:06 +08:00
										 |  |  |             xSemaphoreGive(h); | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | void IRAM_ATTR _lock_release(_lock_t *lock) { | 
					
						
							|  |  |  |     lock_release_generic(lock, queueQUEUE_TYPE_MUTEX); | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 17:09:44 +08:00
										 |  |  | void IRAM_ATTR _lock_release_recursive(_lock_t *lock) { | 
					
						
							|  |  |  |     lock_release_generic(lock, queueQUEUE_TYPE_RECURSIVE_MUTEX); | 
					
						
							| 
									
										
										
										
											2016-08-17 23:08:22 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-18 11:47:00 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* No-op function, used to force linking this file,
 | 
					
						
							|  |  |  |    instead of the dummy locks implementation from newlib. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void newlib_include_locks_impl() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } |