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

313 lines
6.2 KiB
C

/*
* SPDX-FileCopyrightText: 2016 Intel Corporation
* SPDX-FileCopyrightText: 2016 Wind River Systems, Inc.
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "esp_timer.h"
#include <zephyr/kernel.h>
#include <zephyr/autoconf.h>
#include <zephyr/toolchain.h>
#include <zephyr/logging/log.h>
#include "common/host.h"
LOG_MODULE_REGISTER(ISO_TIMER, CONFIG_BT_ISO_LOG_LEVEL);
static void iso_timer_cb(void *arg)
{
struct k_work *work = arg;
int err;
assert(work);
assert(work->timer);
assert(work->handler);
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_TIMER_EVENT, work, 0);
if (err) {
LOG_ERR("IsoTimerPostFail[%d]", err);
}
}
int k_work_submit(struct k_work *work)
{
LOG_DBG("WorkSubmit[%p]", work);
assert(work);
if (work->handler == NULL) {
LOG_WRN("WorkHdlrNull");
return -EINVAL;
}
work->handler(work);
return 0;
}
int k_work_submit_safe(struct k_work *work)
{
int err;
bt_le_host_lock();
err = k_work_submit(work);
bt_le_host_unlock();
return err;
}
bool k_work_is_pending(struct k_work *work)
{
bool is_pending;
LOG_DBG("WorkIsPending[%p]", work);
assert(work);
if (work->handler == NULL) {
LOG_WRN("WorkHdlrNull");
return false;
}
is_pending = (work->timer ? esp_timer_is_active(work->timer) : false);
LOG_DBG("%sPending", is_pending ? "Is" : "Not");
return is_pending;
}
void k_work_init(struct k_work *work, k_work_handler_t handler)
{
LOG_DBG("WorkInit[%p]", work);
assert(work);
work->handler = handler;
}
struct k_work_delayable *k_work_delayable_from_work(struct k_work *work)
{
LOG_DBG("DworkFromWork[%p]", work);
assert(work);
return (struct k_work_delayable *)work;
}
void k_work_init_delayable(struct k_work_delayable *dwork,
k_work_handler_t handler)
{
LOG_DBG("DworkInit[%p]", dwork);
assert(dwork);
const esp_timer_create_args_t timer_args = {
.callback = &iso_timer_cb,
.arg = &dwork->work,
.name = "iso_timer",
};
int err;
if (dwork->work.timer) {
LOG_WRN("TimerCreated");
return;
}
err = esp_timer_create(&timer_args, (esp_timer_handle_t *)&dwork->work.timer);
if (err) {
LOG_ERR("CreateTimerFail[%d]", err);
/* Reset handler so the object remains in a clean uninitialized state */
dwork->work.handler = NULL;
return;
}
dwork->work.handler = handler;
}
void k_work_deinit_delayable(struct k_work_delayable *dwork)
{
int err;
LOG_DBG("DworkDeinit[%p]", dwork);
assert(dwork);
if (dwork->work.timer == NULL) {
LOG_INF("TimerNotCreated");
return;
}
err = esp_timer_delete(dwork->work.timer);
if (err) {
LOG_ERR("DeleteTimerFail[%d]", err);
return;
}
memset(&dwork->work, 0, sizeof(dwork->work));
}
int k_work_cancel_delayable(struct k_work_delayable *dwork)
{
LOG_DBG("DworkCancel[%p]", dwork);
assert(dwork);
if (dwork->work.timer == NULL) {
LOG_INF("TimerNotCreated");
return -EINVAL;
}
esp_timer_stop(dwork->work.timer);
return 0;
}
bool k_work_cancel_delayable_sync(struct k_work_delayable *dwork,
struct k_work_sync *sync)
{
LOG_DBG("DworkCancelSync[%p]", dwork);
assert(dwork);
ARG_UNUSED(sync);
if (dwork->work.timer == NULL) {
LOG_INF("TimerNotCreated");
return false;
}
esp_timer_stop(dwork->work.timer);
/* TODO: check the return result */
return false;
}
int k_work_schedule(struct k_work_delayable *dwork, k_timeout_t ms)
{
int err;
LOG_DBG("WorkSchedule[%p][%u]", dwork, ms);
assert(dwork);
if (dwork->work.timer == NULL) {
LOG_WRN("TimerNotCreated");
return -EINVAL;
}
esp_timer_stop(dwork->work.timer);
dwork->work.timeout_us = esp_timer_get_time() + (int64_t)ms * 1000;
if (ms == K_NO_WAIT) {
k_work_submit(&dwork->work);
return 0;
}
err = esp_timer_start_once(dwork->work.timer, ms * 1000);
if (err) {
LOG_ERR("StartTimerFail[%d]", err);
return -EIO;
}
return 0;
}
int k_work_reschedule(struct k_work_delayable *dwork, k_timeout_t ms)
{
int err;
LOG_DBG("WorkReschedule[%p][%u]", dwork, ms);
assert(dwork);
if (dwork->work.timer == NULL) {
LOG_WRN("TimerNotCreated");
return -EINVAL;
}
esp_timer_stop(dwork->work.timer);
dwork->work.timeout_us = esp_timer_get_time() + (int64_t)ms * 1000;
if (ms == K_NO_WAIT) {
k_work_submit(&dwork->work);
return 0;
}
err = esp_timer_start_once(dwork->work.timer, ms * 1000);
if (err) {
LOG_ERR("RestartTimerFail[%d]", err);
return -EIO;
}
return 0;
}
int k_work_schedule_periodic(struct k_work_delayable *dwork, k_timeout_t period_ms)
{
int err;
LOG_DBG("WorkSchedulePeriodic[%p][%u]", dwork, period_ms);
assert(dwork);
assert(period_ms > 0);
if (dwork->work.timer == NULL) {
LOG_WRN("TimerNotCreated");
return -EINVAL;
}
esp_timer_stop(dwork->work.timer);
err = esp_timer_start_periodic(dwork->work.timer, (uint64_t)period_ms * 1000);
if (err) {
LOG_ERR("StartPeriodicTimerFail[%d]", err);
return -EIO;
}
return 0;
}
k_timeout_t k_work_delayable_remaining_get(struct k_work_delayable *dwork)
{
k_timeout_t timeout;
int64_t delta_us;
LOG_DBG("DworkRemainingGet[%p]", dwork);
assert(dwork);
if (dwork->work.timer == NULL) {
LOG_WRN("TimerNotCreated");
return 0;
}
if (dwork->work.timeout_us == 0) {
LOG_DBG("WorkTimeoutZero");
return 0;
}
delta_us = dwork->work.timeout_us - esp_timer_get_time();
timeout = (delta_us > 0 ? (k_timeout_t)(delta_us / 1000) : 0);
LOG_DBG("Timeout[%u]", timeout);
return timeout;
}
void bt_le_timer_handle_event(void *arg)
{
struct k_work *work = arg;
k_work_submit_safe(work);
}