Topics

[RFC 1/3] Bluetooth: 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>
---
net/bluetooth/Kconfig | 9 +++
net/bluetooth/Makefile | 1 +
net/bluetooth/timer.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/timer.h | 48 +++++++++++
4 files changed, 269 insertions(+)
create mode 100644 net/bluetooth/timer.c
create mode 100644 net/bluetooth/timer.h

diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 2ffb0e0..2d703e3 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -20,6 +20,7 @@ menuconfig BLUETOOTH
bool "Bluetooth support"
default n
select NANO_TIMEOUTS
+ select NANO_TIMERS
select NET_BUF
select BLUETOOTH_LE if !BLUETOOTH_STACK_NBLE
help
@@ -132,6 +133,14 @@ config BLUETOOTH_CONN
default n

if BLUETOOTH_CONN
+config BLUETOOTH_TIMER_STACK_SIZE
+ int "Size of the timer fiber stack"
+ default 512
+ range 512 65536
+ help
+ Size of the timer fiber stack. This is the context from
+ which all timer callbacks occur.
+
config BLUETOOTH_ACL_IN_COUNT
int "Number of incoming ACL data buffers"
default 5
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 160ba34..18f9231 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_BLUETOOTH_STACK_HCI) = \

ifeq ($(CONFIG_BLUETOOTH_CONN),y)
obj-y += conn.o \
+ timer.o \
l2cap.o \
att.o \
gatt.o
diff --git a/net/bluetooth/timer.c b/net/bluetooth/timer.c
new file mode 100644
index 0000000..608ee4f
--- /dev/null
+++ b/net/bluetooth/timer.c
@@ -0,0 +1,211 @@
+/** @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 <bluetooth/log.h>
+
+#include "timer.h"
+
+struct timer_queue {
+ struct bt_timer_queue tq;
+
+ BT_STACK(stack, CONFIG_BLUETOOTH_TIMER_STACK_SIZE);
+};
+
+static struct timer_queue global_tq = {
+ .tq.stack_size = CONFIG_BLUETOOTH_TIMER_STACK_SIZE,
+};
+
+struct bt_timer_queue *__global_tq = &global_tq.tq;
+
+static void timer_expired(struct bt_timer *t)
+{
+ BT_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 bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ stack_analyze("timer queue stack", tq->stack, tq->stack_size);
+
+ while (tq->__head) {
+ struct bt_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:
+ BT_DBG("tq %p ticks %d", tq, ticks);
+
+ return ticks;
+}
+
+static void timer_queue_fiber(struct bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ BT_DBG("tq %p thread_id %d started", tq, tq->__thread_id);
+
+ while ((ticks = timer_queue_ticks_remains(tq))) {
+ fiber_sleep(ticks);
+ }
+
+ BT_DBG("tq %p thread_id %d stopped", tq, tq->__thread_id);
+
+ tq->__thread_id = 0;
+ fiber_abort();
+}
+
+static int timer_queue_start(struct bt_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 bt_timer_queue_init(struct bt_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 bt_timer_queue *tq)
+{
+ if (sys_thread_self_get() != tq->__thread_id)
+ fiber_wakeup(tq->__thread_id);
+}
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer *t, int ticks)
+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer *t)
+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_init(struct bt_timer *t, bt_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/timer.h b/net/bluetooth/timer.h
new file mode 100644
index 0000000..82bee8f
--- /dev/null
+++ b/net/bluetooth/timer.h
@@ -0,0 +1,48 @@
+/** @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.
+ */
+
+extern struct bt_timer_queue *__global_tq;
+
+struct bt_timer_queue {
+ nano_thread_id_t __thread_id;
+ struct bt_timer *__head;
+ size_t stack_size;
+ char stack[0];
+};
+
+typedef void (*bt_timer_func)(void *user_data);
+
+struct bt_timer {
+ struct nano_timer __nano_timer;
+ bt_timer_func func;
+ void *user_data;
+ struct bt_timer *__next;
+};
+
+int bt_timer_queue_init(struct bt_timer_queue *tq, size_t stack_size);
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer *t,
+ int ticks);
+int bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer *t);
+
+int bt_timer_init(struct bt_timer *t, bt_timer_func func, void *user_data);
+
+#define bt_timer_add(_t, _ticks) bt_timer_queue_add(__global_tq, _t, _ticks)
+#define bt_timer_cancel(_t) bt_timer_queue_cancel(__global_tq, _t)
--
2.5.5


Jukka Rissanen
 

Hi Luiz,

On Mon, 2016-04-18 at 18:16 +0300, Luiz Augusto von Dentz wrote:
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>
---
 net/bluetooth/Kconfig  |   9 +++
 net/bluetooth/Makefile |   1 +
 net/bluetooth/timer.c  | 211
+++++++++++++++++++++++++++++++++++++++++++++++++
 net/bluetooth/timer.h  |  48 +++++++++++
 4 files changed, 269 insertions(+)
 create mode 100644 net/bluetooth/timer.c
 create mode 100644 net/bluetooth/timer.h

diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 2ffb0e0..2d703e3 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -20,6 +20,7 @@ menuconfig BLUETOOTH
  bool "Bluetooth support"
  default n
  select NANO_TIMEOUTS
+ select NANO_TIMERS
  select NET_BUF
  select BLUETOOTH_LE if !BLUETOOTH_STACK_NBLE
  help
@@ -132,6 +133,14 @@ config BLUETOOTH_CONN
  default n
 
 if BLUETOOTH_CONN
+config  BLUETOOTH_TIMER_STACK_SIZE
+ int "Size of the timer fiber stack"
+ default 512
+ range 512 65536
+ help
+   Size of the timer fiber stack. This is the context from
+   which all timer callbacks occur.
+
 config  BLUETOOTH_ACL_IN_COUNT
  int "Number of incoming ACL data buffers"
  default 5
Could we make the timer more generic from day one as this can be used
quite easily by IP sub-system. So the config could be placed to
net/Kconfig already.


diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 160ba34..18f9231 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_BLUETOOTH_STACK_HCI) = \
 
 ifeq ($(CONFIG_BLUETOOTH_CONN),y)
  obj-y += conn.o \
+ timer.o \
  l2cap.o \
  att.o \
  gatt.o
diff --git a/net/bluetooth/timer.c b/net/bluetooth/timer.c
new file mode 100644
index 0000000..608ee4f
--- /dev/null
+++ b/net/bluetooth/timer.c
@@ -0,0 +1,211 @@
+/** @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 <bluetooth/log.h>
+
+#include "timer.h"
+
+struct timer_queue {
+ struct bt_timer_queue tq;
+
+ BT_STACK(stack, CONFIG_BLUETOOTH_TIMER_STACK_SIZE);
+};
+
+static struct timer_queue global_tq = {
+ .tq.stack_size = CONFIG_BLUETOOTH_TIMER_STACK_SIZE,
Why do we need to store the stack size to a variable here?


+};
+
+struct bt_timer_queue *__global_tq = &global_tq.tq;
+
+static void timer_expired(struct bt_timer *t)
+{
+ BT_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 bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ stack_analyze("timer queue stack", tq->stack, tq-
stack_size);
+
+ while (tq->__head) {
+ struct bt_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;
Missing { }

+
+ /* 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:
+ BT_DBG("tq %p ticks %d", tq, ticks);
+
+ return ticks;
+}
+
+static void timer_queue_fiber(struct bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ BT_DBG("tq %p thread_id %d started", tq, tq->__thread_id);
+
+ while ((ticks = timer_queue_ticks_remains(tq))) {
+ fiber_sleep(ticks);
+ }
+
+ BT_DBG("tq %p thread_id %d stopped", tq, tq->__thread_id);
+
+ tq->__thread_id = 0;
+ fiber_abort();
+}
+
+static int timer_queue_start(struct bt_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 bt_timer_queue_init(struct bt_timer_queue *tq, size_t
stack_size)+{
Why not timer_queue_init()?


+ if (!tq || !stack_size) {
+ return -EINVAL;
+ }
+
+ tq->stack_size = stack_size;
+
+ return 0;
+}
+
+static inline void timer_queue_wakeup(struct bt_timer_queue *tq)
+{
+ if (sys_thread_self_get() != tq->__thread_id)
+ fiber_wakeup(tq->__thread_id);
Missing { }


+}
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer
*t, int ticks)
Why not timer_queue_add()?


+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer
*t)
Also this one could be named more generic?

+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_init(struct bt_timer *t, bt_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/timer.h b/net/bluetooth/timer.h
new file mode 100644
index 0000000..82bee8f
--- /dev/null
+++ b/net/bluetooth/timer.h
@@ -0,0 +1,48 @@
+/** @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.
+ */
+
+extern struct bt_timer_queue *__global_tq;
+
+struct bt_timer_queue {
+ nano_thread_id_t __thread_id;
+ struct bt_timer *__head;
+ size_t stack_size;
+ char stack[0];
+};
+
+typedef void (*bt_timer_func)(void *user_data);
+
+struct bt_timer {
+ struct nano_timer __nano_timer;
+ bt_timer_func func;
+ void *user_data;
+ struct bt_timer *__next;
+};
+
+int bt_timer_queue_init(struct bt_timer_queue *tq, size_t
stack_size);
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer
*t,
+        int ticks);
+int bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer
*t);
+
+int bt_timer_init(struct bt_timer *t, bt_timer_func func, void
*user_data);
+
+#define bt_timer_add(_t, _ticks) bt_timer_queue_add(__global_tq, _t,
_ticks)
+#define bt_timer_cancel(_t) bt_timer_queue_cancel(__global_tq, _t)

Cheers,
Jukka


Luiz Augusto von Dentz
 

Hi Jukka,

On Tue, Apr 19, 2016 at 10:34 AM, Jukka Rissanen
<jukka.rissanen(a)linux.intel.com> wrote:
Hi Luiz,

On Mon, 2016-04-18 at 18:16 +0300, Luiz Augusto von Dentz wrote:
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>
---
net/bluetooth/Kconfig | 9 +++
net/bluetooth/Makefile | 1 +
net/bluetooth/timer.c | 211
+++++++++++++++++++++++++++++++++++++++++++++++++
net/bluetooth/timer.h | 48 +++++++++++
4 files changed, 269 insertions(+)
create mode 100644 net/bluetooth/timer.c
create mode 100644 net/bluetooth/timer.h

diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 2ffb0e0..2d703e3 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -20,6 +20,7 @@ menuconfig BLUETOOTH
bool "Bluetooth support"
default n
select NANO_TIMEOUTS
+ select NANO_TIMERS
select NET_BUF
select BLUETOOTH_LE if !BLUETOOTH_STACK_NBLE
help
@@ -132,6 +133,14 @@ config BLUETOOTH_CONN
default n

if BLUETOOTH_CONN
+config BLUETOOTH_TIMER_STACK_SIZE
+ int "Size of the timer fiber stack"
+ default 512
+ range 512 65536
+ help
+ Size of the timer fiber stack. This is the context from
+ which all timer callbacks occur.
+
config BLUETOOTH_ACL_IN_COUNT
int "Number of incoming ACL data buffers"
default 5
Could we make the timer more generic from day one as this can be used
quite easily by IP sub-system. So the config could be placed to
net/Kconfig already.
Sure, this was just a little bit more convenient to show how it could
be implemented, etc. I guess this one will actually end up as a
sys_timer_queue or something like that.


diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 160ba34..18f9231 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_BLUETOOTH_STACK_HCI) = \

ifeq ($(CONFIG_BLUETOOTH_CONN),y)
obj-y += conn.o \
+ timer.o \
l2cap.o \
att.o \
gatt.o
diff --git a/net/bluetooth/timer.c b/net/bluetooth/timer.c
new file mode 100644
index 0000000..608ee4f
--- /dev/null
+++ b/net/bluetooth/timer.c
@@ -0,0 +1,211 @@
+/** @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 <bluetooth/log.h>
+
+#include "timer.h"
+
+struct timer_queue {
+ struct bt_timer_queue tq;
+
+ BT_STACK(stack, CONFIG_BLUETOOTH_TIMER_STACK_SIZE);
+};
+
+static struct timer_queue global_tq = {
+ .tq.stack_size = CONFIG_BLUETOOTH_TIMER_STACK_SIZE,
Why do we need to store the stack size to a variable here?
This is initializing the __global_tq, the stack_size is later on used
with timer_queue_start.


+};
+
+struct bt_timer_queue *__global_tq = &global_tq.tq;
+
+static void timer_expired(struct bt_timer *t)
+{
+ BT_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 bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ stack_analyze("timer queue stack", tq->stack, tq-
stack_size);
+
+ while (tq->__head) {
+ struct bt_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;
Missing { }

+
+ /* 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:
+ BT_DBG("tq %p ticks %d", tq, ticks);
+
+ return ticks;
+}
+
+static void timer_queue_fiber(struct bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ BT_DBG("tq %p thread_id %d started", tq, tq->__thread_id);
+
+ while ((ticks = timer_queue_ticks_remains(tq))) {
+ fiber_sleep(ticks);
+ }
+
+ BT_DBG("tq %p thread_id %d stopped", tq, tq->__thread_id);
+
+ tq->__thread_id = 0;
+ fiber_abort();
+}
+
+static int timer_queue_start(struct bt_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 bt_timer_queue_init(struct bt_timer_queue *tq, size_t
stack_size)+{
Why not timer_queue_init()?


+ if (!tq || !stack_size) {
+ return -EINVAL;
+ }
+
+ tq->stack_size = stack_size;
+
+ return 0;
+}
+
+static inline void timer_queue_wakeup(struct bt_timer_queue *tq)
+{
+ if (sys_thread_self_get() != tq->__thread_id)
+ fiber_wakeup(tq->__thread_id);
Missing { }


+}
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer
*t, int ticks)
Why not timer_queue_add()?


+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer
*t)
Also this one could be named more generic?

+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_init(struct bt_timer *t, bt_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/timer.h b/net/bluetooth/timer.h
new file mode 100644
index 0000000..82bee8f
--- /dev/null
+++ b/net/bluetooth/timer.h
@@ -0,0 +1,48 @@
+/** @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.
+ */
+
+extern struct bt_timer_queue *__global_tq;
+
+struct bt_timer_queue {
+ nano_thread_id_t __thread_id;
+ struct bt_timer *__head;
+ size_t stack_size;
+ char stack[0];
+};
+
+typedef void (*bt_timer_func)(void *user_data);
+
+struct bt_timer {
+ struct nano_timer __nano_timer;
+ bt_timer_func func;
+ void *user_data;
+ struct bt_timer *__next;
+};
+
+int bt_timer_queue_init(struct bt_timer_queue *tq, size_t
stack_size);
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer
*t,
+ int ticks);
+int bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer
*t);
+
+int bt_timer_init(struct bt_timer *t, bt_timer_func func, void
*user_data);
+
+#define bt_timer_add(_t, _ticks) bt_timer_queue_add(__global_tq, _t,
_ticks)
+#define bt_timer_cancel(_t) bt_timer_queue_cancel(__global_tq, _t)

Cheers,
Jukka



--
Luiz Augusto von Dentz


Szymon Janc <ext.szymon.janc@...>
 

Hi Luiz,

On Monday 18 of April 2016 18:16:01 Luiz Augusto von Dentz wrote:
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>
---
net/bluetooth/Kconfig | 9 +++
net/bluetooth/Makefile | 1 +
net/bluetooth/timer.c | 211
+++++++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/timer.h |
48 +++++++++++
4 files changed, 269 insertions(+)
create mode 100644 net/bluetooth/timer.c
create mode 100644 net/bluetooth/timer.h

diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 2ffb0e0..2d703e3 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -20,6 +20,7 @@ menuconfig BLUETOOTH
bool "Bluetooth support"
default n
select NANO_TIMEOUTS
+ select NANO_TIMERS
select NET_BUF
select BLUETOOTH_LE if !BLUETOOTH_STACK_NBLE
help
@@ -132,6 +133,14 @@ config BLUETOOTH_CONN
default n

if BLUETOOTH_CONN
+config BLUETOOTH_TIMER_STACK_SIZE
+ int "Size of the timer fiber stack"
+ default 512
+ range 512 65536
+ help
+ Size of the timer fiber stack. This is the context from
+ which all timer callbacks occur.
I think it would be better to have kconfig option for callback needed stack
and add overhead internally. This should be easier to estimate for user.

+
config BLUETOOTH_ACL_IN_COUNT
int "Number of incoming ACL data buffers"
default 5
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 160ba34..18f9231 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_BLUETOOTH_STACK_HCI) = \

ifeq ($(CONFIG_BLUETOOTH_CONN),y)
obj-y += conn.o \
+ timer.o \
l2cap.o \
att.o \
gatt.o
diff --git a/net/bluetooth/timer.c b/net/bluetooth/timer.c
new file mode 100644
index 0000000..608ee4f
--- /dev/null
+++ b/net/bluetooth/timer.c
@@ -0,0 +1,211 @@
+/** @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 <bluetooth/log.h>
+
+#include "timer.h"
+
+struct timer_queue {
+ struct bt_timer_queue tq;
+
+ BT_STACK(stack, CONFIG_BLUETOOTH_TIMER_STACK_SIZE);
+};
+
+static struct timer_queue global_tq = {
+ .tq.stack_size = CONFIG_BLUETOOTH_TIMER_STACK_SIZE,
+};
+
+struct bt_timer_queue *__global_tq = &global_tq.tq;
+
+static void timer_expired(struct bt_timer *t)
+{
+ BT_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 bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ stack_analyze("timer queue stack", tq->stack, tq->stack_size);
+
+ while (tq->__head) {
+ struct bt_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:
+ BT_DBG("tq %p ticks %d", tq, ticks);
+
+ return ticks;
+}
+
+static void timer_queue_fiber(struct bt_timer_queue *tq)
+{
+ int32_t ticks;
+
+ BT_DBG("tq %p thread_id %d started", tq, tq->__thread_id);
+
+ while ((ticks = timer_queue_ticks_remains(tq))) {
+ fiber_sleep(ticks);
+ }
+
+ BT_DBG("tq %p thread_id %d stopped", tq, tq->__thread_id);
+
+ tq->__thread_id = 0;
+ fiber_abort();
+}
+
+static int timer_queue_start(struct bt_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 bt_timer_queue_init(struct bt_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 bt_timer_queue *tq)
+{
+ if (sys_thread_self_get() != tq->__thread_id)
+ fiber_wakeup(tq->__thread_id);
+}
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer *t, int
ticks) +{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer *t)
+{
+ struct bt_timer *cur, *prev;
+
+ BT_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 bt_timer_init(struct bt_timer *t, bt_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/timer.h b/net/bluetooth/timer.h
new file mode 100644
index 0000000..82bee8f
--- /dev/null
+++ b/net/bluetooth/timer.h
@@ -0,0 +1,48 @@
+/** @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.
+ */
+
+extern struct bt_timer_queue *__global_tq;
+
+struct bt_timer_queue {
+ nano_thread_id_t __thread_id;
+ struct bt_timer *__head;
+ size_t stack_size;
+ char stack[0];
+};
+
+typedef void (*bt_timer_func)(void *user_data);
+
+struct bt_timer {
+ struct nano_timer __nano_timer;
+ bt_timer_func func;
+ void *user_data;
+ struct bt_timer *__next;
+};
+
+int bt_timer_queue_init(struct bt_timer_queue *tq, size_t stack_size);
+
+int bt_timer_queue_add(struct bt_timer_queue *tq, struct bt_timer *t,
+ int ticks);
+int bt_timer_queue_cancel(struct bt_timer_queue *tq, struct bt_timer *t);
+
+int bt_timer_init(struct bt_timer *t, bt_timer_func func, void *user_data);
+
+#define bt_timer_add(_t, _ticks) bt_timer_queue_add(__global_tq, _t,
_ticks) +#define bt_timer_cancel(_t) bt_timer_queue_cancel(__global_tq, _t)
--
BR
Szymon Janc