unified: Revise timer code to conform to new API specification
Provides users with a more compact and intuitive API for kernel
timers.
Provides legacy support for microkernel timers and nanokernel
timers by building on the new kernel timer infrastructure.
Each timer type requires only a small amount of additional
wrapper code, as well as the addition of a single pointer
field to the underlying timer structure, all of which will be
easily removed when support for the legacy APIs is discontinued.
Change-Id: I282dfaf1ed08681703baabf21e4dbc3516ee7463
Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
diff --git a/include/kernel.h b/include/kernel.h
index 7c1718b..b720548 100644
--- a/include/kernel.h
+++ b/include/kernel.h
@@ -297,6 +297,7 @@
_timeout_func_t func;
};
+
/* timers */
struct k_timer {
@@ -307,25 +308,23 @@
*/
struct _timeout timeout;
- /* wait queue for the threads waiting on this timer */
+ /* wait queue for the (single) thread waiting on this timer */
_wait_q_t wait_q;
/* runs in ISR context */
- void (*handler)(void *);
- void *handler_arg;
+ void (*expiry_fn)(struct k_timer *);
/* runs in the context of the thread that calls k_timer_stop() */
- void (*stop_handler)(void *);
- void *stop_handler_arg;
+ void (*stop_fn)(struct k_timer *);
/* timer period */
int32_t period;
- /* user supplied data pointer returned to the thread*/
- void *user_data;
+ /* timer status */
+ uint32_t status;
- /* user supplied data pointer */
- void *user_data_internal;
+ /* used to support legacy timer APIs */
+ void *_legacy_data;
_DEBUG_TRACING_KERNEL_OBJECTS_NEXT_PTR(k_timer);
};
@@ -339,19 +338,104 @@
#define K_TIMER_DEFINE(name) \
struct k_timer name = K_TIMER_INITIALIZER(name)
-extern void k_timer_init(struct k_timer *timer, void *data);
+/**
+ * @brief Initialize a timer.
+ *
+ * This routine must be called before the timer is used.
+ *
+ * @param timer Address of timer.
+ * @param expiry_fn Function to invoke each time timer expires.
+ * @param stop_fn Function to invoke if timer is stopped while running.
+ *
+ * @return N/A
+ */
+extern void k_timer_init(struct k_timer *timer,
+ void (*expiry_fn)(struct k_timer *),
+ void (*stop_fn)(struct k_timer *));
+/**
+ * @brief Start a timer.
+ *
+ * This routine starts a timer, and resets its status to zero. The timer
+ * begins counting down using the specified duration and period values.
+ *
+ * Attempting to start a timer that is already running is permitted.
+ * The timer's status is reset to zero and the timer begins counting down
+ * using the new duration and period values.
+ *
+ * @param timer Address of timer.
+ * @param duration Initial timer duration (in milliseconds).
+ * @param period Timer period (in milliseconds).
+ *
+ * @return N/A
+ */
extern void k_timer_start(struct k_timer *timer,
- int32_t duration, int32_t period,
- void (*handler)(void *), void *handler_arg,
- void (*stop_handler)(void *), void *stop_handler_arg);
-extern void k_timer_restart(struct k_timer *timer, int32_t duration,
- int32_t period);
+ int32_t duration, int32_t period);
+
+/**
+ * @brief Stop a timer.
+ *
+ * This routine stops a running timer prematurely. The timer's stop function,
+ * if one exists, is invoked by the caller.
+ *
+ * Attempting to stop a timer that is not running is permitted, but has no
+ * effect on the timer since it is already stopped.
+ *
+ * @param timer Address of timer.
+ *
+ * @return N/A
+ */
extern void k_timer_stop(struct k_timer *timer);
-extern int k_timer_test(struct k_timer *timer, void **data, int wait);
+
+/**
+ * @brief Read timer status.
+ *
+ * This routine reads the timer's status, which indicates the number of times
+ * it has expired since its status was last read.
+ *
+ * Calling this routine resets the timer's status to zero.
+ *
+ * @param timer Address of timer.
+ *
+ * @return Timer status.
+ */
+extern uint32_t k_timer_status_get(struct k_timer *timer);
+
+/**
+ * @brief Synchronize thread to timer expiration.
+ *
+ * This routine blocks the calling thread until the timer's status is non-zero
+ * (indicating that it has expired at least once since it was last examined)
+ * or the timer is stopped. If the timer status is already non-zero,
+ * or the timer is already stopped, the caller continues without waiting.
+ *
+ * Calling this routine resets the timer's status to zero.
+ *
+ * This routine must not be used by interrupt handlers, since they are not
+ * allowed to block.
+ *
+ * @param timer Address of timer.
+ *
+ * @return Timer status.
+ */
+extern uint32_t k_timer_status_sync(struct k_timer *timer);
+
+/**
+ * @brief Get timer remaining before next timer expiration.
+ *
+ * This routine computes the (approximate) time remaining before a running
+ * timer next expires. If the timer is not running, it returns zero.
+ *
+ * @param timer Address of timer.
+ *
+ * @return Remaining time (in milliseconds).
+ */
+
extern int32_t k_timer_remaining_get(struct k_timer *timer);
+/* kernel clocks */
+
/**
* @brief Get the time elapsed since the system booted (uptime)
*
diff --git a/include/legacy.h b/include/legacy.h
index 5b22b03..0156304 100644
--- a/include/legacy.h
+++ b/include/legacy.h
@@ -649,20 +649,27 @@
#define nano_fiber_stack_pop nano_stack_pop
#define nano_task_stack_pop nano_stack_pop
-/* timers */
+/* kernel clocks */
+
+extern int32_t _ms_to_ticks(int32_t ms);
+
+extern int64_t sys_tick_get(void);
+extern uint32_t sys_tick_get_32(void);
+extern int64_t sys_tick_delta(int64_t *reftime);
+extern uint32_t sys_tick_delta_32(int64_t *reftime);
+
+#define sys_cycle_get_32 k_cycle_get_32
+
+/* microkernel timers */
+
+#if (CONFIG_NUM_DYNAMIC_TIMERS > 0)
#define CONFIG_NUM_TIMER_PACKETS CONFIG_NUM_DYNAMIC_TIMERS
#define ktimer_t struct k_timer *
-#if (CONFIG_NUM_DYNAMIC_TIMERS > 0)
-extern struct k_timer *_k_timer_alloc(void);
-#define task_timer_alloc _k_timer_alloc
-
-extern void _k_timer_free(struct k_timer *timer);
-#define task_timer_free _k_timer_free
-#endif
-
+extern ktimer_t task_timer_alloc(void);
+extern void task_timer_free(ktimer_t timer);
extern void task_timer_start(ktimer_t timer, int32_t duration,
int32_t period, ksem_t sema);
@@ -670,34 +677,39 @@
static inline void task_timer_restart(ktimer_t timer, int32_t duration,
int32_t period)
{
- k_timer_restart(timer, _ticks_to_ms(duration), _ticks_to_ms(period));
+ k_timer_start(timer, _ticks_to_ms(duration), _ticks_to_ms(period));
}
+static inline void task_timer_stop(ktimer_t timer)
+{
+ k_timer_stop(timer);
+}
+
+extern bool k_timer_pool_is_empty(void);
+
+#endif /* CONFIG_NUM_DYNAMIC_TIMERS > 0 */
+
+/* nanokernel timers */
+
#define nano_timer k_timer
-#define nano_timer_init k_timer_init
+static inline void nano_timer_init(struct k_timer *timer, void *data)
+{
+ k_timer_init(timer, NULL, NULL);
+ timer->_legacy_data = data;
+}
static inline void nano_timer_start(struct nano_timer *timer, int ticks)
{
- k_timer_start((struct k_timer *)timer, _ticks_to_ms(ticks), 0,
- NULL, NULL, NULL, NULL);
+ k_timer_start(timer, _ticks_to_ms(ticks), 0);
}
#define nano_isr_timer_start nano_timer_start
#define nano_fiber_timer_start nano_timer_start
#define nano_task_timer_start nano_timer_start
-static inline void *nano_timer_test(struct nano_timer *timer,
- int32_t timeout_in_ticks)
-{
- void *data;
-
- if (k_timer_test(timer, &data, _ticks_to_ms(timeout_in_ticks)) < 0) {
- return NULL;
- }
-
- return data;
-}
+extern void *nano_timer_test(struct nano_timer *timer,
+ int32_t timeout_in_ticks);
#define nano_isr_timer_test nano_timer_test
#define nano_fiber_timer_test nano_timer_test
@@ -709,20 +721,11 @@
#define nano_fiber_timer_stop k_timer_stop
#define nano_task_timer_stop k_timer_stop
-extern int32_t _ms_to_ticks(int32_t ms);
-
static inline int32_t nano_timer_ticks_remain(struct nano_timer *timer)
{
return _ms_to_ticks(k_timer_remaining_get(timer));
}
-extern int64_t sys_tick_get(void);
-extern uint32_t sys_tick_get_32(void);
-extern int64_t sys_tick_delta(int64_t *reftime);
-extern uint32_t sys_tick_delta_32(int64_t *reftime);
-
-#define sys_cycle_get_32 k_cycle_get_32
-
/* floating point services */
#define fiber_float_enable k_float_enable
diff --git a/kernel/unified/legacy/timer_legacy.c b/kernel/unified/legacy/timer_legacy.c
index 78f2326..b70edfd 100644
--- a/kernel/unified/legacy/timer_legacy.c
+++ b/kernel/unified/legacy/timer_legacy.c
@@ -15,23 +15,93 @@
*/
#include <kernel.h>
+#include <init.h>
+#include <ksched.h>
+#include <misc/util.h>
+
+#if (CONFIG_NUM_DYNAMIC_TIMERS > 0)
+
+static struct k_timer dynamic_timers[CONFIG_NUM_DYNAMIC_TIMERS];
+static sys_dlist_t timer_pool;
+
+static void timer_sem_give(struct k_timer *timer)
+{
+ k_sem_give((ksem_t)timer->_legacy_data);
+}
+
+static int init_dyamic_timers(struct device *dev)
+{
+ ARG_UNUSED(dev);
+
+ int i;
+ int n_timers = ARRAY_SIZE(dynamic_timers);
+
+ sys_dlist_init(&timer_pool);
+ for (i = 0; i < n_timers; i++) {
+ k_timer_init(&dynamic_timers[i], timer_sem_give, NULL);
+ sys_dlist_append(&timer_pool,
+ &dynamic_timers[i].timeout.node);
+ }
+ return 0;
+}
+
+SYS_INIT(init_dyamic_timers, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
+
+ktimer_t task_timer_alloc(void)
+{
+ k_sched_lock();
+
+ /*
+ * This conversion works only if timeout member
+ * variable is the first in time structure.
+ */
+ struct k_timer *timer = (struct k_timer *)sys_dlist_get(&timer_pool);
+
+ k_sched_unlock();
+ return timer;
+}
+
+void task_timer_free(ktimer_t timer)
+{
+ k_timer_stop(timer);
+ k_sched_lock();
+ sys_dlist_append(&timer_pool, &timer->timeout.node);
+ k_sched_unlock();
+}
void task_timer_start(ktimer_t timer, int32_t duration,
int32_t period, ksem_t sema)
{
if (duration < 0 || period < 0 || (duration == 0 && period == 0)) {
- int key = irq_lock();
-
- if (timer->timeout.delta_ticks_from_prev != -1) {
- k_timer_stop(timer);
- }
-
- irq_unlock(key);
-
+ k_timer_stop(timer);
return;
}
- k_timer_start(timer, _ticks_to_ms(duration),
- _ticks_to_ms(period),
- (void(*)(void *))k_sem_give, sema, NULL, NULL);
+ timer->_legacy_data = (void *)sema;
+
+ k_timer_start(timer, _ticks_to_ms(duration), _ticks_to_ms(period));
+}
+
+bool _timer_pool_is_empty(void)
+{
+ k_sched_lock();
+
+ bool is_empty = sys_dlist_is_empty(&timer_pool);
+
+ k_sched_unlock();
+ return is_empty;
+}
+
+#endif /* (CONFIG_NUM_DYNAMIC_TIMERS > 0) */
+
+void *nano_timer_test(struct nano_timer *timer, int32_t timeout_in_ticks)
+{
+ uint32_t (*test_fn)(struct k_timer *timer);
+
+ if (timeout_in_ticks == TICKS_NONE) {
+ test_fn = k_timer_status_get;
+ } else {
+ test_fn = k_timer_status_sync;
+ }
+ return test_fn(timer) ? timer->_legacy_data : NULL;
}
diff --git a/kernel/unified/timer.c b/kernel/unified/timer.c
index 870546f..7766901 100644
--- a/kernel/unified/timer.c
+++ b/kernel/unified/timer.c
@@ -14,25 +14,22 @@
* limitations under the License.
*/
-#include <nano_private.h>
+#include <kernel.h>
#include <misc/debug/object_tracing_common.h>
#include <wait_q.h>
-#include <ksched.h>
-#include <init.h>
/**
- * @brief Timer expire handler
+ * @brief Handle expiration of a kernel timer object.
*
- * @param t Internal timeout structure
+ * @param t Timeout used by the timer.
*
* @return N/A
*/
-void timer_expiration_handler(struct _timeout *t)
+static void timer_expiration_handler(struct _timeout *t)
{
int key = irq_lock();
struct k_timer *timer = CONTAINER_OF(t, struct k_timer, timeout);
- struct k_thread *first_pending_thread =
- _unpend_first_thread(&timer->wait_q);
+ struct k_thread *pending_thread;
/* if the time is periodic, start it again */
if (timer->period > 0) {
@@ -40,143 +37,44 @@
timer->period);
}
- /* once timer is expired, it can return valid user data pointer */
- timer->user_data = timer->user_data_internal;
- /* resume thread waiting on the timer */
- if (first_pending_thread) {
- _ready_thread(first_pending_thread);
- _set_thread_return_value(first_pending_thread, 0);
- /*
- * Since the routine is called from timer interrupt handler
- * _Swap() is not invoked
- */
+ /* update timer's status */
+ timer->status += 1;
+
+ /* invoke timer expiry function */
+ if (timer->expiry_fn) {
+ timer->expiry_fn(timer);
}
- if (timer->handler) {
- timer->handler(timer->handler_arg);
+ /*
+ * wake up the (only) thread waiting on the timer, if there is one;
+ * don't invoke _Swap() since the timeout ISR called us, not a thread
+ */
+ pending_thread = _unpend_first_thread(&timer->wait_q);
+ if (pending_thread) {
+ _ready_thread(pending_thread);
+ _set_thread_return_value(pending_thread, 0);
}
+
irq_unlock(key);
}
-/**
- * @brief Initialize timer structure
- *
- * Routine initializes timer structure parameters and assigns the user
- * supplied data.
- * Routine needs to be called before timer is used
- *
- * @param timer Pointer to the timer structure to be initialized
- * @param data Pointer to user supplied data
- *
- * @return N/A
- */
-void k_timer_init(struct k_timer *timer, void *data)
+void k_timer_init(struct k_timer *timer,
+ void (*expiry_fn)(struct k_timer *),
+ void (*stop_fn)(struct k_timer *))
{
- timer->user_data = NULL;
- timer->user_data_internal = data;
- timer->period = 0;
+ timer->expiry_fn = expiry_fn;
+ timer->stop_fn = stop_fn;
+ timer->status = 0;
+
sys_dlist_init(&timer->wait_q);
_init_timeout(&timer->timeout, timer_expiration_handler);
SYS_TRACING_OBJ_INIT(micro_timer, timer);
+
+ timer->_legacy_data = NULL;
}
-#if (CONFIG_NUM_DYNAMIC_TIMERS > 0)
-
-/* Implements legacy API support for dynamic timers */
-
-static struct k_timer _dynamic_timers[CONFIG_NUM_DYNAMIC_TIMERS];
-static sys_dlist_t _timer_pool;
-
-/* Initialize the pool of timers for dynamic timer allocation */
-static int init_dyamic_timers(struct device *dev)
-{
- ARG_UNUSED(dev);
-
- int i;
- int n_timers = ARRAY_SIZE(_dynamic_timers);
-
- sys_dlist_init(&_timer_pool);
- for (i = 0; i < n_timers; i++) {
- k_timer_init(&_dynamic_timers[i], NULL);
- sys_dlist_append(&_timer_pool,
- &_dynamic_timers[i].timeout.node);
- }
- return 0;
-}
-
-/**
- * @brief Allocate timer
- *
- * Allocates a new timer timer.
- *
- * @return pointer to the new timer structure
- */
-struct k_timer *_k_timer_alloc(void)
-{
- k_sched_lock();
-
- /*
- * This conversion works only if timeout member
- * variable is the first in time structure.
- */
- struct k_timer *timer = (struct k_timer *)sys_dlist_get(&_timer_pool);
-
- k_sched_unlock();
- return timer;
-}
-
-
-/**
- * @brief Deallocate timer
- *
- * Deallocates timer and inserts it into the timer queue.
- * @param timer Timer to free
- *
- * @return N/A
- */
-void _k_timer_free(struct k_timer *timer)
-{
- k_timer_stop(timer);
- k_sched_lock();
- sys_dlist_append(&_timer_pool, &timer->timeout.node);
- k_sched_unlock();
-}
-
-/**
- *
- * @brief Check if the timer pool is empty
- *
- * @return true if the timer pool is empty, false otherwise
- */
-bool _k_timer_pool_is_empty(void)
-{
- k_sched_lock();
-
- bool is_empty = sys_dlist_is_empty(&_timer_pool);
-
- k_sched_unlock();
- return is_empty;
-}
-
-SYS_INIT(init_dyamic_timers, PRIMARY, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
-
-#endif /* (CONFIG_NUM_DYNAMIC_TIMERS > 0) */
-
-/**
- *
- * @brief Start timer
- *
- * @param timer Timer structure
- * @param duration Initial timer duration (ns)
- * @param period Timer period (ns)
- * @param sem Semaphore to signal timer expiration
- *
- * @return N/A
- */
-void k_timer_start(struct k_timer *timer, int32_t duration, int32_t period,
- void (*handler)(void *), void *handler_arg,
- void (*stop_handler)(void *), void *stop_handler_arg)
+void k_timer_start(struct k_timer *timer, int32_t duration, int32_t period)
{
__ASSERT(duration >= 0 && period >= 0 &&
(duration != 0 || period != 0), "invalid parameters\n");
@@ -188,121 +86,86 @@
}
timer->period = _ms_to_ticks(period);
-
- timer->handler = handler;
- timer->handler_arg = handler_arg;
- timer->stop_handler = stop_handler;
- timer->stop_handler_arg = stop_handler_arg;
-
_add_timeout(NULL, &timer->timeout, &timer->wait_q,
_ms_to_ticks(duration));
+ timer->status = 0;
irq_unlock(key);
}
-/**
- *
- * @brief Restart timer with new parameters
- *
- * @param timer Timer structure
- * @param duration Initial timer duration (ns)
- * @param period Timer period (ns)
- *
- * @return N/A
- */
-void k_timer_restart(struct k_timer *timer, int32_t duration, int32_t period)
-{
- k_timer_start(timer, duration, period,
- timer->handler, timer->handler_arg,
- timer->stop_handler, timer->stop_handler_arg);
-}
-
-
-/**
- *
- * @brief Stop the timer
- *
- * @param timer Timer structure
- *
- * @return N/A
- */
void k_timer_stop(struct k_timer *timer)
{
__ASSERT(!_is_in_isr(), "");
int key = irq_lock();
-
- _abort_timeout(&timer->timeout);
+ int stopped = _abort_timeout(&timer->timeout);
irq_unlock(key);
- if (timer->stop_handler) {
- timer->stop_handler(timer->stop_handler_arg);
+ if (stopped == -1) {
+ return;
+ }
+
+ if (timer->stop_fn) {
+ timer->stop_fn(timer);
}
key = irq_lock();
-
struct k_thread *pending_thread = _unpend_first_thread(&timer->wait_q);
if (pending_thread) {
- _set_thread_return_value(pending_thread, -ECANCELED);
_ready_thread(pending_thread);
}
- _reschedule_threads(key);
+ if (_is_in_isr()) {
+ irq_unlock(key);
+ } else {
+ _reschedule_threads(key);
+ }
}
-/**
- *
- * @brief Test the timer for expiration
- *
- * The routine checks if the timer is expired and returns the pointer
- * to user data. Otherwise makes the thread wait for the timer expiration.
- *
- * @param timer Timer structure
- * @param data User data pointer
- * @param wait May be K_NO_WAIT or K_FOREVER
- *
- * @return 0 or error code
- */
-int k_timer_test(struct k_timer *timer, void **user_data_ptr, int wait)
+uint32_t k_timer_status_get(struct k_timer *timer)
{
- int result = 0;
unsigned int key = irq_lock();
+ uint32_t result = timer->status;
- /* check if the timer has expired */
- if (timer->timeout.delta_ticks_from_prev == -1) {
- *user_data_ptr = timer->user_data;
- timer->user_data = NULL;
- } else if (wait == K_NO_WAIT) {
- /* if the thread should not wait, return immediately */
- *user_data_ptr = NULL;
- result = -EAGAIN;
- } else {
- /* otherwise pend the thread */
- _pend_current_thread(&timer->wait_q, K_FOREVER);
- result = _Swap(key);
- key = irq_lock();
- if (result == 0) {
- *user_data_ptr = timer->user_data;
- timer->user_data = NULL;
- }
- }
-
+ timer->status = 0;
irq_unlock(key);
+
return result;
}
-/**
- *
- * @brief Get timer remaining time
- *
- * @param timer Timer descriptor
- *
- * @return remaining time (ns)
- */
+uint32_t k_timer_status_sync(struct k_timer *timer)
+{
+ __ASSERT(!_is_in_isr(), "");
+
+ unsigned int key = irq_lock();
+ uint32_t result = timer->status;
+
+ if (result == 0) {
+ if (timer->timeout.delta_ticks_from_prev != -1) {
+ /* wait for timer to expire or stop */
+ _pend_current_thread(&timer->wait_q, K_FOREVER);
+ _Swap(key);
+
+ /* get updated timer status */
+ key = irq_lock();
+ result = timer->status;
+ } else {
+ /* timer is already stopped */
+ }
+ } else {
+ /* timer has already expired at least once */
+ }
+
+ timer->status = 0;
+ irq_unlock(key);
+
+ return result;
+}
+
int32_t k_timer_remaining_get(struct k_timer *timer)
{
@@ -314,11 +177,8 @@
remaining_ticks = 0;
} else {
/*
- * As nanokernel timeouts are stored in a linked list with
- * delta_ticks_from_prev, to get the actual number of ticks
- * remaining for the timer, walk through the timeouts list
- * and accumulate all the delta_ticks_from_prev values up to
- * the timer.
+ * compute remaining ticks by walking the timeout list
+ * and summing up the various tick deltas involved
*/
struct _timeout *t =
(struct _timeout *)sys_dlist_peek_head(timeout_q);
diff --git a/tests/kernel/test_timer/microkernel/src/timer.c b/tests/kernel/test_timer/microkernel/src/timer.c
index 8a43ebd..e9a2a55 100644
--- a/tests/kernel/test_timer/microkernel/src/timer.c
+++ b/tests/kernel/test_timer/microkernel/src/timer.c
@@ -32,8 +32,8 @@
#include "fifo_timeout.c"
#ifdef CONFIG_KERNEL_V2
-extern bool _k_timer_pool_is_empty(void); /* For white box testing only */
-#define timer_pool_is_empty _k_timer_pool_is_empty
+extern bool _timer_pool_is_empty(void); /* For white box testing only */
+#define timer_pool_is_empty _timer_pool_is_empty
#else
extern struct nano_lifo _k_timer_free; /* For white box testing only */
static inline bool timer_pool_is_empty(void)