[RFCv2 1/2] misc: Add timer API


Luiz Augusto von Dentz
 

From: Luiz Augusto von Dentz <luiz.von.dentz(a)intel.com>

This adds a timer fiber which can be used track timeouts removing the
need to use one delayed fiber per timeout.

Change-Id: Id13347fbc69b1e83bca22094fbeb741e045ed516
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz(a)intel.com>
---
include/misc/timer.h | 106 ++++++++++++++++++++++++
misc/Kconfig | 37 +++++++++
misc/Makefile | 1 +
misc/timer.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/Kconfig | 1 +
5 files changed, 369 insertions(+)
create mode 100644 include/misc/timer.h
create mode 100644 misc/timer.c

diff --git a/include/misc/timer.h b/include/misc/timer.h
new file mode 100644
index 0000000..d3ce695
--- /dev/null
+++ b/include/misc/timer.h
@@ -0,0 +1,106 @@
+/** @file
+ * @brief System timer APIs.
+ */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern struct sys_timer_queue *__global_tq;
+
+struct sys_timer_queue {
+ nano_thread_id_t __thread_id;
+ struct sys_timer *__head;
+ size_t stack_size;
+ char stack[0];
+};
+
+typedef void (*sys_timer_func)(void *user_data);
+
+struct sys_timer {
+ struct nano_timer __nano_timer;
+ sys_timer_func func;
+ void *user_data;
+ struct sys_timer *__next;
+};
+
+/**
+ * @brief Init timer queue
+ *
+ * @param tq Timer queue object that should be initialized
+ * @param stack_size The stack size in bytes
+ */
+int sys_timer_queue_init(struct sys_timer_queue *tq, size_t stack_size);
+
+/**
+ * @brief Add timer to a timer queue
+ *
+ * Both timer and timer queue must be initialized.
+ *
+ * @param tq Timer queue object to add the timer
+ * @param t Timer object to be added
+ * @param ticks Amount of time in ticks to wait
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int sys_timer_queue_add(struct sys_timer_queue *tq, struct sys_timer *t,
+ int ticks);
+
+/**
+ * @brief Cancel timer
+ *
+ * Remove timer from timer queue and stop it so its callback is not called.
+ *
+ * @param tq Timer queue object where the timer was added
+ * @param t Timer object to be canceled
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int sys_timer_queue_cancel(struct sys_timer_queue *tq, struct sys_timer *t);
+
+/**
+ * @brief Init timer
+ *
+ * @param t Timer object to be initialized
+ * @param func Function callback to be called when timer expires
+ * @param user_data Data to be passed in the callback
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int sys_timer_init(struct sys_timer *t, sys_timer_func func, void *user_data);
+
+/**
+ * @brief Add timer to the global timer queue
+ *
+ * Timer must be initialized.
+ *
+ * @param _t Timer object to be added
+ * @param _ticks Amount of time in ticks to wait
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+#define sys_timer_add(_t, _ticks) sys_timer_queue_add(__global_tq, _t, _ticks)
+
+/**
+ * @brief Cancel global timer
+ *
+ * Remove timer from the global timer queue and stop it so its callback is not
+ * called.
+ *
+ * @param _t Timer object to be canceled
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+#define sys_timer_cancel(_t) sys_timer_queue_cancel(__global_tq, _t)
diff --git a/misc/Kconfig b/misc/Kconfig
index 4c05169..a9bd80f 100644
--- a/misc/Kconfig
+++ b/misc/Kconfig
@@ -374,3 +374,40 @@ config REBOOT
needed to perform a "safe" reboot (e.g. SYSTEM_CLOCK_DISABLE, to stop the
system clock before issuing a reset).
endmenu
+
+menu "Timer Options"
+
+config SYS_TIMER
+ bool
+ prompt "Enable System Timer"
+ default n
+ select NANO_TIMEOUTS
+ select NANO_TIMERS
+ help
+ Global switch for enabling sys_timer API.
+
+config SYS_TIMER_STACK_SIZE
+ int "Size of the global timer fiber stack"
+ depends on SYS_TIMER
+ default 512
+ range 512 65536
+ help
+ Size of the global timer fiber stack. This is the context from
+ which all global timer callbacks occur.
+
+config SYS_LOG_TIMER_LEVEL
+ int
+ prompt "System Timer Log level"
+ depends on SYS_LOG && SYS_TIMER
+ default 0
+ range 0 4
+ help
+ Sets log level for System Timers.
+ Levels are:
+ 0 OFF, do not write
+ 1 ERROR, only write SYS_LOG_ERR
+ 2 WARNING, write SYS_LOG_WRN in adition to previous level
+ 3 INFO, write SYS_LOG_INF in adition to previous levels
+ 4 DEBUG, write SYS_LOG_DBG in adition to previous levels
+
+endmenu
diff --git a/misc/Makefile b/misc/Makefile
index cb9a718..d57b8f2 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_CPLUSPLUS) += cpp_virtual.o cpp_vtable.o \
cpp_init_array.o cpp_ctors.o cpp_dtors.o
obj-$(CONFIG_PRINTK) += printk.o
obj-$(CONFIG_REBOOT) += reboot.o
+obj-$(CONFIG_SYS_TIMER) += timer.o
obj-y += generated/
obj-y += debug/
diff --git a/misc/timer.c b/misc/timer.c
new file mode 100644
index 0000000..90bf196
--- /dev/null
+++ b/misc/timer.c
@@ -0,0 +1,224 @@
+/** @file
+ * @brief Internal APIs for Bluetooth timer handling.
+ */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nanokernel.h>
+#include <toolchain.h>
+#include <sections.h>
+#include <string.h>
+#include <errno.h>
+#include <misc/stack.h>
+#include <misc/timer.h>
+
+#define SYS_LOG_DOMAIN "System Timer"
+#define SYS_LOG_LEVEL CONFIG_SYS_LOG_TIMER_LEVEL
+#include <misc/sys_log.h>
+
+#if CONFIG_SYS_LOG_TIMER_LEVEL > 0
+/* Enabling debug increases stack size requirement considerably */
+#define STACK_DEBUG_EXTRA 512
+#else
+#define STACK_DEBUG_EXTRA 0
+#endif
+
+#define STACK(name, size) \
+ char __stack name[(size) + STACK_DEBUG_EXTRA]
+
+struct timer_queue {
+ struct sys_timer_queue tq;
+
+ STACK(stack, CONFIG_SYS_TIMER_STACK_SIZE);
+};
+
+static struct timer_queue global_tq = {
+ .tq.stack_size = CONFIG_SYS_TIMER_STACK_SIZE,
+};
+
+struct sys_timer_queue *__global_tq = &global_tq.tq;
+
+static void timer_expired(struct sys_timer *t)
+{
+ SYS_LOG_DBG("t %p", t);
+
+ /* Just to be safe stop the timer */
+ nano_fiber_timer_stop(&t->__nano_timer);
+
+ t->func(t->user_data);
+}
+
+static int32_t timer_queue_ticks_remains(struct sys_timer_queue *tq)
+{
+ int32_t ticks;
+
+ stack_analyze("timer queue stack", tq->stack, tq->stack_size);
+
+ while (tq->__head) {
+ struct sys_timer *cur;
+
+ /* If head is not expired sleep the remaining ticks */
+ ticks = nano_timer_ticks_remain(&tq->__head->__nano_timer);
+ if (ticks > 0) {
+ goto done;
+ }
+
+ /* Remove head since it has expired */
+ cur = tq->__head;
+ tq->__head = tq->__head->__next;
+
+ /* Execute callback */
+ timer_expired(cur);
+ }
+
+ /* No timers left, abort fiber */
+ ticks = 0;
+
+done:
+ SYS_LOG_DBG("tq %p ticks %d", tq, ticks);
+
+ return ticks;
+}
+
+static void timer_queue_fiber(struct sys_timer_queue *tq)
+{
+ int32_t ticks;
+
+ SYS_LOG_DBG("tq %p thread_id %d started", tq, tq->__thread_id);
+
+ while ((ticks = timer_queue_ticks_remains(tq))) {
+ fiber_sleep(ticks);
+ }
+
+ SYS_LOG_DBG("tq %p thread_id %d stopped", tq, tq->__thread_id);
+
+ tq->__thread_id = 0;
+ fiber_abort();
+}
+
+static int timer_queue_start(struct sys_timer_queue *tq)
+{
+ if (tq->__thread_id) {
+ return 0;
+ }
+
+ tq->__thread_id = fiber_start(tq->stack, tq->stack_size,
+ (nano_fiber_entry_t)timer_queue_fiber,
+ (int) tq, 0, 7, 0);
+
+ return 0;
+}
+
+int sys_timer_queue_init(struct sys_timer_queue *tq, size_t stack_size)
+{
+ if (!tq || !stack_size) {
+ return -EINVAL;
+ }
+
+ tq->stack_size = stack_size;
+
+ return 0;
+}
+
+static inline void timer_queue_wakeup(struct sys_timer_queue *tq)
+{
+ if (sys_thread_self_get() != tq->__thread_id) {
+ fiber_wakeup(tq->__thread_id);
+ }
+}
+
+int sys_timer_queue_add(struct sys_timer_queue *tq, struct sys_timer *t, int ticks)
+{
+ struct sys_timer *cur, *prev;
+
+ SYS_LOG_DBG("tq %p t %p ticks %d", tq, t, ticks);
+
+ if (!t || !t->func)
+ return -EINVAL;
+
+ timer_queue_start(tq);
+ nano_timer_start(&t->__nano_timer, ticks);
+
+ /* Sort the list of timers */
+ for (cur = tq->__head, prev = NULL; cur;
+ prev = cur, cur = cur->__next) {
+ if (ticks < nano_timer_ticks_remain(&cur->__nano_timer)) {
+ break;
+ }
+ }
+
+ t->__next = cur;
+
+ if (prev) {
+ prev->__next = t;
+ } else {
+ tq->__head = t;
+ /* Wakeup timer queue fiber since there is a new head */
+ timer_queue_wakeup(tq);
+ }
+
+ return 0;
+}
+
+int sys_timer_queue_cancel(struct sys_timer_queue *tq, struct sys_timer *t)
+{
+ struct sys_timer *cur, *prev;
+
+ SYS_LOG_DBG("tq %p t %p", tq, t);
+
+ if (!t) {
+ return -EINVAL;
+ }
+
+ /* Lookup existing timers */
+ for (cur = tq->__head, prev = NULL; cur;
+ prev = cur, cur = cur->__next) {
+ if (cur == t) {
+ break;
+ }
+ }
+
+ if (!cur) {
+ return -ENOENT;
+ }
+
+ nano_timer_stop(&t->__nano_timer);
+
+ /* Remove timer for the queue */
+ if (prev) {
+ prev->__next = t->__next;
+ } else {
+ tq->__head = t->__next;
+ /* Wakeup timer queue fiber since there is a new head */
+ timer_queue_wakeup(tq);
+ }
+
+ return 0;
+}
+
+int sys_timer_init(struct sys_timer *t, sys_timer_func func, void *user_data)
+{
+ if (!t || !func) {
+ return -EINVAL;
+ }
+
+ t->func = func;
+ t->user_data = user_data;
+ nano_timer_init(&t->__nano_timer, t);
+
+ return 0;
+}
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 2ffb0e0..aa2712b 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -129,6 +129,7 @@ config BLUETOOTH_CENTRAL

config BLUETOOTH_CONN
bool
+ select SYS_TIMER
default n

if BLUETOOTH_CONN
--
2.5.5

Join devel@lists.zephyrproject.org to automatically receive all group messages.