diff --git a/Makefile.dep b/Makefile.dep index 9037185244a42030926fee4ed64a7b99495b7313..2c320dbdda38b07d91d8590a4cd560fbedbc1105 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -580,6 +580,10 @@ ifneq (,$(filter phydat,$(USEMODULE))) USEMODULE += fmt endif +ifneq (,$(filter evtimer,$(USEMODULE))) + USEMODULE += xtimer +endif + ifneq (,$(filter random,$(USEMODULE))) # select default prng ifeq (,$(filter prng_%,$(USEMODULE))) diff --git a/sys/evtimer/Makefile b/sys/evtimer/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2 --- /dev/null +++ b/sys/evtimer/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/evtimer/evtimer.c b/sys/evtimer/evtimer.c new file mode 100644 index 0000000000000000000000000000000000000000..4adfd426370326dca83bc67ebe42a97a472126fb --- /dev/null +++ b/sys/evtimer/evtimer.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de> + * 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_evtimer + * @{ + * + * @file + * @brief event timer implementation + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + * @author Martine Lenders <m.lenders@fu-berlin.de> + * + * @} + */ + +#include "div.h" +#include "irq.h" +#include "xtimer.h" + +#include "evtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* XXX this function is intentionally non-static, since the optimizer can't + * handle the pointer hack in this function */ +void evtimer_add_event_to_list(evtimer_t *evtimer, evtimer_event_t *event) +{ + uint32_t delta_sum = 0; + + /* we want list->next to point to the first list element. thus we take the + * *address* of evtimer->events, then cast it from (evtimer_event_t **) to + * (evtimer_event_t*). After that, list->next actually equals + * evtimer->events. */ + evtimer_event_t *list = (evtimer_event_t *)&evtimer->events; + + while (list->next) { + evtimer_event_t *list_entry = list->next; + if ((list_entry->offset + delta_sum) > event->offset) { + break; + } + delta_sum += list_entry->offset; + list = list->next; + } + + event->next = list->next; + if (list->next) { + evtimer_event_t *next_entry = list->next; + next_entry->offset += delta_sum; + next_entry->offset -= event->offset; + } + event->offset -= delta_sum; + + list->next = event; +} + +static void _del_event_from_list(evtimer_t *evtimer, evtimer_event_t *event) +{ + evtimer_event_t *list = (evtimer_event_t *) &evtimer->events; + + while (list->next) { + evtimer_event_t *list_entry = list->next; + if (list_entry == event) { + list->next = event->next; + if (list->next) { + list_entry = list->next; + list_entry->offset += event->offset; + } + break; + } + list = list->next; + } +} + +static void _set_timer(xtimer_t *timer, uint32_t offset) +{ + uint64_t offset_in_us = (uint64_t)offset * 1000; + + DEBUG("evtimer: now=%" PRIu32 " setting xtimer to %" PRIu32 ":%" PRIu32 "\n", + xtimer_now_usec(), (uint32_t)(offset_in_us >> 32), + (uint32_t)(offset_in_us)); + _xtimer_set64(timer, offset_in_us, offset_in_us >> 32); +} + +static void _update_timer(evtimer_t *evtimer) +{ + if (evtimer->events) { + evtimer_event_t *event = evtimer->events; + _set_timer(&evtimer->timer, event->offset); + } + else { + xtimer_remove(&evtimer->timer); + } +} + +static uint32_t _get_offset(xtimer_t *timer) +{ + uint64_t now = xtimer_now_usec64(); + uint64_t target = ((uint64_t)timer->long_target) << 32 | timer->target; + + if (target <= now) { + return 0; + } + else { + target -= now; + /* add half of 125 so integer division rounds to nearest */ + return div_u64_by_125((target >> 3) + 62); + } +} + +static void _update_head_offset(evtimer_t *evtimer) +{ + if (evtimer->events) { + evtimer_event_t *event = evtimer->events; + event->offset = _get_offset(&evtimer->timer); + DEBUG("evtimer: _update_head_offset(): new head offset %" PRIu32 "\n", event->offset); + } +} + +void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event) +{ + unsigned state = irq_disable(); + + DEBUG("evtimer_add(): adding event with offset %" PRIu32 "\n", event->offset); + + _update_head_offset(evtimer); + evtimer_add_event_to_list(evtimer, event); + if (evtimer->events == event) { + _set_timer(&evtimer->timer, event->offset); + } + irq_restore(state); + if (sched_context_switch_request) { + thread_yield_higher(); + } +} + +void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event) +{ + unsigned state = irq_disable(); + + DEBUG("evtimer_del(): removing event with offset %" PRIu32 "\n", event->offset); + + _update_head_offset(evtimer); + _del_event_from_list(evtimer, event); + _update_timer(evtimer); + irq_restore(state); +} + +static evtimer_event_t *_get_next(evtimer_t *evtimer) +{ + evtimer_event_t *event = evtimer->events; + + if (event && (event->offset == 0)) { + evtimer->events = event->next; + return event; + } + else { + return NULL; + } +} + +static void _evtimer_handler(void *arg) +{ + DEBUG("_evtimer_handler()\n"); + + evtimer_t *evtimer = (evtimer_t *)arg; + + /* this function gets called directly by xtimer if the set xtimer expired. + * Thus the offset of the first event is down to zero. */ + evtimer_event_t *event = evtimer->events; + event->offset = 0; + + /* iterate the event list */ + while ((event = _get_next(evtimer))) { + evtimer->callback(event); + } + + _update_timer(evtimer); +} + +void evtimer_init(evtimer_t *evtimer, evtimer_callback_t handler) +{ + evtimer->callback = handler; + evtimer->timer.callback = _evtimer_handler; + evtimer->timer.arg = (void *)evtimer; + evtimer->events = NULL; +} + +void evtimer_print(const evtimer_t *evtimer) +{ + evtimer_event_t *list = evtimer->events; + + while (list->next) { + evtimer_event_t *list_entry = list->next; + printf("ev offset=%u\n", (unsigned)list_entry->offset); + list = list->next; + } +} diff --git a/sys/include/evtimer.h b/sys/include/evtimer.h new file mode 100644 index 0000000000000000000000000000000000000000..da8dc9a1efca7b461bfd6aecf3e8d07fa1a19253 --- /dev/null +++ b/sys/include/evtimer.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de> + * 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_evtimer Millisecond interval event timers + * @ingroup sys + * @brief Provides timers for events up to @$2^{32}@$ milliseconds in the + * future + * + * @note Experimental and likely to replaced with unified timer API + * + * RIOT's main timer subsystem is @ref sys_xtimer "xtimer", but for many + * applications @ref sys_xtimer "xtimer's" 64-bit absolute time values are + * wasteful or clumsy to use. + * + * Compared to @ref sys_xtimer "xtimer", evtimer offers: + * + * - only relative 32-bit millisecond timer values + * Events can be scheduled with a relative offset of up to ~49.7 days in the + * future. + * **For time-critical stuff, use @ref sys_xtimer "xtimer"!** + * - more flexible, "intrusive" timer type @ref evtimer_event_t only contains + * the necessary fields, which can be extended as needed, and handlers define + * actions taken on timer triggers. Check out @ref evtimer_msg_event_t as + * example. + * - uses @ref sys_xtimer "xtimer" as backend + * + * @{ + * + * @file + * @brief evtimer API definitions + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ + +#ifndef EVTIMER_H +#define EVTIMER_H + +#include <stdint.h> + +#include "xtimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generic event + */ +typedef struct evtimer_event { + struct evtimer_event *next; /**< the next event in the queue */ + uint32_t offset; /**< offset in milliseconds from previous event */ +} evtimer_event_t; + +/** + * @brief Event timer callback type + */ +typedef void(*evtimer_callback_t)(evtimer_event_t* event); + +/** + * @brief Event timer + */ +typedef struct { + xtimer_t timer; /**< Timer */ + evtimer_callback_t callback; /**< Handler function for this evtimer's + event type */ + evtimer_event_t *events; /**< Event queue */ +} evtimer_t; + +/** + * @brief Initializes an event timer + * + * @param[in] evtimer An event timer + * @param[in] handler An event handler function + */ +void evtimer_init(evtimer_t *evtimer, evtimer_callback_t handler); + +/** + * @brief Adds event to an event timer + * + * @param[in] evtimer An event timer + * @param[in] event An event + */ +void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event); + +/** + * @brief Removes an event from an event timer + * + * @param[in] evtimer An event timer + * @param[in] event An event + */ +void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event); + +/** + * @brief Print overview of current state of an event timer + * + * @param[in] evtimer An event timer + */ +void evtimer_print(const evtimer_t *evtimer); + +#ifdef __cplusplus +} +#endif + +#endif /* EVTIMER_H */ +/** @} */ diff --git a/sys/include/evtimer_msg.h b/sys/include/evtimer_msg.h new file mode 100644 index 0000000000000000000000000000000000000000..ce15f6c3c65a0a0ab2f79a14bb2e87203ce5f7b9 --- /dev/null +++ b/sys/include/evtimer_msg.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de> + * 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup sys_evtimer + * @{ + * + * @file + * @brief IPC-based evtimer definitions + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef EVTIMER_MSG_H +#define EVTIMER_MSG_H + +#include "msg.h" +#include "evtimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief IPC-message event timer + * @extends evtimer_t + */ +typedef evtimer_t evtimer_msg_t; + +/** + * @brief IPC-message event + * @extends evtimer_event_t + */ +typedef struct { + evtimer_event_t event; /**< base class */ + msg_t msg; /**< the IPC message to generate on event */ +} evtimer_msg_event_t; + +/** + * @brief Adds event to an event timer that handles events via IPC + * + * @param[in] evtimer An event timer + * @param[in] event An event + * @param[in] target_pid The PID of the thread that should receive the IPC + * message + */ +static inline void evtimer_add_msg(evtimer_msg_t *evtimer, + evtimer_msg_event_t *event, + kernel_pid_t target_pid) +{ + /* use sender_pid field to get target_pid into callback function */ + event->msg.sender_pid = target_pid; + evtimer_add(evtimer, &event->event); +} + +/** + * @brief Event handler for IPC messages + * + * @param[in] event The event to handle + */ +static inline void _evtimer_msg_handler(evtimer_event_t *event) +{ + evtimer_msg_event_t *mevent = (evtimer_msg_event_t *)event; + msg_send_int(&mevent->msg, mevent->msg.sender_pid); +} + +/** + * @brief Initializes event timer to handle events via IPC + * + * @param[in] evtimer An event timer + */ +static inline void evtimer_init_msg(evtimer_t *evtimer) +{ + evtimer_init(evtimer, _evtimer_msg_handler); +} + +#ifdef __cplusplus +} +#endif + +#endif /* EVTIMER_MSG_H */ +/** @} */