/* * Copyright (c) 2018 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Kernel thread timer support * * This module provides thread timer support. */ #include #include #include #include #undef THREAD_TIMER_DEBUG //#define CONFIG_THREAD_TIMER_DEBUG //#define THREAD_TIMER_DEBUG #ifdef THREAD_TIMER_DEBUG #include #define TT_DEBUG(fmt, ...) printk("[%d] thread %p: " fmt, k_uptime_get_32(), \ (void *)_current, ##__VA_ARGS__) #else #define TT_DEBUG(fmt, ...) #endif #define compare_time(a, b) ((int)((u32_t)(a) - (u32_t)(b))) #ifdef CONFIG_THREAD_TIMER_DEBUG static void _dump_thread_timer(struct thread_timer *ttimer) { printk("timer %p, prev: %p, next: %p\n", ttimer, ttimer->node.prev, ttimer->node.next); printk("\tthread: %p, period %d ms, delay %d ms\n", _current, ttimer->period, ttimer->duration); printk("\texpiry_time: %u, expiry_fn: %p, arg %p\n\n", ttimer->expiry_time, ttimer->expiry_fn, ttimer->expiry_fn_arg); } void _dump_thread_timer_q(void) { sys_dlist_t *thread_timer_q = &_current->thread_timer_q; struct thread_timer *ttimer; printk("thread: %p, thread_timer_q: %p, head: %p, tail: %p\n", _current, thread_timer_q, thread_timer_q->head, thread_timer_q->tail); SYS_DLIST_FOR_EACH_CONTAINER(thread_timer_q, ttimer, node) { _dump_thread_timer(ttimer); } } #endif static void _thread_timer_remove(struct k_thread *thread, struct thread_timer *ttimer) { struct thread_timer *in_q, *next; SYS_DLIST_FOR_EACH_CONTAINER_SAFE(&thread->thread_timer_q, in_q, next, node) { if (ttimer == in_q) { sys_dlist_remove(&in_q->node); return; } } } static void _thread_timer_insert(struct k_thread *thread, struct thread_timer *ttimer, u32_t expiry_time) { sys_dlist_t *thread_timer_q = &thread->thread_timer_q; struct thread_timer *in_q; SYS_DLIST_FOR_EACH_CONTAINER(thread_timer_q, in_q, node) { if (compare_time(ttimer->expiry_time, in_q->expiry_time) < 0) { sys_dlist_insert(&in_q->node, &ttimer->node); return; } } sys_dlist_append(thread_timer_q, &ttimer->node); } void thread_timer_init(struct thread_timer *ttimer, thread_timer_expiry_t expiry_fn, void *expiry_fn_arg) { __ASSERT(ttimer != NULL, ""); if (!ttimer) { return; } TT_DEBUG("timer %p: init func %p arg 0x%x\n", ttimer, ttimer->expiry_fn, ttimer->expiry_fn_arg); /* remove thread timer if already submited */ _thread_timer_remove(_current, ttimer); memset(ttimer, 0, sizeof(struct thread_timer)); ttimer->expiry_fn = expiry_fn; ttimer->expiry_fn_arg = expiry_fn_arg; sys_dlist_init(&ttimer->node); } void thread_timer_start(struct thread_timer *ttimer, s32_t duration, s32_t period) { u32_t cur_time; if (!ttimer) { return; } __ASSERT((ttimer != NULL) && (ttimer->expiry_fn != NULL), ""); _thread_timer_remove(_current, ttimer); cur_time = k_uptime_get_32(); ttimer->expiry_time = cur_time + duration; ttimer->period = period; ttimer->duration = duration; TT_DEBUG("timer %p: start duration %d period %d, expiry_time %d\n", ttimer, duration, period, ttimer->expiry_time); _thread_timer_insert(_current, ttimer, ttimer->expiry_time); } void thread_timer_stop(struct thread_timer *ttimer) { __ASSERT(ttimer != NULL, ""); TT_DEBUG("timer %p: stop\n", ttimer); _thread_timer_remove(_current, ttimer); } bool thread_timer_is_running(struct thread_timer *ttimer) { struct thread_timer *in_q; __ASSERT(ttimer != NULL, ""); SYS_DLIST_FOR_EACH_CONTAINER(&_current->thread_timer_q, in_q, node) { if (ttimer == in_q) return true; } return false; } int thread_timer_next_timeout(void) { struct thread_timer *ttimer; int timeout; ttimer = SYS_DLIST_PEEK_HEAD_CONTAINER(&_current->thread_timer_q, ttimer, node); if (ttimer) { timeout = (int)((u32_t)ttimer->expiry_time - k_uptime_get_32()); return (timeout < 0) ? 0 : timeout; } return -1; } void thread_timer_handle_expired(void) { struct thread_timer *ttimer, *next; u32_t cur_time; cur_time = k_uptime_get_32(); SYS_DLIST_FOR_EACH_CONTAINER_SAFE(&_current->thread_timer_q, ttimer, next, node) { if (compare_time(ttimer->expiry_time, cur_time) > 0) { /* no expired thread timer */ return; } /* remove this expiry thread timer */ sys_dlist_remove(&ttimer->node); if (ttimer->expiry_fn) { /* resubmit this thread timer if it is a period timer */ if (ttimer->period != 0) { thread_timer_start(ttimer, ttimer->period, ttimer->period); } TT_DEBUG("timer %p: call %p\n", ttimer, ttimer->expiry_fn); /* invoke thread timer expiry function */ ttimer->expiry_fn(ttimer, ttimer->expiry_fn_arg); } } }