diff --git a/Makefile.dep b/Makefile.dep index d01275961d..5aceacf8b7 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -29,6 +29,7 @@ endif ifneq (,$(filter gnrc_mac,$(USEMODULE))) USEMODULE += gnrc_priority_pktqueue USEMODULE += csma_sender + USEMODULE += evtimer endif ifneq (,$(filter gnrc_gomach,$(USEMODULE))) diff --git a/sys/include/net/gnrc/mac/timeout.h b/sys/include/net/gnrc/mac/timeout.h new file mode 100644 index 0000000000..9a84940bfb --- /dev/null +++ b/sys/include/net/gnrc/mac/timeout.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 Daniel Krebs + * 2017 INRIA + * + * 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 net_gnrc_mac + * @{ + * + * @file + * @brief Timeout APIs used by GNRC_MAC + * + * @author Daniel Krebs + * @author Shuguo Zhuo + */ + +#ifndef NET_GNRC_MAC_TIMEOUT_H +#define NET_GNRC_MAC_TIMEOUT_H + +#include +#include + +#include "evtimer_msg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief definition for GNRC_MAC timeout event type + */ +#define GNRC_MAC_EVENT_TIMEOUT_TYPE (0x4400) + +/** + * @brief Definitions of GNRC_MAC timeout types. + * + * This structure can be extended to contain more needed + * timeout types of different MAC protocols. Please guard + * them by appropriate \#ifdef directives when applicable. + */ +typedef enum { + GNRC_MAC_TIMEOUT_DISABLED = 0, /**< Timeout is disabled, not in used. */ +} gnrc_mac_timeout_type_t; + +/** + * @brief Structure of the GNRC_MAC timeout event. + */ +typedef struct { + evtimer_msg_event_t msg_event; /**< The timeout message event. */ + gnrc_mac_timeout_type_t type; /**< GNRC_MAC timeout type. */ +} gnrc_mac_timeout_event_t; + +/** + * @brief Structure holding the GNRC_MAC timeouts. + */ +typedef struct { + evtimer_t evtimer; /**< evtimer entity which + stores the timeout list. */ + gnrc_mac_timeout_event_t *timeouts; /**< The gnrc_mac timeout + unites. */ + uint8_t timeout_num; /**< Timeout number. */ +} gnrc_mac_timeout_t; + +/** + * @brief Initialize the MAC timeout module of gnrc_mac before using, + * it also sets the timeout callback function. + * + * @param[in,out] mac_timeout gnrc_mac timeout management unit + * @param[in] timeouts gnrc_mac timeouts + * @param[in] num timeout number + */ +void gnrc_mac_init_timeouts(gnrc_mac_timeout_t *mac_timeout, + gnrc_mac_timeout_event_t timeouts[], + uint8_t num); + +/** + * @brief Set a MAC timeout of @p type. + * + * @param[in,out] mac_timeout gnrc_mac timeout management unit + * @param[in] type the MAC timeout type + * @param[in] offset the timeout offset + * @param[in] pid the targeted thread pid + */ +void gnrc_mac_set_timeout(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type, + uint32_t offset, kernel_pid_t pid); + +/** + * @brief Find a MAC timeout of @p type. + * + * @param[in] mac_timeout gnrc_mac timeout management unit + * @param[in] type the MAC timeout type + * + * @return Return index >= 0 if found timeout, -ENONENT if not found + */ +int gnrc_mac_find_timeout(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type); + +/** + * @brief Clear a MAC timeout of @p type. + * + * @param[in,out] mac_timeout gnrc_mac timeout management unit + * @param[in] type the MAC timeout type + */ +void gnrc_mac_clear_timeout(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type); + +/** + * @brief Check whether a MAC timeout of @p type is running or not. + * + * @param[in] mac_timeout gnrc_mac timeout management unit + * @param[in] type the MAC timeout type + * + * @return true, if the time of @p type is running + * @return false, if the time of @p type is not running, or not exist + */ +static inline bool gnrc_mac_timeout_is_running(gnrc_mac_timeout_t *mac_timeout, + gnrc_mac_timeout_type_t type) +{ + assert(mac_timeout); + return (gnrc_mac_find_timeout(mac_timeout, type) >= 0); +} + +/** + * @brief Check whether a MAC timeout of @p type has expired or not. + * + * @param[in,out] mac_timeout gnrc_mac timeout management unit + * @param[in] type the MAC timeout type + * + * @return true, if the MAC time of @p type is expired + * @return false, if the MAC time of @p type is not expired, or not exist + */ +bool gnrc_mac_timeout_is_expired(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type); + +/** + * @brief Reset all the MAC timeouts. + * + * @param[in,out] mac_timeout gnrc_mac timeout management unit + */ +void gnrc_mac_reset_timeouts(gnrc_mac_timeout_t *mac_timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_MAC_TIMEOUT_H */ diff --git a/sys/net/gnrc/link_layer/gnrc_mac/timeout.c b/sys/net/gnrc/link_layer/gnrc_mac/timeout.c new file mode 100644 index 0000000000..1b3f8eb5db --- /dev/null +++ b/sys/net/gnrc/link_layer/gnrc_mac/timeout.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 Daniel Krebs + * 2017 INRIA + * + * 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 net_gnrc_mac + * @{ + * + * @file + * @brief Implementation of timeout module of GNRC_MAC + * + * @author Daniel Krebs + * @author Shuguo Zhuo + * @} + */ + +#include "net/gnrc.h" +#include "net/gnrc/mac/timeout.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +void gnrc_mac_init_timeouts(gnrc_mac_timeout_t *mac_timeout, + gnrc_mac_timeout_event_t timeouts[], + uint8_t num) +{ + assert(mac_timeout); + assert(timeouts); + assert(num); + + mac_timeout->timeouts = timeouts; + mac_timeout->timeout_num = num; + + for (int i = 0; i < mac_timeout->timeout_num; i++) { + mac_timeout->timeouts[i].msg_event.event.next = NULL; + mac_timeout->timeouts[i].type = GNRC_MAC_TIMEOUT_DISABLED; + } + + evtimer_init_msg(&mac_timeout->evtimer); +} + + +int gnrc_mac_find_timeout(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type) +{ + assert(mac_timeout); + assert(mac_timeout->timeout_num); + + for (unsigned i = 0; i < mac_timeout->timeout_num; i++) { + if (mac_timeout->timeouts[i].type == type) { + return i; + } + } + return -ENOENT; +} + +gnrc_mac_timeout_event_t *_gnrc_mac_acquire_timeout(gnrc_mac_timeout_t *mac_timeout, + gnrc_mac_timeout_type_t type) +{ + assert(mac_timeout); + assert(mac_timeout->timeout_num); + + if (gnrc_mac_timeout_is_running(mac_timeout, type)) { + return NULL; + } + + for (unsigned i = 0; i < mac_timeout->timeout_num; i++) { + if (mac_timeout->timeouts[i].type == GNRC_MAC_TIMEOUT_DISABLED) { + mac_timeout->timeouts[i].type = type; + return &mac_timeout->timeouts[i]; + } + } + return NULL; +} + +void gnrc_mac_set_timeout(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type, + uint32_t offset, kernel_pid_t pid) +{ + assert(mac_timeout); + + gnrc_mac_timeout_event_t *timeout_event; + if ((timeout_event = _gnrc_mac_acquire_timeout(mac_timeout, type))) { + DEBUG("[gnrc_mac] Set timeout type-%d in %" PRIu32 " us\n", + type, offset); + timeout_event->msg_event.event.offset = offset; + timeout_event->msg_event.msg.type = GNRC_MAC_EVENT_TIMEOUT_TYPE; + timeout_event->msg_event.msg.content.ptr = (void *) timeout_event; + timeout_event->msg_event.msg.sender_pid = pid; + evtimer_add(&mac_timeout->evtimer, &timeout_event->msg_event.event); + } + else { + DEBUG("[gnrc_mac] Cannot set timeout type-%d, too many concurrent timeouts\n", + type); + } +} + +void gnrc_mac_clear_timeout(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type) +{ + assert(mac_timeout); + + int index = gnrc_mac_find_timeout(mac_timeout, type); + if (index >= 0) { + mac_timeout->timeouts[index].type = GNRC_MAC_TIMEOUT_DISABLED; + evtimer_del(&mac_timeout->evtimer, + &mac_timeout->timeouts[index].msg_event.event); + } +} + +bool gnrc_mac_timeout_is_expired(gnrc_mac_timeout_t *mac_timeout, gnrc_mac_timeout_type_t type) +{ + assert(mac_timeout); + + int index = gnrc_mac_find_timeout(mac_timeout, type); + if (index >= 0) { + evtimer_event_t *list; + list = (evtimer_event_t *)&mac_timeout->evtimer.events; + while (list->next) { + if (list->next == &mac_timeout->timeouts[index].msg_event.event) { + return false; + } + list = list->next; + } + + /* if we reach here, timeout is expired */ + mac_timeout->timeouts[index].type = GNRC_MAC_TIMEOUT_DISABLED; + return true; + } + return false; +} + +void gnrc_mac_reset_timeouts(gnrc_mac_timeout_t *mac_timeout) +{ + assert(mac_timeout); + assert(mac_timeout->timeout_num); + + for (unsigned i = 0; i < mac_timeout->timeout_num; i++) { + if (mac_timeout->timeouts[i].type != GNRC_MAC_TIMEOUT_DISABLED) { + mac_timeout->timeouts[i].type = GNRC_MAC_TIMEOUT_DISABLED; + evtimer_del(&mac_timeout->evtimer, + &mac_timeout->timeouts[i].msg_event.event); + } + } +} diff --git a/tests/gnrc_mac_timeout/Makefile b/tests/gnrc_mac_timeout/Makefile new file mode 100644 index 0000000000..f6d15881f4 --- /dev/null +++ b/tests/gnrc_mac_timeout/Makefile @@ -0,0 +1,10 @@ +APPLICATION = gnrc_mac_timeout +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-mega2560 arduino-uno \ + chronos nucleo-f030r8 nucleo-f031k6 nucleo-f042k6 \ + nucleo-l031k6 nucleo-l053r8 stm32f0discovery waspmote-pro + +USEMODULE += gnrc_mac + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_mac_timeout/README.md b/tests/gnrc_mac_timeout/README.md new file mode 100644 index 0000000000..aa526aa60c --- /dev/null +++ b/tests/gnrc_mac_timeout/README.md @@ -0,0 +1,14 @@ +Expected result +=============== + +This is a test application for using the timeout module of gnrc_mac for setting timeouts. + +When everything works as expected, you should see timeouts expired at the time they are set +to be expired. Also, you should see the status of timeouts are corresponding to their real +status, i.e., the system should state that a timeout is "running" when it is set and +awaiting to be expired, and state "not running" when a timeout has expired and not set +again. + +Background +========== +Test for verifying the functionalities of the timeout module of gnrc_mac. diff --git a/tests/gnrc_mac_timeout/main.c b/tests/gnrc_mac_timeout/main.c new file mode 100644 index 0000000000..1dcad248a2 --- /dev/null +++ b/tests/gnrc_mac_timeout/main.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 INRIA + * + * 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 tests + * @{ + * + * @file + * @brief gnrc_mac timeout test application + * + * @author Shuguo Zhuo + * + * @} + */ + +#include + +#include "net/gnrc/mac/timeout.h" +#include "thread.h" +#include "msg.h" +#include "xtimer.h" + +#define TIMEOUT_COUNT 3 +#define TIMEOUT_1_DURATION 1000 +#define TIMEOUT_2_DURATION 2538 +#define TIMEOUT_3_DURATION 3471 +static gnrc_mac_timeout_t mac_timeout; +static gnrc_mac_timeout_event_t test_timeouts[TIMEOUT_COUNT]; +static gnrc_mac_timeout_type_t timeout_1; +static gnrc_mac_timeout_type_t timeout_2; +static gnrc_mac_timeout_type_t timeout_3; +static uint32_t start_time; + +static char worker_stack[THREAD_STACKSIZE_MAIN]; + + +/* This thread will print the drift to stdout once per second */ +void *worker_thread(void *arg) +{ + int count = 1; + + (void) arg; + + while (1) { + msg_t m; + uint32_t now; + + msg_receive(&m); + now = xtimer_now_usec() / US_PER_MS; + + if (gnrc_mac_timeout_is_expired(&mac_timeout, timeout_1)) { + printf("At %6" PRIu32 " ms received msg %i: timeout_1 (set at %" PRIu32 " ms) expired, " + "supposed to be %" PRIu32 " ms!\n", now, count++, start_time, (TIMEOUT_1_DURATION + start_time)); + } + + if (gnrc_mac_timeout_is_expired(&mac_timeout, timeout_2)) { + printf("At %6" PRIu32 " ms received msg %i: timeout_2 (set at %" PRIu32 " ms) expired, " + "supposed to be %" PRIu32 " ms!\n", now, count++, start_time, (TIMEOUT_2_DURATION + start_time)); + } + + if (gnrc_mac_timeout_is_expired(&mac_timeout, timeout_3)) { + printf("At %6" PRIu32 " ms received msg %i: timeout_3 (set at %" PRIu32 " ms) expired, " + "supposed to be %" PRIu32 " ms!\n", now, count++, start_time, (TIMEOUT_3_DURATION + start_time)); + } + + if (gnrc_mac_timeout_is_running(&mac_timeout, timeout_1)) { + printf("At %6" PRIu32 " ms: timeout_1 is running.\n", now); + } + else { + printf("At %6" PRIu32 " ms: timeout_1 is not running.\n", now); + } + + if (gnrc_mac_timeout_is_running(&mac_timeout, timeout_2)) { + printf("At %6" PRIu32 " ms: timeout_2 is running.\n", now); + } + else { + printf("At %6" PRIu32 " ms: timeout_2 is not running.\n", now); + } + + if (gnrc_mac_timeout_is_running(&mac_timeout, timeout_3)) { + printf("At %6" PRIu32 " ms: timeout_3 is running.\n", now); + } + else { + printf("At %6" PRIu32 " ms: timeout_3 is not running.\n", now); + } + } +} + +int main(void) +{ + /* create worker thread */ + kernel_pid_t pid = thread_create(worker_stack, sizeof(worker_stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + worker_thread, NULL, "worker"); + + timeout_1 = -1; + timeout_2 = -2; + timeout_3 = -3; + + start_time = xtimer_now_usec() / US_PER_MS; + gnrc_mac_init_timeouts(&mac_timeout, test_timeouts, TIMEOUT_COUNT); + gnrc_mac_set_timeout(&mac_timeout, timeout_1, TIMEOUT_1_DURATION, pid); + gnrc_mac_set_timeout(&mac_timeout, timeout_2, TIMEOUT_2_DURATION, pid); + gnrc_mac_set_timeout(&mac_timeout, timeout_3, TIMEOUT_3_DURATION, pid); + printf("Testing gnrc_mac timeout module (start time = %" PRIu32 " ms)\n", start_time); + printf("Set timeout_1, should be expired at %" PRIu32 " ms)\n", TIMEOUT_1_DURATION + start_time); + printf("Set timeout_2, should be expired at %" PRIu32 " ms)\n", TIMEOUT_2_DURATION + start_time); + printf("Set timeout_3, should be expired at %" PRIu32 " ms)\n", TIMEOUT_3_DURATION + start_time); + + + puts("Are the reception times of all 3 msgs close to the supposed values?\n"); + puts("If yes, the tests were successful"); +}