#include "mutex.h" #include #include #include "check.h" #include "event_loop_link_i.h" // Internal FreeRTOS member names #define ucQueueType ucDummy9 struct FuriMutex { StaticSemaphore_t container; FuriEventLoopLink event_loop_link; }; // IMPORTANT: container MUST be the FIRST struct member static_assert(offsetof(FuriMutex, container) == 0); FuriMutex* furi_mutex_alloc(FuriMutexType type) { furi_check(!FURI_IS_IRQ_MODE()); FuriMutex* instance = malloc(sizeof(FuriMutex)); SemaphoreHandle_t hMutex; if(type == FuriMutexTypeNormal) { hMutex = xSemaphoreCreateMutexStatic(&instance->container); } else if(type == FuriMutexTypeRecursive) { hMutex = xSemaphoreCreateRecursiveMutexStatic(&instance->container); } else { furi_crash(); } furi_check(hMutex == (SemaphoreHandle_t)instance); return instance; } void furi_mutex_free(FuriMutex* instance) { furi_check(!FURI_IS_IRQ_MODE()); furi_check(instance); // Event Loop must be disconnected furi_check(!instance->event_loop_link.item_in); furi_check(!instance->event_loop_link.item_out); vSemaphoreDelete((SemaphoreHandle_t)instance); free(instance); } FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { furi_check(instance); SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance); const uint8_t mutex_type = instance->container.ucQueueType; FuriStatus stat = FuriStatusOk; if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) { if(timeout != 0U) { stat = FuriStatusErrorTimeout; } else { stat = FuriStatusErrorResource; } } } else if(mutex_type == queueQUEUE_TYPE_MUTEX) { if(xSemaphoreTake(hMutex, timeout) != pdPASS) { if(timeout != 0U) { stat = FuriStatusErrorTimeout; } else { stat = FuriStatusErrorResource; } } } else { furi_crash(); } if(stat == FuriStatusOk) { furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut); } return stat; } FuriStatus furi_mutex_release(FuriMutex* instance) { furi_check(instance); SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance); const uint8_t mutex_type = instance->container.ucQueueType; FuriStatus stat = FuriStatusOk; if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) { if(xSemaphoreGiveRecursive(hMutex) != pdPASS) { stat = FuriStatusErrorResource; } } else if(mutex_type == queueQUEUE_TYPE_MUTEX) { if(xSemaphoreGive(hMutex) != pdPASS) { stat = FuriStatusErrorResource; } } else { furi_crash(); } if(stat == FuriStatusOk) { furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn); } return stat; } FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { furi_check(instance); SemaphoreHandle_t hMutex = (SemaphoreHandle_t)instance; FuriThreadId owner; if(FURI_IS_IRQ_MODE()) { owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex); } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); } return owner; } static FuriEventLoopLink* furi_mutex_event_loop_get_link(FuriEventLoopObject* object) { FuriMutex* instance = object; furi_assert(instance); return &instance->event_loop_link; } static bool furi_mutex_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) { FuriMutex* instance = object; furi_assert(instance); if(event == FuriEventLoopEventIn || event == FuriEventLoopEventOut) { return !furi_mutex_get_owner(instance); } else { furi_crash(); } } const FuriEventLoopContract furi_mutex_event_loop_contract = { .get_link = furi_mutex_event_loop_get_link, .get_level = furi_mutex_event_loop_get_level, };