Files
2026-07-13 13:04:25 +08:00

273 lines
6.8 KiB
C

/*
* SPDX-FileCopyrightText: 2016 Wind River Systems, Inc.
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <zephyr/sys/slist.h>
#include <zephyr/logging/log.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "toolchain.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Mutex */
#define K_MUTEX_FOREVER portMAX_DELAY
#define K_MUTEX_SHORT (5000 / portTICK_PERIOD_MS)
struct k_mutex {
SemaphoreHandle_t handle;
};
static inline void k_mutex_create(struct k_mutex *mutex)
{
assert(mutex);
assert(mutex->handle == NULL);
mutex->handle = xSemaphoreCreateRecursiveMutex();
assert(mutex->handle);
}
static inline void k_mutex_delete(struct k_mutex *mutex)
{
assert(mutex);
assert(mutex->handle);
vSemaphoreDelete(mutex->handle);
mutex->handle = NULL;
}
/* Inline log helper with a fixed tag. These functions are inlined into many
* translation units (including api/ wrappers), and not all of them call
* LOG_MODULE_REGISTER, so we cannot reference the per-TU __iso_log_tag
* here. Use a hardcoded "ISO_MUTEX" tag instead, keeping the same level
* gating. */
#if CONFIG_BT_ISO_NO_LOG || (CONFIG_BT_ISO_LOG_LEVEL < BT_ISO_LOG_ERROR)
#define K_MUTEX_LOG_ERR(fmt, args...)
#else
#define K_MUTEX_LOG_ERR(fmt, args...) BT_ISO_LOGE("ISO_MUTEX", fmt, ## args)
#endif
static inline int k_mutex_lock(struct k_mutex *mutex, uint32_t timeout)
{
assert(mutex);
assert(mutex->handle);
if (xSemaphoreTakeRecursive(mutex->handle, timeout) == pdTRUE) {
return 0;
}
#if !CONFIG_BT_ISO_NO_LOG && (CONFIG_BT_ISO_LOG_LEVEL >= BT_ISO_LOG_ERROR)
/* On timeout, dump who timed out and who is currently holding the
* mutex so the caller's log makes wedge diagnosis a one-look job
* instead of a multi-task scavenger hunt.
*/
TaskHandle_t holder = xSemaphoreGetMutexHolder(mutex->handle);
K_MUTEX_LOG_ERR("LockFail[self=%s][holder=%s]",
pcTaskGetName(NULL),
holder ? pcTaskGetName(holder) : "<none>");
#else
K_MUTEX_LOG_ERR("LockFail");
#endif
return -EIO;
}
static inline int k_mutex_unlock(struct k_mutex *mutex)
{
assert(mutex);
assert(mutex->handle);
if (xSemaphoreGiveRecursive(mutex->handle) != pdTRUE) {
K_MUTEX_LOG_ERR("UnlockFail");
return -EIO;
}
return 0;
}
/* Semaphore */
#define K_SEM_FOREVER portMAX_DELAY
#define K_SEM_SHORT (5000 / portTICK_PERIOD_MS)
struct k_sem {
SemaphoreHandle_t handle;
int result;
};
static inline void k_sem_create(struct k_sem *sem)
{
assert(sem);
assert(sem->handle == NULL);
sem->handle = xSemaphoreCreateBinary();
assert(sem->handle);
sem->result = 0;
}
static inline void k_sem_delete(struct k_sem *sem)
{
assert(sem);
assert(sem->handle);
vSemaphoreDelete(sem->handle);
sem->handle = NULL;
}
/* Inline log helper with a fixed tag, mirroring K_MUTEX_LOG_ERR. The sem
* helpers are inlined across many TUs, not all of which call
* LOG_MODULE_REGISTER, so we cannot reference the per-TU __iso_log_tag.
* Hardcode the "ISO_SEM" tag and keep the same level gating. */
#if CONFIG_BT_ISO_NO_LOG || (CONFIG_BT_ISO_LOG_LEVEL < BT_ISO_LOG_ERROR)
#define K_SEM_LOG_ERR(fmt, args...)
#else
#define K_SEM_LOG_ERR(fmt, args...) BT_ISO_LOGE("ISO_SEM", fmt, ## args)
#endif
static inline int k_sem_take(struct k_sem *sem, uint32_t timeout)
{
assert(sem);
assert(sem->handle);
/* Do NOT touch sem->result here. The producer may have already written
* it and called k_sem_give before this take ran (BTU/HCI cb on a
* higher-priority task). Clearing now would race-overwrite the
* producer's value. Caller must k_sem_reset() before initiating the
* async op that will produce the result. */
if (xSemaphoreTake(sem->handle, timeout) == pdTRUE) {
return 0;
}
#if !CONFIG_BT_ISO_NO_LOG && (CONFIG_BT_ISO_LOG_LEVEL >= BT_ISO_LOG_ERROR)
K_SEM_LOG_ERR("TakeFail[self=%s]", pcTaskGetName(NULL));
#else
K_SEM_LOG_ERR("TakeFail");
#endif
return -EIO;
}
static inline int k_sem_give(struct k_sem *sem)
{
assert(sem);
assert(sem->handle);
if (xSemaphoreGive(sem->handle) != pdTRUE) {
K_SEM_LOG_ERR("GiveFail");
return -EIO;
}
return 0;
}
/* Discard any pending give. Used by callers that reuse the same sem across
* cmd/response cycles where a previous cycle timed out — without this, a
* late give from the timed-out cycle would unblock the next take and the
* caller would see uninitialized response data. */
static inline void k_sem_reset(struct k_sem *sem)
{
assert(sem);
assert(sem->handle);
xQueueReset(sem->handle);
sem->result = 0;
}
/* Timer */
typedef uint32_t k_timeout_t;
#define K_NO_WAIT 0
#define K_FOREVER (-1)
#define MSEC_PER_SEC 1000
#define K_USEC(t) (t)
#define K_MSEC(ms) (ms)
#define K_SECONDS(s) K_MSEC((s) * MSEC_PER_SEC)
#define K_MINUTES(m) K_SECONDS((m) * 60)
#define K_HOURS(h) K_MINUTES((h) * 60)
struct k_work;
typedef void (*k_work_handler_t)(struct k_work *work);
struct k_work {
void *timer;
k_work_handler_t handler;
int64_t timeout_us;
void *user_data;
};
struct k_work_sync {
struct k_work work;
};
struct k_work_delayable {
struct k_work work;
};
#define K_WORK_DEFINE(work, work_handler) \
struct k_work work = { \
.handler = work_handler, \
}
#define K_WORK_DELAYABLE_DEFINE(dwork, work_handler) \
struct k_work_delayable dwork = { \
.work.handler = work_handler, \
}
#define K_TIMEOUT_EQ(a, b) ((a) == (b))
typedef void (*k_work_handler_t)(struct k_work *work);
int k_work_submit(struct k_work *work);
bool k_work_is_pending(struct k_work *work);
void k_work_init(struct k_work *work, k_work_handler_t handler);
struct k_work_delayable *k_work_delayable_from_work(struct k_work *work);
void k_work_init_delayable(struct k_work_delayable *dwork,
k_work_handler_t handler);
void k_work_deinit_delayable(struct k_work_delayable *dwork);
int k_work_cancel_delayable(struct k_work_delayable *dwork);
bool k_work_cancel_delayable_sync(struct k_work_delayable *dwork,
struct k_work_sync *sync);
int k_work_schedule(struct k_work_delayable *dwork, k_timeout_t delay);
int k_work_reschedule(struct k_work_delayable *dwork, k_timeout_t delay);
int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_ms);
k_timeout_t k_work_delayable_remaining_get(struct k_work_delayable *dwork);
/* Slist */
struct k_fifo {
sys_slist_t slist;
};
#ifdef __cplusplus
}
#endif