Example
Dependencies: FXAS21002 FXOS8700Q
Diff: simple-mbed-cloud-client/mbed-cloud-client/sal-stack-nanostack-eventloop/source/ns_timer.c
- Revision:
- 0:11cc2b7889af
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simple-mbed-cloud-client/mbed-cloud-client/sal-stack-nanostack-eventloop/source/ns_timer.c Tue Nov 19 09:49:38 2019 +0000 @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2014-2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 "ns_types.h" +#include "ns_list.h" +#include "ns_timer.h" +#include "eventOS_callback_timer.h" +#include "platform/arm_hal_interrupt.h" +#include "platform/arm_hal_timer.h" +#include "nsdynmemLIB.h" + +#ifndef NS_EXCLUDE_HIGHRES_TIMER +typedef enum ns_timer_state_e { + NS_TIMER_ACTIVE = 0, // Will run on the next HAL interrupt + NS_TIMER_HOLD, // Will run on a later HAL interrupt + NS_TIMER_RUN_INTERRUPT, // Running on the interrupt we're currently handling + NS_TIMER_STOP // Timer not scheduled ("start" not called since last callback) +} ns_timer_state_e; + +typedef struct ns_timer_struct { + int8_t ns_timer_id; + ns_timer_state_e timer_state; + uint16_t slots; + uint16_t remaining_slots; + void (*interrupt_handler)(int8_t, uint16_t); + ns_list_link_t link; +} ns_timer_struct; + +static NS_LIST_DEFINE(ns_timer_list, ns_timer_struct, link); + +#define NS_TIMER_RUNNING 1 +static uint8_t ns_timer_state = 0; + +#ifdef ATMEGA256RFR2 +#define COMPENSATION 3 +#define COMPENSATION_TUNE 1 +#else +#define COMPENSATION 0 +#define COMPENSATION_TUNE 0 +#endif + +static void ns_timer_interrupt_handler(void); +static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id); +static bool ns_timer_initialized = 0; + +int8_t eventOS_callback_timer_register(void (*timer_interrupt_handler)(int8_t, uint16_t)) +{ + int8_t retval = -1; + + if (!ns_timer_initialized) { + /*Set interrupt handler in HAL driver*/ + platform_timer_set_cb(ns_timer_interrupt_handler); + ns_timer_initialized = 1; + } + + /*Find first free timer ID in timer list*/ + /*(Note use of uint8_t to avoid overflow if we reach 0x7F)*/ + for (uint8_t i = 0; i <= INT8_MAX; i++) { + if (!ns_timer_get_pointer_to_timer_struct(i)) { + retval = i; + break; + } + } + + if (retval == -1) { + return -1; + } + + ns_timer_struct *new_timer = ns_dyn_mem_alloc(sizeof(ns_timer_struct)); + if (!new_timer) { + return -1; + } + + /*Initialise new timer*/ + new_timer->ns_timer_id = retval; + new_timer->timer_state = NS_TIMER_STOP; + new_timer->remaining_slots = 0; + new_timer->interrupt_handler = timer_interrupt_handler; + + // Critical section sufficient as long as list can't be reordered from + // interrupt, otherwise will need to cover whole routine + platform_enter_critical(); + ns_list_add_to_end(&ns_timer_list, new_timer); + platform_exit_critical(); + + /*Return timer ID*/ + return retval; +} + +int8_t eventOS_callback_timer_unregister(int8_t ns_timer_id) +{ + ns_timer_struct *current_timer; + + current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id); + if (!current_timer) { + return -1; + } + + // Critical section sufficient as long as list can't be reordered from + // interrupt, otherwise will need to cover whole routine + platform_enter_critical(); + ns_list_remove(&ns_timer_list, current_timer); + platform_exit_critical(); + + ns_dyn_mem_free(current_timer); + return 0; +} + + +static int8_t ns_timer_start_pl_timer(uint16_t pl_timer_start_slots) +{ + /*Don't start timer with 0 slots*/ + if (!pl_timer_start_slots) { + pl_timer_start_slots = 1; + } + + /*Start HAL timer*/ + platform_timer_start(pl_timer_start_slots); + /*Set HAL timer state to running*/ + ns_timer_state |= NS_TIMER_RUNNING; + return 0; +} + +int8_t ns_timer_sleep(void) +{ + int8_t ret_val = -1; + if (ns_timer_state & NS_TIMER_RUNNING) { + /*Start HAL timer*/ + platform_timer_disable(); + /*Set HAL timer state to running*/ + ns_timer_state &= ~NS_TIMER_RUNNING; + ret_val = 0; + } + return ret_val; +} + +static int8_t ns_timer_get_next_running_to(void) +{ + uint8_t hold_count = 0; + ns_timer_struct *first_timer = NULL; + + /*Find hold-labelled timer with the least remaining slots*/ + ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { + if (current_timer->timer_state == NS_TIMER_HOLD) { + if (!first_timer || current_timer->remaining_slots < first_timer->remaining_slots) { + first_timer = current_timer; + } + /*For optimisation, count the found timers*/ + hold_count++; + } + } + + if (!first_timer) { + return 0; + } + + /*If hold-labelled timer found, set it active and start the HAL driver*/ + hold_count--; + first_timer->timer_state = NS_TIMER_ACTIVE; + /*Compensate time spent in timer function*/ + if (first_timer->remaining_slots > COMPENSATION) { + first_timer->remaining_slots -= COMPENSATION; + } + /*Start HAL timer*/ + ns_timer_start_pl_timer(first_timer->remaining_slots); + + /*Update other hold-labelled timers*/ + ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { + if (hold_count == 0) { // early termination optimisation + break; + } + if (current_timer->timer_state == NS_TIMER_HOLD) { + if (current_timer->remaining_slots == first_timer->remaining_slots) { + current_timer->timer_state = NS_TIMER_ACTIVE; + } else { + current_timer->remaining_slots -= first_timer->remaining_slots; + /*Compensate time spent in timer function*/ + if (current_timer->remaining_slots > COMPENSATION) { + current_timer->remaining_slots -= COMPENSATION; + } + } + hold_count--; + } + } + + return 0; +} + + +static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id) +{ + /*Find timer with the given ID*/ + ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { + if (current_timer->ns_timer_id == timer_id) { + return current_timer; + } + } + return NULL; +} + +int8_t eventOS_callback_timer_start(int8_t ns_timer_id, uint16_t slots) +{ + int8_t ret_val = 0; + uint16_t pl_timer_remaining_slots; + ns_timer_struct *timer; + platform_enter_critical(); + + /*Find timer to be activated*/ + timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id); + if (!timer) { + ret_val = -1; + goto exit; + } + + // XXX this assumes the timer currently isn't running? + // Is event.c relying on this restarting HAL timer after ns_timer_sleep()? + + /*If any timers are active*/ + if (ns_timer_state & NS_TIMER_RUNNING) { + /*Get remaining slots of the currently activated timeout*/ + pl_timer_remaining_slots = platform_timer_get_remaining_slots(); + + /*New timeout is shorter than currently enabled timeout*/ + if (pl_timer_remaining_slots > slots) { + /*Start HAL timer*/ + ns_timer_start_pl_timer(slots - 0); + + ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { + /*Switch active timers to hold*/ + if (current_timer->timer_state == NS_TIMER_ACTIVE) { + current_timer->timer_state = NS_TIMER_HOLD; + current_timer->remaining_slots = 0; + } + /*Update hold-labelled timers*/ + if (current_timer->timer_state == NS_TIMER_HOLD) { + current_timer->remaining_slots += (pl_timer_remaining_slots - slots); + /*Compensate time spent in timer function*/ + if (current_timer->remaining_slots > (COMPENSATION - COMPENSATION_TUNE)) { + current_timer->remaining_slots -= (COMPENSATION - COMPENSATION_TUNE); + } + } + } + /*Mark active and start the timer*/ + timer->timer_state = NS_TIMER_ACTIVE; + timer->slots = slots; + timer->remaining_slots = slots; + } + + /*New timeout is longer than currently enabled timeout*/ + else if (pl_timer_remaining_slots < slots) { + /*Mark hold and update remaining slots*/ + timer->timer_state = NS_TIMER_HOLD; + timer->slots = slots; + timer->remaining_slots = (slots - pl_timer_remaining_slots); + } + /*New timeout is equal to currently enabled timeout*/ + else { + /*Mark it active and it will be handled in next interrupt*/ + timer->timer_state = NS_TIMER_ACTIVE; + timer->slots = slots; + timer->remaining_slots = slots; + } + } else { + /*No timers running*/ + timer->timer_state = NS_TIMER_HOLD; + timer->slots = slots; + timer->remaining_slots = slots; + /*Start next timeout*/ + ns_timer_get_next_running_to(); + } +exit: + platform_exit_critical(); + return ret_val; +} + +static void ns_timer_interrupt_handler(void) +{ + uint8_t i = 0; + + platform_enter_critical(); + /*Clear timer running state*/ + ns_timer_state &= ~NS_TIMER_RUNNING; + /*Mark active timers as NS_TIMER_RUN_INTERRUPT, interrupt functions are called at the end of this function*/ + ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { + if (current_timer->timer_state == NS_TIMER_ACTIVE) { + current_timer->timer_state = NS_TIMER_RUN_INTERRUPT; + /*For optimisation, count the found timers*/ + i++; + } + } + + /*Start next timeout*/ + ns_timer_get_next_running_to(); + + /*Call interrupt functions*/ + ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { + if (i == 0) { + break; + } + if (current_timer->timer_state == NS_TIMER_RUN_INTERRUPT) { + current_timer->timer_state = NS_TIMER_STOP; + current_timer->interrupt_handler(current_timer->ns_timer_id, current_timer->slots); + i--; + } + } + + platform_exit_critical(); +} + +int8_t eventOS_callback_timer_stop(int8_t ns_timer_id) +{ + uint16_t pl_timer_remaining_slots; + bool active_timer_found = false; + ns_timer_struct *current_timer; + ns_timer_struct *first_timer = NULL; + int8_t retval = -1; + + platform_enter_critical(); + /*Find timer with given timer ID*/ + current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id); + if (!current_timer) { + goto exit; + } + + retval = 0; + + /*Check if already stopped*/ + if (current_timer->timer_state == NS_TIMER_STOP) { + goto exit; + } + + current_timer->timer_state = NS_TIMER_STOP; + current_timer->remaining_slots = 0; + + /*Check if some timer is already active*/ + ns_list_foreach(ns_timer_struct, curr_timer, &ns_timer_list) { + if (curr_timer->timer_state == NS_TIMER_ACTIVE) { + active_timer_found = true; + break; + } + } + /*If no active timers found, start one*/ + if (!active_timer_found) { + pl_timer_remaining_slots = platform_timer_get_remaining_slots(); + /*Find hold-labelled timer with the least remaining slots*/ + ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) { + if (cur_timer->timer_state == NS_TIMER_HOLD) { + cur_timer->remaining_slots += pl_timer_remaining_slots; + + if (!first_timer || cur_timer->remaining_slots < first_timer->remaining_slots) { + first_timer = cur_timer; + } + } + } + /*If hold-labelled timer found, set it active and start the HAL driver*/ + if (first_timer) { + first_timer->timer_state = NS_TIMER_ACTIVE; + /*Start HAL timer*/ + ns_timer_start_pl_timer(first_timer->remaining_slots); + /*If some of the other hold-labelled timers have the same remaining slots as the timer_tmp, mark them active*/ + ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) { + if (cur_timer->timer_state == NS_TIMER_HOLD) { + if (cur_timer->remaining_slots == first_timer->remaining_slots) { + cur_timer->timer_state = NS_TIMER_ACTIVE; + } else { + cur_timer->remaining_slots -= first_timer->remaining_slots; + } + } + } + } + } + +exit: + platform_exit_critical(); + + return retval; +} +#endif // NS_EXCLUDE_HIGHRES_TIMER