442 lines
11 KiB
C
442 lines
11 KiB
C
/*----------------------------------------------------------------------------
|
|
* Tencent is pleased to support the open source community by making TencentOS
|
|
* available.
|
|
*
|
|
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* If you have downloaded a copy of the TencentOS binary from Tencent, please
|
|
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
|
|
*
|
|
* If you have downloaded a copy of the TencentOS source code from Tencent,
|
|
* please note that TencentOS source code is licensed under the BSD 3-Clause
|
|
* License, except for the third-party components listed below which are
|
|
* subject to different license terms. Your integration of TencentOS into your
|
|
* own projects may require compliance with the BSD 3-Clause License, as well
|
|
* as the other licenses applicable to the third-party components included
|
|
* within TencentOS.
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include "tos_k.h"
|
|
|
|
#if TOS_CFG_TIMER_EN > 0u
|
|
|
|
__STATIC__ void timer_place(k_timer_t *tmr)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_timer_t *iter = K_NULL;
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
tmr->expires += k_tick_count;
|
|
|
|
TOS_LIST_FOR_EACH_ENTRY(iter, k_timer_t, list, &k_timer_ctl.list) {
|
|
if (tmr->expires < iter->expires) {
|
|
break;
|
|
}
|
|
}
|
|
tos_list_add_tail(&tmr->list, &iter->list);
|
|
|
|
if (k_timer_ctl.list.next == &tmr->list) {
|
|
// we are the first guy now
|
|
k_timer_ctl.next_expires = tmr->expires;
|
|
|
|
#if TOS_CFG_TIMER_AS_PROC == 0u
|
|
if (task_state_is_sleeping(&k_timer_task)) {
|
|
tos_task_delay_abort(&k_timer_task);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if TOS_CFG_TIMER_AS_PROC == 0u
|
|
if (task_state_is_suspended(&k_timer_task)) {
|
|
tos_task_resume(&k_timer_task);
|
|
}
|
|
#endif
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
}
|
|
|
|
__STATIC__ void timer_takeoff(k_timer_t *tmr)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_timer_t *first, *next;
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
first = TOS_LIST_FIRST_ENTRY(&k_timer_ctl.list, k_timer_t, list);
|
|
|
|
tos_list_del(&tmr->list);
|
|
|
|
if (first == tmr) {
|
|
// if the first guy removed, we need to refresh k_timer_ctl.next_expires
|
|
next = TOS_LIST_FIRST_ENTRY_OR_NULL(&tmr->list, k_timer_t, list);
|
|
if (!next) {
|
|
// the only guy removed
|
|
k_timer_ctl.next_expires = TOS_TIME_FOREVER;
|
|
} else {
|
|
k_timer_ctl.next_expires = next->expires;
|
|
}
|
|
}
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
}
|
|
|
|
__STATIC_INLINE__ void timer_reset(k_timer_t *tmr)
|
|
{
|
|
tmr->state = TIMER_STATE_UNUSED;
|
|
tmr->delay = (k_tick_t)0u;
|
|
tmr->expires = (k_tick_t)0u;
|
|
tmr->period = (k_tick_t)0u;
|
|
tmr->opt = (k_opt_t)0u;
|
|
tmr->cb = K_NULL;
|
|
tmr->cb_arg = K_NULL;
|
|
tos_list_init(&tmr->list);
|
|
|
|
TOS_OBJ_DEINIT(tmr);
|
|
}
|
|
|
|
__API__ k_err_t tos_timer_create(k_timer_t *tmr,
|
|
k_tick_t delay,
|
|
k_tick_t period,
|
|
k_timer_callback_t callback,
|
|
void *cb_arg,
|
|
k_opt_t opt)
|
|
{
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_PTR_SANITY_CHECK(callback);
|
|
|
|
if (opt == TOS_OPT_TIMER_PERIODIC && period == (k_tick_t)0u) {
|
|
return K_ERR_TIMER_INVALID_PERIOD;
|
|
}
|
|
|
|
if (opt == TOS_OPT_TIMER_ONESHOT && delay == (k_tick_t)0u) {
|
|
// if you create a oneshot timer and delay 0 to trigger, why don't just call the timer_callback?
|
|
return K_ERR_TIMER_INVALID_DELAY;
|
|
}
|
|
|
|
if (opt != TOS_OPT_TIMER_ONESHOT && opt != TOS_OPT_TIMER_PERIODIC) {
|
|
return K_ERR_TIMER_INVALID_OPT;
|
|
}
|
|
|
|
if (delay == TOS_TIME_FOREVER) {
|
|
return K_ERR_TIMER_DELAY_FOREVER;
|
|
}
|
|
|
|
if (period == TOS_TIME_FOREVER) {
|
|
return K_ERR_TIMER_PERIOD_FOREVER;
|
|
}
|
|
|
|
tmr->state = TIMER_STATE_STOPPED;
|
|
tmr->delay = delay;
|
|
tmr->expires = (k_tick_t)0u;
|
|
tmr->period = period;
|
|
tmr->opt = opt;
|
|
tmr->cb = callback;
|
|
tmr->cb_arg = cb_arg;
|
|
tos_list_init(&tmr->list);
|
|
|
|
TOS_OBJ_INIT(tmr, KNL_OBJ_TYPE_TIMER);
|
|
|
|
#if TOS_CFG_OBJ_DYNAMIC_CREATE_EN > 0u
|
|
knl_object_alloc_set_static(&tmr->knl_obj);
|
|
#endif
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_timer_destroy(k_timer_t *tmr)
|
|
{
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_OBJ_VERIFY(tmr, KNL_OBJ_TYPE_TIMER);
|
|
|
|
#if TOS_CFG_OBJ_DYNAMIC_CREATE_EN > 0u
|
|
if (!knl_object_alloc_is_static(&tmr->knl_obj)) {
|
|
return K_ERR_OBJ_INVALID_ALLOC_TYPE;
|
|
}
|
|
#endif
|
|
|
|
if (tmr->state == TIMER_STATE_UNUSED) {
|
|
return K_ERR_TIMER_INACTIVE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_RUNNING) {
|
|
timer_takeoff(tmr);
|
|
}
|
|
|
|
timer_reset(tmr);
|
|
|
|
#if TOS_CFG_OBJ_DYNAMIC_CREATE_EN > 0u
|
|
knl_object_alloc_reset(&tmr->knl_obj);
|
|
#endif
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
#if TOS_CFG_OBJ_DYNAMIC_CREATE_EN > 0u
|
|
|
|
__API__ k_err_t tos_timer_create_dyn(k_timer_t **tmr,
|
|
k_tick_t delay,
|
|
k_tick_t period,
|
|
k_timer_callback_t callback,
|
|
void *cb_arg,
|
|
k_opt_t opt)
|
|
{
|
|
k_err_t err;
|
|
k_timer_t *the_timer;
|
|
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_PTR_SANITY_CHECK(callback);
|
|
|
|
the_timer = tos_mmheap_calloc(1, sizeof(k_timer_t));
|
|
if (!the_timer) {
|
|
return K_ERR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
err = tos_timer_create(the_timer, delay, period, callback, cb_arg, opt);
|
|
if (err != K_ERR_NONE) {
|
|
tos_mmheap_free(the_timer);
|
|
return err;
|
|
}
|
|
|
|
knl_object_alloc_set_dynamic(&the_timer->knl_obj);
|
|
|
|
*tmr = the_timer;
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_timer_destroy_dyn(k_timer_t *tmr)
|
|
{
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_OBJ_VERIFY(tmr, KNL_OBJ_TYPE_TIMER);
|
|
|
|
if (!knl_object_alloc_is_dynamic(&tmr->knl_obj)) {
|
|
return K_ERR_OBJ_INVALID_ALLOC_TYPE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_UNUSED) {
|
|
return K_ERR_TIMER_INACTIVE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_RUNNING) {
|
|
timer_takeoff(tmr);
|
|
}
|
|
|
|
timer_reset(tmr);
|
|
|
|
tos_mmheap_free(tmr);
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
#endif
|
|
|
|
__API__ k_err_t tos_timer_start(k_timer_t *tmr)
|
|
{
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_OBJ_VERIFY(tmr, KNL_OBJ_TYPE_TIMER);
|
|
|
|
if (tmr->state == TIMER_STATE_UNUSED) {
|
|
return K_ERR_TIMER_INACTIVE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_RUNNING) {
|
|
timer_takeoff(tmr);
|
|
tmr->expires = tmr->delay;
|
|
timer_place(tmr);
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_STOPPED ||
|
|
tmr->state == TIMER_STATE_COMPLETED) {
|
|
tmr->state = TIMER_STATE_RUNNING;
|
|
if (tmr->delay == (k_tick_t)0u) {
|
|
tmr->expires = tmr->period;
|
|
} else {
|
|
tmr->expires = tmr->delay;
|
|
}
|
|
timer_place(tmr);
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
return K_ERR_TIMER_INVALID_STATE;
|
|
}
|
|
|
|
__API__ k_err_t tos_timer_stop(k_timer_t *tmr)
|
|
{
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_OBJ_VERIFY(tmr, KNL_OBJ_TYPE_TIMER);
|
|
|
|
if (tmr->state == TIMER_STATE_UNUSED) {
|
|
return K_ERR_TIMER_INACTIVE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_COMPLETED ||
|
|
tmr->state == TIMER_STATE_STOPPED) {
|
|
return K_ERR_TIMER_STOPPED;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_RUNNING) {
|
|
tmr->state = TIMER_STATE_STOPPED;
|
|
timer_takeoff(tmr);
|
|
}
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__STATIC__ k_err_t timer_change(k_timer_t *tmr, k_tick_t new_val, timer_change_type_t change_type)
|
|
{
|
|
TOS_PTR_SANITY_CHECK(tmr);
|
|
TOS_OBJ_VERIFY(tmr, KNL_OBJ_TYPE_TIMER);
|
|
|
|
if (tmr->state == TIMER_STATE_UNUSED) {
|
|
return K_ERR_TIMER_INACTIVE;
|
|
}
|
|
|
|
if (tmr->state == TIMER_STATE_RUNNING) {
|
|
return K_ERR_TIMER_RUNNING;
|
|
}
|
|
|
|
if (tmr->opt == TOS_OPT_TIMER_ONESHOT &&
|
|
change_type == TIMER_CHANGE_TYPE_DELAY &&
|
|
new_val == (k_tick_t)0u) {
|
|
return K_ERR_TIMER_INVALID_DELAY;
|
|
}
|
|
|
|
if (tmr->opt == TOS_OPT_TIMER_PERIODIC &&
|
|
change_type == TIMER_CHANGE_TYPE_PERIOD &&
|
|
new_val == (k_tick_t)0u) {
|
|
return K_ERR_TIMER_INVALID_PERIOD;
|
|
}
|
|
|
|
if (change_type == TIMER_CHANGE_TYPE_DELAY) {
|
|
tmr->delay = new_val;
|
|
} else {
|
|
tmr->period = new_val;
|
|
}
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_timer_delay_change(k_timer_t *tmr, k_tick_t delay)
|
|
{
|
|
return timer_change(tmr, delay, TIMER_CHANGE_TYPE_DELAY);
|
|
}
|
|
|
|
__API__ k_err_t tos_timer_period_change(k_timer_t *tmr, k_tick_t period)
|
|
{
|
|
return timer_change(tmr, period, TIMER_CHANGE_TYPE_PERIOD);
|
|
}
|
|
|
|
__KNL__ k_tick_t soft_timer_next_expires_get(void)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_tick_t next_expires;
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
if (k_timer_ctl.next_expires == TOS_TIME_FOREVER) {
|
|
next_expires = TOS_TIME_FOREVER;
|
|
} else if (k_timer_ctl.next_expires <= k_tick_count) {
|
|
next_expires = (k_tick_t)0u;
|
|
} else {
|
|
next_expires = k_timer_ctl.next_expires - k_tick_count;
|
|
}
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
return next_expires;
|
|
}
|
|
|
|
#if TOS_CFG_TIMER_AS_PROC > 0u
|
|
|
|
__KNL__ void soft_timer_update(void)
|
|
{
|
|
k_timer_t *tmr, *tmp;
|
|
|
|
if (k_timer_ctl.next_expires > k_tick_count) { // not yet
|
|
return;
|
|
}
|
|
|
|
tos_knl_sched_lock();
|
|
|
|
TOS_LIST_FOR_EACH_ENTRY_SAFE(tmr, tmp, k_timer_t, list, &k_timer_ctl.list) {
|
|
if (tmr->expires > k_tick_count) {
|
|
break;
|
|
}
|
|
|
|
// time's up
|
|
timer_takeoff(tmr);
|
|
|
|
if (tmr->opt == TOS_OPT_TIMER_PERIODIC) {
|
|
tmr->expires = tmr->period;
|
|
timer_place(tmr);
|
|
} else {
|
|
tmr->state = TIMER_STATE_COMPLETED;
|
|
}
|
|
|
|
(*tmr->cb)(tmr->cb_arg);
|
|
}
|
|
|
|
tos_knl_sched_unlock();
|
|
}
|
|
|
|
#else /* TOS_CFG_TIMER_AS_PROC > 0u */
|
|
|
|
__STATIC__ void timer_task_entry(void *arg)
|
|
{
|
|
k_timer_t *tmr, *tmp;
|
|
k_tick_t next_expires;
|
|
|
|
arg = arg; // make compiler happy
|
|
while (K_TRUE) {
|
|
next_expires = soft_timer_next_expires_get();
|
|
if (next_expires == TOS_TIME_FOREVER) {
|
|
tos_task_suspend(K_NULL);
|
|
} else if (next_expires > (k_tick_t)0u) {
|
|
tos_task_delay(next_expires);
|
|
}
|
|
|
|
tos_knl_sched_lock();
|
|
|
|
TOS_LIST_FOR_EACH_ENTRY_SAFE(tmr, tmp, k_timer_t, list, &k_timer_ctl.list) {
|
|
if (tmr->expires > k_tick_count) { // not yet
|
|
break;
|
|
}
|
|
|
|
// time's up
|
|
timer_takeoff(tmr);
|
|
|
|
if (tmr->opt == TOS_OPT_TIMER_PERIODIC) {
|
|
tmr->expires = tmr->period;
|
|
timer_place(tmr);
|
|
} else {
|
|
tmr->state = TIMER_STATE_COMPLETED;
|
|
}
|
|
|
|
(*tmr->cb)(tmr->cb_arg);
|
|
}
|
|
|
|
tos_knl_sched_unlock();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
__KNL__ k_err_t soft_timer_init(void)
|
|
{
|
|
#if TOS_CFG_TIMER_AS_PROC > 0u
|
|
return K_ERR_NONE;
|
|
#else
|
|
return tos_task_create(&k_timer_task,
|
|
"soft_timer",
|
|
timer_task_entry,
|
|
K_NULL,
|
|
k_timer_task_prio,
|
|
k_timer_task_stk_addr,
|
|
k_timer_task_stk_size,
|
|
0);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|