diff --git a/src/common/pico_sync/include/pico/mutex.h b/src/common/pico_sync/include/pico/mutex.h index bcb3e99e3..1b354ba8a 100644 --- a/src/common/pico_sync/include/pico/mutex.h +++ b/src/common/pico_sync/include/pico/mutex.h @@ -47,6 +47,7 @@ extern "C" { typedef struct __packed_aligned { lock_core_t core; lock_owner_id_t owner; //! owner id LOCK_INVALID_OWNER_ID for unowned + uint32_t save; uint8_t enter_count; //! ownership count #if PICO_MUTEX_ENABLE_SDK120_COMPATIBILITY bool recursive; @@ -60,6 +61,7 @@ typedef struct __packed_aligned { typedef struct __packed_aligned mutex { lock_core_t core; lock_owner_id_t owner; //! owner id LOCK_INVALID_OWNER_ID for unowned + uint32_t save; } mutex_t; #else typedef recursive_mutex_t mutex_t; // they are one and the same when backwards compatible with SDK1.2.0 @@ -114,6 +116,19 @@ void recursive_mutex_enter_blocking(recursive_mutex_t *mtx); */ bool mutex_try_enter(mutex_t *mtx, uint32_t *owner_out); +/*! \brief Attempt to take ownership of a mutex + * \ingroup mutex + * + * If the mutex wasn't owned, this will claim the mutex for the caller and return true. + * Otherwise (if the mutex was already owned) this will return false and the + * caller will NOT own the mutex. + * + * \param mtx Pointer to mutex structure + * \param owner_out If mutex was already owned, and this pointer is non-zero, it will be filled in with the owner id of the current owner of the mutex + * \return true if mutex now owned, false otherwise + */ +bool mutex_try_enter_isr(mutex_t *mtx, uint32_t *owner_out); + /*! \brief Attempt to take ownership of a mutex until the specified time * \ingroup mutex * @@ -142,6 +157,20 @@ bool mutex_try_enter_block_until(mutex_t *mtx, absolute_time_t until); */ bool recursive_mutex_try_enter(recursive_mutex_t *mtx, uint32_t *owner_out); +/*! \brief Attempt to take ownership of a recursive mutex + * \ingroup mutex + * + * If the mutex wasn't owned or was owned by the caller, this will claim the mutex and return true. + * Otherwise (if the mutex was already owned by another owner) this will return false and the + * caller will NOT own the mutex. + * + * \param mtx Pointer to recursive mutex structure + * \param owner_out If mutex was already owned by another owner, and this pointer is non-zero, + * it will be filled in with the owner id of the current owner of the mutex + * \return true if the recursive mutex (now) owned, false otherwise + */ +bool recursive_mutex_try_enter_isr(recursive_mutex_t *mtx, uint32_t *owner_out); + /*! \brief Wait for mutex with timeout * \ingroup mutex * @@ -232,6 +261,13 @@ bool recursive_mutex_enter_block_until(recursive_mutex_t *mtx, absolute_time_t u */ void mutex_exit(mutex_t *mtx); +/*! \brief Release ownership of a mutex + * \ingroup mutex + * + * \param mtx Pointer to mutex structure + */ +void mutex_exit_isr(mutex_t *mtx); + /*! \brief Release ownership of a recursive mutex * \ingroup mutex * @@ -239,6 +275,13 @@ void mutex_exit(mutex_t *mtx); */ void recursive_mutex_exit(recursive_mutex_t *mtx); +/*! \brief Release ownership of a recursive mutex + * \ingroup mutex + * + * \param mtx Pointer to recursive mutex structure + */ +void recursive_mutex_exit_isr(recursive_mutex_t *mtx); + /*! \brief Test for mutex initialized state * \ingroup mutex * diff --git a/src/common/pico_sync/mutex.c b/src/common/pico_sync/mutex.c index 828be68c9..1bcef36e2 100644 --- a/src/common/pico_sync/mutex.c +++ b/src/common/pico_sync/mutex.c @@ -80,6 +80,24 @@ bool __time_critical_func(mutex_try_enter)(mutex_t *mtx, uint32_t *owner_out) { return entered; } +bool __time_critical_func(mutex_try_enter_isr)(mutex_t *mtx, uint32_t *owner_out) { +#if PICO_MUTEX_ENABLE_SDK120_COMPATIBILITY + if (mtx->recursive) { + return recursive_mutex_try_enter_isr(mtx, owner_out); + } +#endif + uint32_t save = spin_lock_blocking(mtx->core.spin_lock); + if (!lock_is_owner_id_valid(mtx->owner)) { + mtx->owner = lock_get_caller_owner_id(); + mtx->save = save; + return true; + } else { + if (owner_out) *owner_out = (uint32_t) mtx->owner; + spin_unlock(mtx->core.spin_lock, save); + return false; + } +} + bool __time_critical_func(mutex_try_enter_block_until)(mutex_t *mtx, absolute_time_t until) { // not using lock_owner_id_t to avoid backwards incompatibility change to mutex_try_enter API static_assert(sizeof(lock_owner_id_t) <= 4, ""); @@ -108,6 +126,22 @@ bool __time_critical_func(recursive_mutex_try_enter)(recursive_mutex_t *mtx, uin return entered; } +bool __time_critical_func(recursive_mutex_try_enter_isr)(recursive_mutex_t *mtx, uint32_t *owner_out) { + lock_owner_id_t caller = lock_get_caller_owner_id(); + uint32_t save = spin_lock_blocking(mtx->core.spin_lock); + if (!lock_is_owner_id_valid(mtx->owner) || mtx->owner == caller) { + mtx->owner = caller; + uint __unused total = ++mtx->enter_count; + assert(total); // check for overflow + mtx->save = save; + return true; + } else { + if (owner_out) *owner_out = (uint32_t) mtx->owner; + spin_unlock(mtx->core.spin_lock, save); + return false; + } +} + bool __time_critical_func(mutex_enter_timeout_ms)(mutex_t *mtx, uint32_t timeout_ms) { return mutex_enter_block_until(mtx, make_timeout_time_ms(timeout_ms)); } @@ -182,6 +216,18 @@ void __time_critical_func(mutex_exit)(mutex_t *mtx) { lock_internal_spin_unlock_with_notify(&mtx->core, save); } +void __time_critical_func(mutex_exit_isr)(mutex_t *mtx) { +#if PICO_MUTEX_ENABLE_SDK120_COMPATIBILITY + if (mtx->recursive) { + recursive_mutex_exit_isr(mtx); + return; + } +#endif + assert(lock_is_owner_id_valid(mtx->owner)); + mtx->owner = LOCK_INVALID_OWNER_ID; + lock_internal_spin_unlock_with_notify(&mtx->core, mtx->save); +} + void __time_critical_func(recursive_mutex_exit)(recursive_mutex_t *mtx) { uint32_t save = spin_lock_blocking(mtx->core.spin_lock); assert(lock_is_owner_id_valid(mtx->owner)); @@ -192,4 +238,15 @@ void __time_critical_func(recursive_mutex_exit)(recursive_mutex_t *mtx) { } else { spin_unlock(mtx->core.spin_lock, save); } +} + +void __time_critical_func(recursive_mutex_exit_isr)(recursive_mutex_t *mtx) { + assert(lock_is_owner_id_valid(mtx->owner)); + assert(mtx->enter_count); + if (!--mtx->enter_count) { + mtx->owner = LOCK_INVALID_OWNER_ID; + lock_internal_spin_unlock_with_notify(&mtx->core, mtx->save); + } else { + spin_unlock(mtx->core.spin_lock, mtx->save); + } } \ No newline at end of file diff --git a/src/rp2_common/pico_stdio_usb/stdio_usb.c b/src/rp2_common/pico_stdio_usb/stdio_usb.c index 0dc8d6d9b..f3d1577a4 100644 --- a/src/rp2_common/pico_stdio_usb/stdio_usb.c +++ b/src/rp2_common/pico_stdio_usb/stdio_usb.c @@ -55,9 +55,9 @@ static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) { } static void low_priority_worker_irq(void) { - if (mutex_try_enter(&stdio_usb_mutex, NULL)) { + if (mutex_try_enter_isr(&stdio_usb_mutex, NULL)) { tud_task(); - mutex_exit(&stdio_usb_mutex); + mutex_exit_isr(&stdio_usb_mutex); } else { // if the mutex is already owned, then we are in non IRQ code in this file. //