diff --git a/examples/dimming/dimming_client/src/main.c b/examples/dimming/dimming_client/src/main.c index f8aaa086c..f42cd5159 100644 --- a/examples/dimming/dimming_client/src/main.c +++ b/examples/dimming/dimming_client/src/main.c @@ -249,7 +249,7 @@ static void button_event_handler(uint32_t button_number) static generic_level_move_set_params_t move_set_params = {0}; static generic_level_delta_set_params_t delta_set_params = {0}; model_transition_t transition_params; - static uint32_t timestamp = 0; + static timestamp_t timestamp = 0; timestamp_t current_time; switch(button_number) diff --git a/examples/enocean_switch/src/main.c b/examples/enocean_switch/src/main.c index c95c727c3..fc390c897 100644 --- a/examples/enocean_switch/src/main.c +++ b/examples/enocean_switch/src/main.c @@ -254,7 +254,7 @@ static void rx_callback(const nrf_mesh_adv_packet_rx_data_t * p_rx_data) static void app_switch_debounce(enocean_switch_status_t * p_status, uint8_t index) { - uint32_t timestamp = timer_now(); + timestamp_t timestamp = timer_now(); uint32_t status = NRF_ERROR_INTERNAL; generic_onoff_set_params_t set_params; model_transition_t transition_params; diff --git a/examples/light_ctl/ctl_client/src/main.c b/examples/light_ctl/ctl_client/src/main.c index 8dea1b304..f6d8f9caa 100644 --- a/examples/light_ctl/ctl_client/src/main.c +++ b/examples/light_ctl/ctl_client/src/main.c @@ -343,7 +343,7 @@ static void button_event_handler(uint32_t button_number) uint32_t status = NRF_SUCCESS; static uint8_t client = 0; model_transition_t transition_params; - static uint32_t timestamp = 0; + static timestamp_t timestamp = 0; timestamp_t current_time; switch(button_number) diff --git a/examples/light_lightness/client/src/main.c b/examples/light_lightness/client/src/main.c index 5b9a997e6..4f8f0ba77 100644 --- a/examples/light_lightness/client/src/main.c +++ b/examples/light_lightness/client/src/main.c @@ -330,7 +330,7 @@ static void button_event_handler(uint32_t button_number) static light_lightness_default_set_params_t default_set_params = {0}; static light_lightness_range_set_params_t range_set_params = {1, 65535}; model_transition_t transition_params; - static uint32_t timestamp = 0; + static timestamp_t timestamp = 0; timestamp_t current_time; switch(button_number) diff --git a/mesh/access/api/access_reliable.h b/mesh/access/api/access_reliable.h index 8eabb0187..765813765 100644 --- a/mesh/access/api/access_reliable.h +++ b/mesh/access/api/access_reliable.h @@ -148,7 +148,7 @@ typedef struct * Relative acknowledged message timeout. * That is, the time from access_model_reliable_publish() is called to the message is timed out. */ - uint32_t timeout; + timestamp_t timeout; /** Callback to call after the acknowledged transfer has ended. */ access_reliable_cb_t status_cb; } access_reliable_t; diff --git a/mesh/access/include/access_publish.h b/mesh/access/include/access_publish.h index 5e69d48ba..79509699c 100644 --- a/mesh/access/include/access_publish.h +++ b/mesh/access/include/access_publish.h @@ -62,7 +62,7 @@ typedef struct __access_model_publication_state_t /** Callback called for each publishing event. */ access_publish_timeout_cb_t publish_timeout_cb; /** Target time in units of 100 ms for when the next publishing operation is triggered. */ - uint32_t target; + timestamp_t target; /** Pointer to the next publication event. */ struct __access_model_publication_state_t * p_next; } access_model_publication_state_t; diff --git a/mesh/access/src/access_reliable.c b/mesh/access/src/access_reliable.c index d16d5d60c..734830c73 100644 --- a/mesh/access/src/access_reliable.c +++ b/mesh/access/src/access_reliable.c @@ -280,7 +280,7 @@ static uint32_t calculate_interval(const access_reliable_t * p_message) static void add_reliable_message(uint16_t index, const access_reliable_t * p_message) { NRF_MESH_ASSERT(!m_reliable.pool[index].in_use); - uint32_t time_now = timer_now(); + timestamp_t time_now = timer_now(); memcpy(&(m_reliable.pool[index].params), p_message, sizeof(access_reliable_t)); m_reliable.pool[index].interval = calculate_interval(p_message); m_reliable.pool[index].params.timeout += time_now; diff --git a/mesh/bearer/src/adv_ext_tx.c b/mesh/bearer/src/adv_ext_tx.c index 8165d9af8..f12c515fa 100644 --- a/mesh/bearer/src/adv_ext_tx.c +++ b/mesh/bearer/src/adv_ext_tx.c @@ -314,7 +314,7 @@ static void setup_aux(adv_ext_tx_t * p_tx) mesh_pa_lna_setup_stop(); /* Verify that we were able to service the TX in time */ - NRF_MESH_ASSERT(TIMER_OLDER_THAN(ts_timer_now(), txen_trigger_time + m_tx_session.start_time)); + NRF_MESH_ASSERT(TS_TIMER_OLDER_THAN(ts_timer_now(), txen_trigger_time + m_tx_session.start_time)); if (last_in_chain) { diff --git a/mesh/bearer/src/bearer_handler.c b/mesh/bearer/src/bearer_handler.c index 831271d51..3d099e926 100644 --- a/mesh/bearer/src/bearer_handler.c +++ b/mesh/bearer/src/bearer_handler.c @@ -157,7 +157,7 @@ static void end_handle(void) { /* Ensure the action didn't last too long: */ ts_timestamp_t time_now = ts_timer_now(); - NRF_MESH_ASSERT(TIMER_OLDER_THAN(time_now, m_end_time)); + NRF_MESH_ASSERT(TS_TIMER_OLDER_THAN(time_now, m_end_time)); timeslot_state_lock(false); diff --git a/mesh/bearer/src/broadcast.c b/mesh/bearer/src/broadcast.c index 8b7a0ac72..6009b51fe 100644 --- a/mesh/bearer/src/broadcast.c +++ b/mesh/bearer/src/broadcast.c @@ -164,7 +164,7 @@ static inline void tx_complete_notify_user(broadcast_t * p_broadcast) #if BROADCAST_DEBUG p_broadcast->debug.prev_tx_complete_app_time_us = user_cb_time_end - user_cb_time_start; #endif - NRF_MESH_ASSERT(TIMER_OLDER_THAN(user_cb_time_end, user_cb_time_start + USR_SOFTWARE_OVERHEAD_US)); + NRF_MESH_ASSERT(TS_TIMER_OLDER_THAN(user_cb_time_end, user_cb_time_start + USR_SOFTWARE_OVERHEAD_US)); } static inline void end_action(broadcast_t * p_broadcast) { diff --git a/mesh/bearer/src/instaburst_rx.c b/mesh/bearer/src/instaburst_rx.c index a4157bcf5..14c97d0f5 100644 --- a/mesh/bearer/src/instaburst_rx.c +++ b/mesh/bearer/src/instaburst_rx.c @@ -271,7 +271,7 @@ static void action_start(ts_timestamp_t start_time, void * p_args) NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; - if (TIMER_OLDER_THAN(m_instaburst.event.packet.start_time, ts_timer_now() + START_TIME_OVERHEAD_US)) + if (TS_TIMER_OLDER_THAN(m_instaburst.event.packet.start_time, ts_timer_now() + START_TIME_OVERHEAD_US)) { /* Can't make it in time */ DEBUG_PIN_INSTABURST_ON(DEBUG_PIN_INSTABURST_TOO_SLOW); @@ -333,7 +333,7 @@ static void action_start(ts_timestamp_t start_time, void * p_args) /* Ensure that the timer order above was scheduled in time. If this fails, we were * locking IRQs for too long, delaying packet processing. */ - NRF_MESH_ASSERT(TIMER_OLDER_THAN(ts_timer_now(), rampup_start_time)); + NRF_MESH_ASSERT(TS_TIMER_OLDER_THAN(ts_timer_now(), rampup_start_time)); } } else diff --git a/mesh/bearer/src/instaburst_tx.c b/mesh/bearer/src/instaburst_tx.c index 43a6f2c26..d837a057b 100644 --- a/mesh/bearer/src/instaburst_tx.c +++ b/mesh/bearer/src/instaburst_tx.c @@ -218,7 +218,7 @@ static bool order_tx(instaburst_tx_t * p_instaburst) return did_order; } -static void tx_timeout(uint32_t timestamp, void * p_context) +static void tx_timeout(timestamp_t timestamp, void * p_context) { instaburst_tx_t * p_instaburst = p_context; instaburst_tx_finalize(p_instaburst); diff --git a/mesh/bearer/src/scanner.c b/mesh/bearer/src/scanner.c index dbc6c0a29..85cfbc92c 100755 --- a/mesh/bearer/src/scanner.c +++ b/mesh/bearer/src/scanner.c @@ -451,13 +451,13 @@ void scanner_timer_irq_handler(void) * Timer handling *****************************************************************************/ -static void scan_window_end(uint32_t timestamp, void * p_context) +static void scan_window_end(timestamp_t timestamp, void * p_context) { m_scanner.window_state = SCAN_WINDOW_STATE_OFF; radio_trigger(); } -static void scan_window_start(uint32_t timestamp, void * p_context) +static void scan_window_start(timestamp_t timestamp, void * p_context) { channel_iterate(); radio_trigger(); @@ -465,7 +465,7 @@ static void scan_window_start(uint32_t timestamp, void * p_context) static void schedule_timers(void) { - uint32_t time_now = timer_now(); + timestamp_t time_now = timer_now(); if (continuous_scanning()) { diff --git a/mesh/core/CMakeLists.txt b/mesh/core/CMakeLists.txt index c12244724..f23dde283 100644 --- a/mesh/core/CMakeLists.txt +++ b/mesh/core/CMakeLists.txt @@ -18,7 +18,6 @@ set(MESH_CORE_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/aes_cmac.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/timer_scheduler.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/timer.c" - "${CMAKE_CURRENT_SOURCE_DIR}/src/long_timer.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/rand.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/nrf_mesh_opt.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/mesh_opt.c" diff --git a/mesh/core/include/core_tx.h b/mesh/core/include/core_tx.h index b17382188..f00c9b1e8 100644 --- a/mesh/core/include/core_tx.h +++ b/mesh/core/include/core_tx.h @@ -202,7 +202,7 @@ struct core_tx_bearer */ typedef void (*core_tx_complete_cb_t)(core_tx_role_t role, uint32_t bearer_index, - uint32_t timestamp, + timestamp_t timestamp, nrf_mesh_tx_token_t token); /** @@ -287,7 +287,7 @@ void core_tx_bearer_add(core_tx_bearer_t * p_bearer, */ void core_tx_complete(core_tx_bearer_t * p_bearer, core_tx_role_t role, - uint32_t timestamp, + timestamp_t timestamp, nrf_mesh_tx_token_t token); /** @} */ diff --git a/mesh/core/include/long_timer.h b/mesh/core/include/long_timer.h deleted file mode 100644 index 9e008b879..000000000 --- a/mesh/core/include/long_timer.h +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright (c) 2010 - 2020, Nordic Semiconductor ASA - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form, except as embedded into a Nordic - * Semiconductor ASA integrated circuit in a product or a software update for - * such product, must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. Neither the name of Nordic Semiconductor ASA nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * 4. This software, with or without modification, must only be used with a - * Nordic Semiconductor ASA integrated circuit. - * - * 5. Any software provided in binary form under this license must not be reverse - * engineered, decompiled, modified and/or disassembled. - * - * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef LONG_TIMER_H__ -#define LONG_TIMER_H__ - -#include "timer_scheduler.h" - -/** - * @defgroup LONG_TIMER Long timer - * Long running timer for the mesh. - * @{ - */ - - -/** Callback function type */ -typedef void (*lt_callback_t)(void * p_context); - -/** Long running timer context. */ -typedef struct -{ - uint64_t remaining_time_us; /**< Number of microseconds until the timer fires at the previous tick. - Use @ref lt_remaining_time to get actual value. */ - lt_callback_t callback; /**< Callback to call when the timer expires. */ - void * p_context; /**< User specified context passed in the callback. */ - timer_event_t event; /**< Timer event used to keep track of the remaining time. */ -} long_timer_t; - -/** - * Schedule a long running timer with a delay. - * - * The memory associated with the @p p_timer must be valid until the timer either fires or is aborted. - * - * @param[in,out] p_timer Timer to schedule. Rescheduling a running timer will cancel its original timeout value. - * @param[in] callback Callback to call when the timer finishes. - * @param[in,out] p_context Context to pass to the timer. - * @param[in] delay_us Delay in microseconds before the timer should fire. - */ -void lt_schedule(long_timer_t * p_timer, lt_callback_t callback, void * p_context, uint64_t delay_us); - -/** - * Get the time remaining until the timer fires. - * - * @param[in] p_timer Timer to check - * - * @returns The number of microseconds until the timer fires. - */ -uint64_t lt_remaining_time_get(const long_timer_t * p_timer); - -/** - * Abort a scheduled timer. - * - * The timer callback will not fire, and the memory can safely be freed. - * - * @param[in,out] p_timer Timer to abort. - */ -void lt_abort(long_timer_t * p_timer); - -/** @} */ - -#endif /* LONG_TIMER_H__ */ diff --git a/mesh/core/include/timer.h b/mesh/core/include/timer.h index c1add46e7..079074273 100644 --- a/mesh/core/include/timer.h +++ b/mesh/core/include/timer.h @@ -53,6 +53,12 @@ /** Get timestamp - ref, including rollover. */ #define TIMER_DIFF(timestamp, reference) timer_diff(timestamp, reference) +/** The maximum timer value. A user may substract any value from timestamp_t, which may result in + * negative value. Due to the nature of unsigned int type, the negative value will wrap around, + * which will result in a value greater than UINT64_MAX / 2. Values greater than UINT64_MAX / 2 are + * considered as negative. */ +#define TIMER_MAX (UINT64_MAX / 2) + /** * Checks whether the given time is older than the reference (occurs before in time). * @@ -64,10 +70,10 @@ TIMER_OLDER_THAN(t2, t1) => false @endverbatim */ -#define TIMER_OLDER_THAN(time, ref) (((uint32_t) (time)) - ((uint32_t) (ref)) > UINT32_MAX / 2) +#define TIMER_OLDER_THAN(time, ref) ((time) - (ref) > TIMER_MAX) /** Timestamp type for all time-values */ -typedef uint32_t timestamp_t; +typedef uint64_t timestamp_t; /** Callback type for callbacks at finished timers */ typedef void(* timer_callback_t)(timestamp_t); @@ -82,7 +88,7 @@ typedef void(* timer_callback_t)(timestamp_t); */ static inline timestamp_t timer_diff(timestamp_t time1, timestamp_t time2) { - if (time1 - time2 > UINT32_MAX / 2) + if (TIMER_OLDER_THAN(time1, time2)) { return time2 - time1; } diff --git a/mesh/core/include/timer_scheduler.h b/mesh/core/include/timer_scheduler.h index d025591b9..7077b9210 100644 --- a/mesh/core/include/timer_scheduler.h +++ b/mesh/core/include/timer_scheduler.h @@ -137,6 +137,15 @@ bool timer_sch_is_scheduled(const timer_event_t * p_timer_evt); */ void timer_sch_stop(void); +/** + * Get remaining time. + * + * @param[in] p_timer_evt Timer scheduler event context pointer. + * + * @returns Remaining time in us. + */ +timestamp_t timer_sch_remaining_time_get(const timer_event_t * p_timer_evt); + /** @} */ #endif /* TIMER_SCHEDULER_H__ */ diff --git a/mesh/core/include/timeslot_timer.h b/mesh/core/include/timeslot_timer.h index 41ad4e55e..db602a5c7 100644 --- a/mesh/core/include/timeslot_timer.h +++ b/mesh/core/include/timeslot_timer.h @@ -62,6 +62,9 @@ /** Timer index for getting timestamps */ #define TS_TIMER_INDEX_TIMESTAMP (3) +/** Analogue of TIMER_OLDER_THAN but for ts_timestamp_t. */ +#define TS_TIMER_OLDER_THAN(time, ref) ((time) - (ref) > UINT32_MAX / 2) + /** Timestamp type for time-values based on the timer working within timeslots*/ typedef uint32_t ts_timestamp_t; diff --git a/mesh/core/src/core_tx.c b/mesh/core/src/core_tx.c index e160065f0..0bb664631 100644 --- a/mesh/core/src/core_tx.c +++ b/mesh/core/src/core_tx.c @@ -194,7 +194,7 @@ uint32_t core_tx_bearer_count_get(void) void core_tx_complete(core_tx_bearer_t * p_bearer, core_tx_role_t role, - uint32_t timestamp, + timestamp_t timestamp, nrf_mesh_tx_token_t token) { NRF_MESH_ASSERT(p_bearer); diff --git a/mesh/core/src/core_tx_instaburst.c b/mesh/core/src/core_tx_instaburst.c index 0bcad3284..d240231bd 100644 --- a/mesh/core/src/core_tx_instaburst.c +++ b/mesh/core/src/core_tx_instaburst.c @@ -72,7 +72,7 @@ static core_tx_bearer_t m_bearer; static void instaburst_tx_complete_callback(instaburst_tx_t * p_instaburst, nrf_mesh_tx_token_t token, - uint32_t timestamp) + timestamp_t timestamp) { core_tx_role_t role = (core_tx_role_t) (p_instaburst - &m_instaburst[0]); core_tx_complete(&m_bearer, role, timestamp, token); diff --git a/mesh/core/src/long_timer.c b/mesh/core/src/long_timer.c deleted file mode 100644 index af2ad0331..000000000 --- a/mesh/core/src/long_timer.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) 2010 - 2020, Nordic Semiconductor ASA - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form, except as embedded into a Nordic - * Semiconductor ASA integrated circuit in a product or a software update for - * such product, must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. Neither the name of Nordic Semiconductor ASA nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * 4. This software, with or without modification, must only be used with a - * Nordic Semiconductor ASA integrated circuit. - * - * 5. Any software provided in binary form under this license must not be reverse - * engineered, decompiled, modified and/or disassembled. - * - * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "long_timer.h" -#include "utils.h" - -#define TIMER_STEP ((UINT32_MAX / 2) - 1) - -static void timer_handler(timestamp_t timestamp, void * p_context) -{ - long_timer_t * p_timer = p_context; - p_timer->remaining_time_us -= MIN(TIMER_STEP, p_timer->remaining_time_us); - - if (p_timer->remaining_time_us == 0) - { - p_timer->event.interval = 0; - p_timer->callback(p_timer->p_context); - } - else - { - p_timer->event.interval = MIN(TIMER_STEP, p_timer->remaining_time_us); - } -} - -void lt_schedule(long_timer_t * p_timer, lt_callback_t callback, void * p_context, uint64_t delay_us) -{ - p_timer->callback = callback; - p_timer->p_context = p_context; - p_timer->remaining_time_us = delay_us; - p_timer->event.p_context = p_timer; - p_timer->event.cb = timer_handler; - timer_sch_reschedule(&p_timer->event, timer_now() + MIN(TIMER_STEP, p_timer->remaining_time_us)); -} - -void lt_abort(long_timer_t * p_timer) -{ - timer_sch_abort(&p_timer->event); -} - -uint64_t lt_remaining_time_get(const long_timer_t * p_timer) -{ - timestamp_t diff = timer_diff(timer_now(), p_timer->event.timestamp); - - return p_timer->remaining_time_us > diff ? p_timer->remaining_time_us - diff : 0; -} diff --git a/mesh/core/src/lpn.c b/mesh/core/src/lpn.c index 081355449..f189a0fe1 100644 --- a/mesh/core/src/lpn.c +++ b/mesh/core/src/lpn.c @@ -48,7 +48,7 @@ #include "core_tx_lpn.h" #include "transport.h" #include "packet_mesh.h" -#include "long_timer.h" +#include "timer_scheduler.h" #include "utils.h" #include "event.h" #include "nrf_mesh_assert.h" @@ -188,7 +188,7 @@ typedef struct uint8_t fsn; uint8_t poll_attempts_count; uint8_t frndreq_attempts_count; - long_timer_t timeout_scheduler; + timer_event_t timeout_scheduler; transport_control_packet_t * p_subman_data; } lpn_t; @@ -202,10 +202,10 @@ DECLARE_GUARD_PROTOTYPE(GUARD_LIST) static void lpn_fsm_action(fsm_action_id_t action_id, void * p_data); static bool lpn_fsm_guard(fsm_action_id_t action_id, void * p_data); static void lpn_fault_task_post(void); -static void friend_request_receive_delay_handle(void * p_context); -static void friend_request_receive_window_handle(void * p_context); -static void receive_delay_handle(void * p_context); -static void timeout_handle(void * p_context); +static void friend_request_receive_delay_handle(timestamp_t timestamp, void * p_context); +static void friend_request_receive_window_handle(timestamp_t timestamp, void * p_context); +static void receive_delay_handle(timestamp_t timestamp, void * p_context); +static void timeout_handle(timestamp_t timestamp, void * p_context); static void friend_offer_handle(const transport_control_packet_t * p_control_packet, const nrf_mesh_rx_metadata_t * p_rx_metadata); static void friend_update_handle(const transport_control_packet_t * p_control_packet, @@ -217,6 +217,8 @@ static void transmit_and_reschedule(const transport_control_packet_t * p_packet, uint32_t receive_delay_ms, uint32_t receive_window_ms); static void event_send(const nrf_mesh_evt_t * p_evt); +static void timer_schedule(timer_event_t * p_timer, timer_sch_callback_t callback, void * p_context, + uint64_t delay_us); static const fsm_transition_t m_lpn_fsm_transition_table[] = { @@ -565,7 +567,7 @@ static void a_poll_schedule(void * p_data) { fsm_transac_data_t * p_trasac_data = p_data; - lt_schedule(&m_lpn.timeout_scheduler, timeout_handle, NULL, + timer_schedule(&m_lpn.timeout_scheduler, timeout_handle, NULL, p_trasac_data != NULL ? MS_TO_US((uint64_t)p_trasac_data->poll_scheduler.delay_ms) : 0ull); } @@ -666,7 +668,7 @@ static void friend_update_handle(const transport_control_packet_t * p_control_pa fsm_event_post(&m_lpn_fsm, E_FRIEND_UPDATE_RX, &data); } -static void friend_request_receive_delay_handle(void * p_context) +static void friend_request_receive_delay_handle(timestamp_t timestamp, void * p_context) { uint32_t receive_window_ms = (uint32_t) p_context; uint64_t scanning_time_us = SCANNING_TIME_US((uint64_t) receive_window_ms); @@ -680,20 +682,21 @@ static void friend_request_receive_delay_handle(void * p_context) uint32_t remaining_time_ms = MESH_LPN_FRIEND_REQUEST_TIMEOUT_MAX_MS - receive_window_ms; scanner_enable(); - lt_schedule(&m_lpn.timeout_scheduler, - friend_request_receive_window_handle, - (void *) remaining_time_ms, - scanning_time_us); + timer_schedule(&m_lpn.timeout_scheduler, + friend_request_receive_window_handle, + (void *) remaining_time_ms, + scanning_time_us); } -static void friend_request_receive_window_handle(void * p_context) +static void friend_request_receive_window_handle(timestamp_t timestamp, void * p_context) { uint32_t remaining_time_ms = (uint32_t) p_context; scanner_disable(); if (remaining_time_ms > 0) { - lt_schedule(&m_lpn.timeout_scheduler, timeout_handle, NULL, MS_TO_US((uint64_t) remaining_time_ms)); + timer_schedule(&m_lpn.timeout_scheduler, timeout_handle, NULL, + MS_TO_US((uint64_t) remaining_time_ms)); } else { @@ -701,15 +704,16 @@ static void friend_request_receive_window_handle(void * p_context) } } -static void receive_delay_handle(void * p_context) +static void receive_delay_handle(timestamp_t timestamp, void * p_context) { uint32_t receive_window_ms = (uint32_t) p_context; scanner_enable(); - lt_schedule(&m_lpn.timeout_scheduler, timeout_handle, NULL, SCANNING_TIME_US((uint64_t) receive_window_ms)); + timer_schedule(&m_lpn.timeout_scheduler, timeout_handle, NULL, + SCANNING_TIME_US((uint64_t) receive_window_ms)); } -static void timeout_handle(void * p_context) +static void timeout_handle(timestamp_t timestamp, void * p_context) { (void)p_context; @@ -745,26 +749,26 @@ static void transmit_and_reschedule(const transport_control_packet_t * p_packet, { if (token == NRF_MESH_FRIEND_REQUEST_TOKEN) { - lt_schedule(&m_lpn.timeout_scheduler, - friend_request_receive_delay_handle, - (void*) receive_window_ms, - MS_TO_US((uint64_t)receive_delay_ms) + TIMER_JITTER_US); + timer_schedule(&m_lpn.timeout_scheduler, + friend_request_receive_delay_handle, + (void*) receive_window_ms, + MS_TO_US((uint64_t)receive_delay_ms) + TIMER_JITTER_US); } else { - lt_schedule(&m_lpn.timeout_scheduler, - receive_delay_handle, - (void*) receive_window_ms, - MS_TO_US((uint64_t)receive_delay_ms) + TIMER_JITTER_US); + timer_schedule(&m_lpn.timeout_scheduler, + receive_delay_handle, + (void*) receive_window_ms, + MS_TO_US((uint64_t)receive_delay_ms) + TIMER_JITTER_US); } } else { __LOG(LOG_SRC_FSM, LOG_LEVEL_DBG1, "LPN transport TX failed (%u)\n", err_code); - lt_schedule(&m_lpn.timeout_scheduler, - timeout_handle, - NULL, - MS_TO_US((uint64_t)receive_delay_ms + receive_window_ms)); + timer_schedule(&m_lpn.timeout_scheduler, + timeout_handle, + NULL, + MS_TO_US((uint64_t)receive_delay_ms + receive_window_ms)); } } @@ -799,7 +803,7 @@ static void lpn_terminate_task(void) { fsm_event_post(&m_lpn_fsm, E_TERMINATE, NULL); - lt_abort(&m_lpn.timeout_scheduler); + timer_sch_abort(&m_lpn.timeout_scheduler); nrf_mesh_evt_t evt; termination_evt_prepare(&evt, NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_LPN); @@ -849,6 +853,15 @@ static bool is_friendship_secmat(const network_packet_metadata_t * p_net_metadat (intptr_t) p_frnd_secmat == (intptr_t) p_net_metadata->p_security_material); } +static void timer_schedule(timer_event_t * p_timer, timer_sch_callback_t callback, void * p_context, + uint64_t delay_us) +{ + p_timer->cb = callback; + p_timer->p_context = p_context; + p_timer->interval = 0ul; + timer_sch_reschedule(p_timer, timer_now() + delay_us); +} + /******************************* API *****************************************/ void mesh_lpn_init(void) { diff --git a/mesh/core/src/timer.c b/mesh/core/src/timer.c index 949fef547..2d4e8b5e9 100644 --- a/mesh/core/src/timer.c +++ b/mesh/core/src/timer.c @@ -52,12 +52,12 @@ #define PROTECTION_MARGIN_FOR_OVFW_HANDLER 2ul #define TIMER_US_TO_TICKS(US) \ - ((uint32_t)ROUNDED_DIV( \ + (ROUNDED_DIV( \ (US) * (uint64_t)APP_TIMER_CLOCK_FREQ, \ 1000000ull * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1))) #define TIMER_TICKS_TO_US(TICKS) \ - ((uint32_t)ROUNDED_DIV( \ + (ROUNDED_DIV( \ (TICKS) * 1000000ull * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1), \ APP_TIMER_CLOCK_FREQ)) @@ -122,7 +122,7 @@ void timer_init(void) timestamp_t timer_now(void) { uint32_t was_masked; - uint32_t ovfw_counter; + uint64_t ovfw_counter; uint32_t sample; _DISABLE_IRQS(was_masked); @@ -153,7 +153,7 @@ void timer_start(timestamp_t timestamp, timer_callback_t cb) time_diff = timestamp - time_now; } - uint32_t timeout_ticks = TIMER_US_TO_TICKS(time_diff); + uint64_t timeout_ticks = TIMER_US_TO_TICKS(time_diff); _DISABLE_IRQS(was_masked); uint32_t ticks_now = NRF_RTC1->COUNTER; diff --git a/mesh/core/src/timer_scheduler.c b/mesh/core/src/timer_scheduler.c index 6b1e9ec15..76c22fd8d 100644 --- a/mesh/core/src/timer_scheduler.c +++ b/mesh/core/src/timer_scheduler.c @@ -236,3 +236,16 @@ void timer_sch_stop(void) m_scheduler.p_head = NULL; timer_stop(); } + +timestamp_t timer_sch_remaining_time_get(const timer_event_t * p_timer_evt) +{ + timestamp_t time_now = timer_now(); + timestamp_t diff = 0; + + if (TIMER_OLDER_THAN(time_now, p_timer_evt->timestamp)) + { + diff = timer_diff(time_now, p_timer_evt->timestamp); + } + + return diff; +} diff --git a/mesh/core/src/timeslot_timer.c b/mesh/core/src/timeslot_timer.c index a069e663e..71602d11c 100644 --- a/mesh/core/src/timeslot_timer.c +++ b/mesh/core/src/timeslot_timer.c @@ -53,7 +53,7 @@ * Static globals *****************************************************************************/ /** Array of function pointers for callbacks for each timer. */ -static timer_callback_t m_callbacks[TIMER_COMPARE_COUNT]; +static ts_timer_callback_t m_callbacks[TIMER_COMPARE_COUNT]; /** Array of PPI tasks to trigger on timeout. */ static uint32_t* mp_ppi_tasks[TIMER_COMPARE_COUNT]; /** Time captured at the beginning of the current timeslot. */ diff --git a/mesh/core/src/transport.c b/mesh/core/src/transport.c index e0f289c99..02fe10104 100644 --- a/mesh/core/src/transport.c +++ b/mesh/core/src/transport.c @@ -531,7 +531,7 @@ static void tx_retry_timer_reset(trs_sar_ctx_t * p_sar_ctx) { NRF_MESH_ASSERT(p_sar_ctx->session.session_type == TRS_SAR_SESSION_TX); - uint32_t next_timeout = timer_now(); + timestamp_t next_timeout = timer_now(); /* For non-unicast addresses, we're not going to get any acknowledgements, so there's no * point in scaling the retry interval according to TTL. */ @@ -1855,7 +1855,7 @@ static void abort_timeout(timestamp_t timestamp, void * p_context) sar_ctx_cancel(p_sar_ctx, NRF_MESH_SAR_CANCEL_REASON_TIMEOUT); } -static void tx_complete(core_tx_role_t role, uint32_t bearer_index, uint32_t timestamp, nrf_mesh_tx_token_t token) +static void tx_complete(core_tx_role_t role, uint32_t bearer_index, timestamp_t timestamp, nrf_mesh_tx_token_t token) { if (role == CORE_TX_ROLE_ORIGINATOR && token != NRF_MESH_SAR_TOKEN) { diff --git a/mesh/dfu/src/nrf_mesh_dfu.c b/mesh/dfu/src/nrf_mesh_dfu.c index 5e9bdb070..49fde514b 100644 --- a/mesh/dfu/src/nrf_mesh_dfu.c +++ b/mesh/dfu/src/nrf_mesh_dfu.c @@ -82,7 +82,7 @@ NRF_MESH_STATIC_ASSERT(NRF_MESH_DFU_ROLE__LAST <= UINT8_MAX); * periodic transmit gets its own slot. */ typedef struct { - uint32_t order_time; /**< Timestamp when the packet was first ordered. */ + timestamp_t order_time; /**< Timestamp when the packet was first ordered. */ uint16_t tx_randomization_offset_us; /**< Randomized time deviation from the normal for the next transmission. */ bl_radio_interval_type_t interval_type; /**< Type of interval for this periodic transmit. */ nrf_mesh_dfu_packet_t dfu_packet; /**< DFU packet being sent on this slot. */ @@ -196,7 +196,7 @@ static uint32_t get_curr_fwid(nrf_mesh_dfu_type_t type, nrf_mesh_fwid_t* p_fwid) return NRF_SUCCESS; } -static uint32_t next_tx_timeout(dfu_tx_t* p_tx) +static timestamp_t next_tx_timeout(dfu_tx_t* p_tx) { switch (p_tx->interval_type) { @@ -285,14 +285,14 @@ static void tx_timeout_handled(dfu_tx_t * p_tx) } } -static void tx_timeout(uint32_t timestamp, void* p_context) +static void tx_timeout(timestamp_t timestamp, void* p_context) { - uint32_t next_timeout = timestamp + (UINT32_MAX / 2); + timestamp_t next_timeout = timestamp + (UINT32_MAX / 2); for (uint32_t i = 0; i < NRF_MESH_DFU_TX_SLOTS; ++i) { if (tx_slot_in_use(&m_tx_slots[i])) { - uint32_t timeout = next_tx_timeout(&m_tx_slots[i]); + timestamp_t timeout = next_tx_timeout(&m_tx_slots[i]); if (TIMER_OLDER_THAN(timeout, (timestamp + DFU_TX_TIMER_MARGIN_US))) { if (m_tx_slots[i].rx_count >= DFU_TX_REDUNDANCY_MAX) @@ -320,9 +320,9 @@ static void tx_timeout(uint32_t timestamp, void* p_context) timer_sch_reschedule(&m_tx_timer_evt, next_timeout); } -static void abort_timeout(uint32_t timestamp, void* p_context) +static void abort_timeout(timestamp_t timestamp, void* p_context) { - __LOG(LOG_SRC_DFU, LOG_LEVEL_INFO, "ABORT Timeout fired @%d\n", timestamp); + __LOG(LOG_SRC_DFU, LOG_LEVEL_INFO, "ABORT Timeout fired @%lu\n", timestamp); bl_cmd_t abort_cmd; abort_cmd.type = BL_CMD_TYPE_DFU_ABORT; if (nrf_mesh_dfu_cmd_send(&abort_cmd) == NRF_SUCCESS) @@ -331,9 +331,9 @@ static void abort_timeout(uint32_t timestamp, void* p_context) } } -static void timer_timeout(uint32_t timestamp, void* p_context) +static void timer_timeout(timestamp_t timestamp, void* p_context) { - __LOG(LOG_SRC_DFU, LOG_LEVEL_INFO, "Timeout fired @%d\n", timestamp); + __LOG(LOG_SRC_DFU, LOG_LEVEL_INFO, "Timeout fired @%lu\n", timestamp); bl_cmd_t timeout_cmd; timeout_cmd.type = BL_CMD_TYPE_TIMEOUT; timeout_cmd.params.timeout.timer_index = 0; @@ -567,7 +567,7 @@ static uint32_t dfu_evt_handler(const bl_evt_t* p_evt) /* Disable the slot */ p_tx_slot->repeats = 0; - uint32_t time_now = timer_now(); + timestamp_t time_now = timer_now(); /* Fill the TX slot. */ p_tx_slot->interval_type = p_evt->params.tx.radio.interval_type; @@ -673,7 +673,7 @@ static uint32_t dfu_cmd_handler_set(bl_if_cmd_handler_t handler) } #endif -static void tx_complete_cb(broadcast_params_t * p_broadcast, uint32_t timestamp) +static void tx_complete_cb(broadcast_params_t * p_broadcast, timestamp_t timestamp) { /* Do nothing */ } diff --git a/mesh/friend/src/friend.c b/mesh/friend/src/friend.c index caa570cb9..362b6e6d1 100644 --- a/mesh/friend/src/friend.c +++ b/mesh/friend/src/friend.c @@ -58,7 +58,7 @@ #include "friend_queue.h" #include "friend_sublist.h" -#include "long_timer.h" +#include "timer_scheduler.h" #include "core_tx_friend.h" #include "log.h" @@ -96,8 +96,8 @@ typedef struct uint8_t fsn; /**< Friend Sequence Number. */ friend_queue_t queue; /**< Friend queue. */ friend_sublist_t sublist; /**< Subscription list of the LPN. */ - long_timer_t poll_timeout; /**< Poll timeout timer. */ - long_timer_t clear_repeat_timer; /**< Friend clear repeat timer. */ + timer_event_t poll_timeout; /**< Poll timeout timer. */ + timer_event_t clear_repeat_timer; /**< Friend clear repeat timer. */ uint8_t clear_repeat_count; /**< Number of times to send the friend clear message. */ core_tx_friend_t bearer; /**< Bearer instance. */ } friendship_t; @@ -107,7 +107,7 @@ typedef struct uint16_t last_lpn; /**< Address of the recently seen LPN. */ uint16_t last_req_count; /**< Value of the LPN counter received in the friend request. */ nrf_mesh_tx_token_t token; /**< Bearer TX token. */ - long_timer_t confirm_send_timer; /**< Respond to valid Friend clear message in this period. */ + timer_event_t confirm_send_timer; /**< Respond to valid Friend clear message in this period. */ } recent_lpns_t; typedef struct @@ -126,9 +126,9 @@ typedef struct /***************************************************************************** * Static function declarations *****************************************************************************/ -static void poll_timeout_cb(void * p_context); -static void friend_clear_timeout_cb(void * p_context); -static void confirm_send_timer_cb(void * p_context); +static void poll_timeout_cb(timestamp_t timestamp, void * p_context); +static void friend_clear_timeout_cb(timestamp_t timestamp, void * p_context); +static void confirm_send_timer_cb(timestamp_t timestamp, void * p_context); /***************************************************************************** * Static globals @@ -187,6 +187,14 @@ MESH_CONFIG_ENTRY(mesh_opt_friend, /* Utility functions */ +static void timer_schedule(timer_event_t * p_timer, timer_sch_callback_t callback, void * p_context, uint64_t delay_us) +{ + p_timer->cb = callback; + p_timer->p_context = p_context; + p_timer->interval = 0ul; + timer_sch_reschedule(p_timer, timer_now() + delay_us); +} + /* Find entry matching with given LPN address */ static uint32_t confirm_timer_entry_find(uint16_t lpn_addr) { @@ -206,7 +214,7 @@ static uint32_t confirm_timer_entry_find(uint16_t lpn_addr) /* Find empty entry, and if not found, then nearest expiring entry. */ static uint32_t confirm_timer_entry_get(void) { - uint64_t lowest_remaining_time_us = UINT64_MAX; + uint64_t lowest_remaining_time_us = TIMER_MAX; uint32_t idx = 0; for (uint32_t i = 0; i < FRIEND_RECENT_LPNS_LIST_COUNT; i++) @@ -218,7 +226,8 @@ static uint32_t confirm_timer_entry_get(void) } else { - uint64_t current_remaining_time_us = lt_remaining_time_get(&m_friend.recent_lpns[i].confirm_send_timer); + timestamp_t current_remaining_time_us = timer_sch_remaining_time_get( + &m_friend.recent_lpns[i].confirm_send_timer); if (current_remaining_time_us < lowest_remaining_time_us) { @@ -247,7 +256,7 @@ static void confirm_timer_add(uint16_t lpn_addr, uint32_t poll_timeout_ms, uint3 lpn_addr, poll_timeout_ms, entry); m_friend.recent_lpns[entry].last_lpn = lpn_addr; m_friend.recent_lpns[entry].last_req_count = req_count; - lt_schedule(&m_friend.recent_lpns[entry].confirm_send_timer, + timer_schedule(&m_friend.recent_lpns[entry].confirm_send_timer, confirm_send_timer_cb, &m_friend.recent_lpns[entry], MS_TO_US((uint64_t)poll_timeout_ms)); @@ -262,7 +271,7 @@ static void confirm_timer_clear(uint16_t lpn_addr) { __LOG(LOG_SRC_FRIEND, LOG_LEVEL_DBG1, "Cleared\n"); m_friend.recent_lpns[i].last_lpn = NRF_MESH_ADDR_UNASSIGNED; - lt_abort(&m_friend.recent_lpns[i].confirm_send_timer); + timer_sch_abort(&m_friend.recent_lpns[i].confirm_send_timer); } } } @@ -333,7 +342,7 @@ static void friendship_state_reset(friendship_t * p_friendship) core_tx_friend_disable(&p_friendship->bearer); friend_queue_clear(&p_friendship->queue); friend_sublist_clear(&p_friendship->sublist); - lt_abort(&p_friendship->poll_timeout); + timer_sch_abort(&p_friendship->poll_timeout); } static void friendship_terminate(friendship_t * p_friendship, @@ -413,13 +422,13 @@ static friendship_t * friendship_find(uint16_t lpn_address) static void poll_timeout_schedule(friendship_t * p_friendship, timestamp_t rx_timestamp, uint64_t poll_timeout_us) { timestamp_t diff = timer_now() - rx_timestamp; - lt_schedule(&p_friendship->poll_timeout, poll_timeout_cb, p_friendship, + timer_schedule(&p_friendship->poll_timeout, poll_timeout_cb, p_friendship, diff < poll_timeout_us ? poll_timeout_us - diff : 0); } static void friend_clear_timeout_schedule(friendship_t * p_friendship, uint64_t clear_repeat_timeout_us) { - lt_schedule(&p_friendship->clear_repeat_timer, friend_clear_timeout_cb, p_friendship, clear_repeat_timeout_us); + timer_schedule(&p_friendship->clear_repeat_timer, friend_clear_timeout_cb, p_friendship, clear_repeat_timeout_us); } static friendship_t * free_friendship_context_get(void) @@ -693,7 +702,7 @@ static void friend_clear_confirm_tx(uint16_t lpn_src, uint16_t lpn_req_count, status == NRF_ERROR_FORBIDDEN); } -static void poll_timeout_cb(void * p_context) +static void poll_timeout_cb(timestamp_t timestamp, void * p_context) { NRF_MESH_ASSERT_DEBUG(p_context != NULL); friendship_t * p_friendship = p_context; @@ -702,7 +711,7 @@ static void poll_timeout_cb(void * p_context) NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_TIMEOUT); } -static void friend_clear_timeout_cb(void * p_context) +static void friend_clear_timeout_cb(timestamp_t timestamp, void * p_context) { friendship_t * p_friendship = p_context; @@ -717,7 +726,7 @@ static void friend_clear_timeout_cb(void * p_context) } } -static void confirm_send_timer_cb(void * p_context) +static void confirm_send_timer_cb(timestamp_t timestamp, void * p_context) { recent_lpns_t * p_recent_lpns = p_context; __LOG(LOG_SRC_FRIEND, LOG_LEVEL_DBG1, "Removing last seen LPN: 0x%04x\n", p_recent_lpns->last_lpn); @@ -1044,7 +1053,7 @@ static void friend_clear_confirm_handle(const transport_control_packet_t * p_con return; } - lt_abort(&p_friendship->clear_repeat_timer); + timer_sch_abort(&p_friendship->clear_repeat_timer); } static void friend_sublist_add_handle(const transport_control_packet_t * p_control_packet, @@ -1578,7 +1587,8 @@ uint32_t friend_remaining_poll_timeout_time_get(uint16_t src) friendship_t * p_friendship = friendship_find(src); if (p_friendship != NULL) { - return ROUNDED_DIV(US_TO_MS(lt_remaining_time_get(&p_friendship->poll_timeout)), 100); + timestamp_t remaining_time = timer_sch_remaining_time_get(&p_friendship->poll_timeout); + return ROUNDED_DIV(US_TO_MS(remaining_time), 100); } else { diff --git a/mesh/prov/src/prov_bearer_adv.c b/mesh/prov/src/prov_bearer_adv.c index 9f4f583b0..d89a04bb3 100644 --- a/mesh/prov/src/prov_bearer_adv.c +++ b/mesh/prov/src/prov_bearer_adv.c @@ -1239,7 +1239,7 @@ static uint32_t prov_bearer_adv_tx(prov_bearer_t * p_bearer, const uint8_t * p_d reset_timeout_timer(p_pb_adv); /* Add the time we'll spend transmitting the packets to the retry interval. */ - uint32_t time_now = timer_now(); + timestamp_t time_now = timer_now(); uint32_t retry_interval = PROV_BEARER_ADV_TRANSACTION_BASE_RETRY_INTERVAL_US + (transaction_total_segment_count_get(length) * p_pb_adv->advertiser.config.advertisement_interval_us); p_pb_adv->timeout_event.interval = retry_interval; diff --git a/mesh/test/CMakeLists.txt b/mesh/test/CMakeLists.txt index 1e76ecc83..591ad3a2d 100644 --- a/mesh/test/CMakeLists.txt +++ b/mesh/test/CMakeLists.txt @@ -1375,7 +1375,7 @@ set(lpn_srcs ../core/src/log.c ${CMOCK_BIN}/transport_mock.c ${CMOCK_BIN}/bearer_event_mock.c - ${CMOCK_BIN}/long_timer_mock.c + ${CMOCK_BIN}/timer_scheduler_mock.c ${CMOCK_BIN}/scanner_mock.c ${CMOCK_BIN}/event_mock.c ${CMOCK_BIN}/nrf_mesh_externs_mock.c @@ -1429,7 +1429,7 @@ set(friend_srcs ${CMOCK_BIN}/event_mock.c ${CMOCK_BIN}/net_state_mock.c ${CMOCK_BIN}/device_state_manager_mock.c - ${CMOCK_BIN}/long_timer_mock.c) + ${CMOCK_BIN}/timer_scheduler_mock.c) add_unit_test(friend "${friend_srcs}" "${include_directories}" "${compile_options};-DMESH_FEATURE_FRIEND_ENABLED") set(friend_sublist_test_srcs @@ -1545,14 +1545,6 @@ set(scene_server_srcs ) add_unit_test(scene_server "${scene_server_srcs}" "${include_directories}" "${compile_options};-DNRF52;-DSCENE_SETUP_SERVER_INSTANCES_MAX=1") -set(long_timer_srcs - src/ut_long_timer.c - ../core/src/long_timer.c - ${CMOCK_BIN}/timer_scheduler_mock.c - ${CMOCK_BIN}/timer_mock.c - ) -add_unit_test(long_timer "${long_timer_srcs}" "${include_directories}" "${compile_options}") - set(emergency_cache_srcs src/ut_emergency_cache.c ../core/src/emergency_cache.c diff --git a/mesh/test/src/proxy_test_common.c b/mesh/test/src/proxy_test_common.c index 4ed092aed..778f0fc9a 100644 --- a/mesh/test/src/proxy_test_common.c +++ b/mesh/test/src/proxy_test_common.c @@ -218,7 +218,7 @@ void core_tx_bearer_add(core_tx_bearer_t * p_bearer, void core_tx_complete(core_tx_bearer_t * p_bearer, core_tx_role_t role, - uint32_t timestamp, + timestamp_t timestamp, nrf_mesh_tx_token_t token) { diff --git a/mesh/test/src/ut_core_tx.c b/mesh/test/src/ut_core_tx.c index 225bf2cff..2b38536b8 100644 --- a/mesh/test/src/ut_core_tx.c +++ b/mesh/test/src/ut_core_tx.c @@ -154,7 +154,7 @@ struct void tx_complete_cb(core_tx_role_t role, uint32_t bearer_index, - uint32_t timestamp, + timestamp_t timestamp, nrf_mesh_tx_token_t token) { TEST_ASSERT_TRUE(m_tx_complete_expect.calls > 0); diff --git a/mesh/test/src/ut_friend.c b/mesh/test/src/ut_friend.c index db8b685ab..75e6d6811 100644 --- a/mesh/test/src/ut_friend.c +++ b/mesh/test/src/ut_friend.c @@ -54,7 +54,7 @@ #include "test_assert.h" #include "mesh_opt_core_mock.h" #include "device_state_manager_mock.h" -#include "long_timer_mock.h" +#include "timer_scheduler_mock.h" #include "mesh_opt_friend.h" #include "log.h" @@ -89,7 +89,7 @@ static nrf_mesh_network_secmat_t m_friend_secmat; static uint16_t m_friend_counter; static uint16_t m_lpn_src = LPN_SRC; static uint32_t m_time_now; -static long_timer_t * mp_confirm_send_timer; +static timer_event_t * mp_confirm_send_timer; static nrf_mesh_rx_metadata_t m_rx_metadata; static network_tx_packet_buffer_t m_net_buf; static uint8_t m_trs_pdu[PACKET_MESH_TRS_UNSEG_ACCESS_MAX_SIZE]; @@ -175,11 +175,11 @@ static void event_handle_stub(const nrf_mesh_evt_t * p_evt, TEST_ASSERT_EQUAL_nrf_mesh_evt_t(expected, (*p_evt)); } -uint64_t lt_remaining_time_get_stub(const long_timer_t * p_timer, int num_calls) +timestamp_t timer_sch_remaining_time_get_stub(const timer_event_t * p_timer_evt, int num_calls) { - TEST_ASSERT_NOT_NULL(p_timer); + TEST_ASSERT_NOT_NULL(p_timer_evt); - return p_timer->remaining_time_us; + return p_timer_evt->timestamp - timer_now(); } static void unicast_address_get_Expect(void) @@ -241,20 +241,19 @@ timestamp_t timer_now(void) return m_time_now; } -static void lt_schedule_stub_cb(long_timer_t * p_timer, lt_callback_t callback, void * p_context, uint64_t delay_us, int num) +static void timer_sch_reschedule_stub_cb(timer_event_t* p_timer_evt, timestamp_t new_timeout, int num) { - TEST_ASSERT(p_timer != NULL); - TEST_ASSERT(callback != NULL); - TEST_ASSERT(p_context != NULL); + TEST_ASSERT(p_timer_evt != NULL); + TEST_ASSERT(p_timer_evt->cb != NULL); + TEST_ASSERT(p_timer_evt->p_context != NULL); - mp_confirm_send_timer = p_timer; - mp_confirm_send_timer->callback = callback; - mp_confirm_send_timer->p_context = p_context; + mp_confirm_send_timer = p_timer_evt; + p_timer_evt->timestamp = new_timeout; } -static void timer_trigger(long_timer_t * p_timer) +static void timer_trigger(timer_event_t * p_timer) { - p_timer->callback(p_timer->p_context); + p_timer->cb(p_timer->timestamp, p_timer->p_context); } static void friend_rx(transport_control_opcode_t opcode, @@ -355,10 +354,14 @@ static void friend_request_Receive(uint16_t prev_address, uint16_t dst) static void poll_timeout_schedule_Expect(timestamp_t rx_timestamp, uint32_t timeout_us) { timestamp_t diff = timer_now() - rx_timestamp; - lt_schedule_Expect(NULL, NULL, NULL, diff < timeout_us ? timeout_us - diff : 0); - lt_schedule_IgnoreArg_p_timer(); - lt_schedule_IgnoreArg_callback(); - lt_schedule_IgnoreArg_p_context(); + timestamp_t expect = timer_now(); + + if (diff < timeout_us) { + expect += timeout_us - diff; + } + + timer_sch_reschedule_Expect(NULL, expect); + timer_sch_reschedule_IgnoreArg_p_timer_evt(); } static uint32_t network_packet_alloc_stub(network_tx_packet_buffer_t * p_buf, @@ -527,7 +530,7 @@ static void friendship_state_reset_Expect(void) { core_tx_friend_disable_ExpectAnyArgs(); friend_sublist_clear_ExpectAnyArgs(); - lt_abort_ExpectAnyArgs(); + timer_sch_abort_ExpectAnyArgs(); } static void friendship_terminated_event_Expect(void) @@ -560,7 +563,7 @@ static void friendship_Terminate(void) const mesh_friendship_t * p_friendships[MESH_FRIEND_FRIENDSHIP_COUNT]; uint8_t count = MESH_FRIEND_FRIENDSHIP_COUNT; TEST_ASSERT_EQUAL(NRF_SUCCESS, mesh_friend_friendships_get(&p_friendships[0], &count)); - lt_schedule_StubWithCallback(lt_schedule_stub_cb); + timer_sch_reschedule_StubWithCallback(timer_sch_reschedule_stub_cb); TEST_ASSERT_EQUAL(NRF_SUCCESS, mesh_friend_friendship_terminate(p_friendships[0])); } @@ -572,7 +575,7 @@ static void friendship_Terminate_by_friend_clear(void) const mesh_friendship_t * p_friendships[MESH_FRIEND_FRIENDSHIP_COUNT]; uint8_t count = MESH_FRIEND_FRIENDSHIP_COUNT; TEST_ASSERT_EQUAL(NRF_SUCCESS, mesh_friend_friendships_get(&p_friendships[0], &count)); - lt_schedule_StubWithCallback(lt_schedule_stub_cb); + timer_sch_reschedule_StubWithCallback(timer_sch_reschedule_stub_cb); friend_clear_confirm_Expect(p_friendships[0]->lpn.src, p_friendships[0]->lpn.request_count); friend_clear_Receive(p_friendships[0]->lpn.src, p_friendships[0]->lpn.request_count); @@ -602,7 +605,7 @@ void setUp(void) event_queue_Init(); nrf_mesh_events_mock_Init(); device_state_manager_mock_Init(); - long_timer_mock_Init(); + timer_scheduler_mock_Init(); __LOG_INIT(0xFFFFFFFF, LOG_LEVEL_DBG3, LOG_CALLBACK_DEFAULT); @@ -671,8 +674,8 @@ void tearDown(void) nrf_mesh_events_mock_Destroy(); device_state_manager_mock_Verify(); device_state_manager_mock_Destroy(); - long_timer_mock_Verify(); - long_timer_mock_Destroy(); + timer_scheduler_mock_Verify(); + timer_scheduler_mock_Destroy(); } /******************************************************************************* @@ -919,7 +922,7 @@ void test_confirm_send_timer(void) uint32_t i; /* Last termination should over-write the first entry (corresponding to LPN_SRC) */ - lt_remaining_time_get_StubWithCallback(lt_remaining_time_get_stub); + timer_sch_remaining_time_get_StubWithCallback(timer_sch_remaining_time_get_stub); for (i = 0; i < (FRIEND_RECENT_LPNS_LIST_COUNT + 1); i++) { friendship_Establish(); diff --git a/mesh/test/src/ut_long_timer.c b/mesh/test/src/ut_long_timer.c deleted file mode 100644 index 812e40a7c..000000000 --- a/mesh/test/src/ut_long_timer.c +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright (c) 2010 - 2020, Nordic Semiconductor ASA - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form, except as embedded into a Nordic - * Semiconductor ASA integrated circuit in a product or a software update for - * such product, must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 3. Neither the name of Nordic Semiconductor ASA nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * 4. This software, with or without modification, must only be used with a - * Nordic Semiconductor ASA integrated circuit. - * - * 5. Any software provided in binary form under this license must not be reverse - * engineered, decompiled, modified and/or disassembled. - * - * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "long_timer.h" - -#include -#include -#include "utils.h" - -#include "timer_scheduler_mock.h" -#include "timer_mock.h" - -#define TIMER_STEP ((UINT32_MAX / 2) - 1) -#define TIME_NOW (0x2a) - -static long_timer_t m_timer; -static bool m_is_cb_expected; - -void setUp(void) -{ - timer_scheduler_mock_Init(); - timer_mock_Init(); -} - -void tearDown(void) -{ - timer_scheduler_mock_Verify(); - timer_scheduler_mock_Destroy(); - timer_mock_Verify(); - timer_mock_Destroy(); -} - -static void lt_cb(void * p_context) -{ - TEST_ASSERT_EQUAL(m_timer.p_context, p_context); - TEST_ASSERT_TRUE(m_is_cb_expected); - m_is_cb_expected = false; -} - -static void single_timer_schedule(uint64_t time) -{ - timer_now_ExpectAndReturn(TIME_NOW); - timer_sch_reschedule_Expect(&m_timer.event, TIME_NOW + MIN(TIMER_STEP, time)); - lt_schedule(&m_timer, lt_cb, single_timer_schedule, time); - - TEST_ASSERT_EQUAL(m_timer.callback, lt_cb); - TEST_ASSERT_EQUAL(m_timer.p_context, single_timer_schedule); - TEST_ASSERT_EQUAL(m_timer.remaining_time_us, time); - TEST_ASSERT_EQUAL(m_timer.event.p_context, &m_timer); - TEST_ASSERT_NOT_NULL(m_timer.event.cb); -} - -static void timer_handler_rounds(uint64_t rounds) -{ - uint64_t remaining_time_us = m_timer.remaining_time_us; - - while (rounds != 0) - { - remaining_time_us -= MIN(TIMER_STEP, remaining_time_us); - - if (rounds == 1) - { - m_is_cb_expected = true; - } - m_timer.event.cb(0, (void *)&m_timer); - - rounds--; - TEST_ASSERT_EQUAL(remaining_time_us, m_timer.remaining_time_us); - TEST_ASSERT_EQUAL(rounds == 0 ? 0 : MIN(TIMER_STEP, remaining_time_us), m_timer.event.interval); - } - - TEST_ASSERT_EQUAL(0, remaining_time_us); -} - -void test_lt_schedule(void) -{ - single_timer_schedule(TIMER_STEP - 1); - single_timer_schedule(TIMER_STEP + 1); -} - -void test_lt_timer_handler(void) -{ - single_timer_schedule(0); - timer_handler_rounds(CEIL_DIV(0, TIMER_STEP)); - - single_timer_schedule(TIMER_STEP); - timer_handler_rounds(CEIL_DIV(TIMER_STEP, TIMER_STEP)); - - single_timer_schedule(TIMER_STEP + 1); - timer_handler_rounds(CEIL_DIV(TIMER_STEP + 1, TIMER_STEP)); - - single_timer_schedule(100ull * TIMER_STEP + 42); - timer_handler_rounds(CEIL_DIV(100ull * TIMER_STEP + 42, TIMER_STEP)); -} - -void test_lt_abort(void) -{ - timer_sch_abort_Expect(&m_timer.event); - lt_abort(&m_timer); -} - -void test_lt_remaining_time_get(void) -{ - single_timer_schedule(0); - timer_now_ExpectAndReturn(TIME_NOW); - m_timer.event.timestamp = TIME_NOW; - TEST_ASSERT_EQUAL(0, lt_remaining_time_get(&m_timer)); - - single_timer_schedule(TIMER_STEP); - m_timer.event.timestamp = TIME_NOW; - timer_now_ExpectAndReturn(2 * TIME_NOW); - TEST_ASSERT_EQUAL(TIMER_STEP - TIME_NOW, lt_remaining_time_get(&m_timer)); -} diff --git a/mesh/test/src/ut_lpn.c b/mesh/test/src/ut_lpn.c index 97c18e30b..128ce14e1 100644 --- a/mesh/test/src/ut_lpn.c +++ b/mesh/test/src/ut_lpn.c @@ -45,7 +45,7 @@ #include "transport_mock.h" #include "bearer_event_mock.h" -#include "long_timer_mock.h" +#include "timer_scheduler_mock.h" #include "scanner_mock.h" #include "event_mock.h" #include "nrf_mesh_externs_mock.h" @@ -54,7 +54,7 @@ void setUp(void) { transport_mock_Init(); bearer_event_mock_Init(); - long_timer_mock_Init(); + timer_scheduler_mock_Init(); scanner_mock_Init(); event_mock_Init(); nrf_mesh_externs_mock_Init(); @@ -66,8 +66,8 @@ void tearDown(void) transport_mock_Destroy(); bearer_event_mock_Verify(); bearer_event_mock_Destroy(); - long_timer_mock_Verify(); - long_timer_mock_Destroy(); + timer_scheduler_mock_Verify(); + timer_scheduler_mock_Destroy(); scanner_mock_Verify(); scanner_mock_Destroy(); event_mock_Verify(); diff --git a/mesh/test/src/ut_timer.c b/mesh/test/src/ut_timer.c index 3a3ba3cca..fc9f856cc 100644 --- a/mesh/test/src/ut_timer.c +++ b/mesh/test/src/ut_timer.c @@ -48,12 +48,12 @@ #include "nrf_error.h" #define TIMER_US_TO_TICKS(US) \ - ((uint32_t)ROUNDED_DIV( \ + (ROUNDED_DIV( \ (US) * (uint64_t)APP_TIMER_CLOCK_FREQ, \ 1000000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1))) #define TIMER_TICKS_TO_US(TICKS) \ - ((uint32_t)ROUNDED_DIV( \ + (ROUNDED_DIV( \ (TICKS) * 1000000ull * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1), \ APP_TIMER_CLOCK_FREQ)) @@ -63,6 +63,10 @@ #define OVFW_TIMESTAMP 512000000ul #define OVFW_TICKS 0x1000000ul +/* Long timeout test defines. */ +#define LONG_TIMEOUT_TICKS ((uint64_t)UINT32_MAX * 2 + 2) +#define LONG_START_TIME_US 1ul + extern void nrf_mesh_timer_tail_handle(void); extern void nrf_mesh_timer_ovfw_handle(void); @@ -75,7 +79,7 @@ static bool m_is_timer_init; // function does not handle more then 1 overflow static void timer_now_smart_mock(timestamp_t timestamp) { - uint32_t tikcs = TIMER_US_TO_TICKS(timestamp); + uint64_t tikcs = TIMER_US_TO_TICKS(timestamp); NRF_RTC1->EVENTS_OVRFLW = tikcs >> RTC_RESOLUTION ? 1 : 0; NRF_RTC1->COUNTER = tikcs & APP_TIMER_MAX_CNT_VAL; @@ -99,6 +103,16 @@ static void timer_ovfw_cb(timestamp_t timestamp) TEST_ASSERT_EQUAL(TIMER_TICKS_TO_US(OVFW_TICKS), timestamp); } +static void timer_long_cb(timestamp_t timestamp) +{ + TEST_ASSERT_FALSE(m_is_timer_fired); + m_is_timer_fired = true; + TEST_ASSERT_EQUAL(TIMER_TICKS_TO_US(LONG_TIMEOUT_TICKS + LONG_START_TIME_US), timestamp); + TEST_ASSERT_EQUAL(TIMER_TICKS_TO_US(LONG_TIMEOUT_TICKS + LONG_START_TIME_US), timer_now()); + TEST_ASSERT_TRUE(timestamp > UINT32_MAX); + TEST_ASSERT_TRUE(timestamp < UINT64_MAX / 2); +} + bool is_app_timer_init(void) { bool retuned_value = m_is_timer_init; @@ -140,6 +154,7 @@ void test_timer_now(void) { timestamp_t expected1; timestamp_t expected2; + timestamp_t expected3; // normal case NRF_RTC1->EVENTS_OVRFLW = 0; @@ -157,6 +172,21 @@ void test_timer_now(void) TEST_ASSERT_EQUAL((timestamp_t)TIMER_TICKS_TO_US((1ul << 24) | 0), expected2); TEST_ASSERT_TRUE(expected1 < expected2); + + // rtc ovfw counter exceeds UINT32_MAX, timer_now() exceeds UINT32_MAX + timer_now_smart_mock(0); + for (uint32_t i = 0; i < 256; i++) + { + nrf_mesh_timer_ovfw_handle(); + } + + NRF_RTC1->COUNTER = 1ul; + + expected3 = timer_now(); + + TEST_ASSERT_EQUAL((timestamp_t)TIMER_TICKS_TO_US((256ull << 24) | 1), expected3); + TEST_ASSERT_TRUE(UINT32_MAX < expected3); + TEST_ASSERT_TRUE(expected2 < expected3); } void test_timer_start(void) @@ -273,3 +303,32 @@ void test_timer_start(void) } TEST_ASSERT_FALSE(m_is_timer_fired); } + +/* Check TIMER_TICKS_TO_US/_US_TO_TICKS macros by setting difference between timestamp and + * timer_now() more than UINT32_MAX in ticks. */ +void test_long_timeout(void) +{ + uint64_t ticks = LONG_TIMEOUT_TICKS; + timestamp_t timestamp = TIMER_TICKS_TO_US(ticks); + + TEST_ASSERT_TRUE(timestamp > UINT32_MAX); + TEST_ASSERT_TRUE(timestamp < (UINT64_MAX >> 1)); + + m_is_timer_fired = false; + timer_stop(); + memset(NRF_RTC1, 0, sizeof(NRF_RTC_Type)); + + timer_now_smart_mock(LONG_START_TIME_US); + timer_start(timestamp, timer_long_cb); + + for (uint32_t i = 0; i < (ticks >> RTC_RESOLUTION); i++) + { + nrf_mesh_timer_ovfw_handle(); + } + + TEST_ASSERT_FALSE(m_is_timer_fired); + NRF_RTC1->COUNTER = (LONG_TIMEOUT_TICKS + LONG_START_TIME_US) & APP_TIMER_MAX_CNT_VAL; + nrf_mesh_timer_tail_handle(); + + TEST_ASSERT_TRUE(m_is_timer_fired); +} diff --git a/mesh/test/src/ut_timer_scheduler.c b/mesh/test/src/ut_timer_scheduler.c index 07ee36612..2f9579706 100644 --- a/mesh/test/src/ut_timer_scheduler.c +++ b/mesh/test/src/ut_timer_scheduler.c @@ -118,6 +118,14 @@ static void timer_callback_call_schedule(timestamp_t timestamp, void * p_context timer_sch_schedule((timer_event_t *) p_context); } +static void timer_callback_call_reschedule_far_in_future(timestamp_t timestamp, void * p_context) +{ + TEST_ASSERT_NOT_NULL(p_context); + timestamp_t next_timestamp = timestamp + UINT32_MAX / 2; + ((timer_event_t *) p_context)->cb = timer_sch_cb; + timer_sch_reschedule((timer_event_t *) p_context, next_timestamp); +} + static bool event_is_in_loop(timer_event_t * p_evt) { for (uint32_t i = 0; i < 1000; i++) @@ -899,3 +907,71 @@ void test_timer_sch_stop(void) TEST_ASSERT_EQUAL(0, m_cb_count); TEST_ASSERT_EQUAL(2, m_timer_stop_count); } + +void test_event_in_future(void) +{ + /* Test special case when events that should be scheduled far in future (see nrf_mesh_dfu.c:290, + * tx_timeout callback) aren't scheduled in past. */ + + m_async_exec = true; + timer_event_t evts[3]; + for (uint32_t i = 0; i < 3; i++) + { + evts[i].cb = timer_sch_cb; + evts[i].timestamp = (i + 1) * 1000; + evts[i].interval = 0; + evts[i].state = TIMER_EVENT_STATE_UNUSED; + evts[i].p_context = &evts[i]; + TEST_ASSERT_FALSE(timer_sch_is_scheduled(&evts[i])); + } + + /* Make the timer schedule an other event from its callback */ + evts[1].cb = timer_callback_call_reschedule_far_in_future; + for (uint32_t i = 0; i < 3; i++) + { + timer_sch_schedule(&evts[i]); + TEST_ASSERT_EQUAL(TIMER_EVENT_STATE_ADDED, evts[i].state); + TEST_ASSERT_TRUE(timer_sch_is_scheduled(&evts[i])); + } + exec_async(); + for (uint32_t i = 0; i < 3; i++) + { + TEST_ASSERT_EQUAL(TIMER_EVENT_STATE_ADDED, evts[i].state); + TEST_ASSERT_TRUE(timer_sch_is_scheduled(&evts[i])); + } + + m_cb_count = 0; + + /* the first timer will fire normally */ + m_time_now = 1000; + m_timer_cb(m_time_now); + exec_async(); + TEST_ASSERT_EQUAL(1, m_cb_count); + + /* The second event will reschedule itself in future. */ + m_time_now = 2000; + m_timer_cb(m_time_now); + exec_async(); + TEST_ASSERT_EQUAL(1, m_cb_count); + TEST_ASSERT_EQUAL(TIMER_EVENT_STATE_ADDED, evts[1].state); /* still queued. */ + + /* Check that event that should be scheduled far in future isn't scheduled in past. */ + m_time_now = 2500; + m_timer_cb(m_time_now); + exec_async(); + TEST_ASSERT_EQUAL(1, m_cb_count); /* still the same. */ + TEST_ASSERT_EQUAL(TIMER_EVENT_STATE_ADDED, evts[1].state); /* still queued. */ + + /* 3rd event should be scheduled. */ + m_time_now = 3000; + m_timer_cb(m_time_now); + exec_async(); + TEST_ASSERT_EQUAL(2, m_cb_count); + TEST_ASSERT_EQUAL(TIMER_EVENT_STATE_ADDED, evts[1].state); /* still queued. */ + + m_time_now = 2000 + UINT32_MAX / 2; + m_timer_cb(m_time_now); + exec_async(); + TEST_ASSERT_EQUAL(3, m_cb_count); + TEST_ASSERT_EQUAL(TIMER_EVENT_STATE_UNUSED, evts[1].state); /* still queued. */ +}