diff options
author | Christian Gromm <christian.gromm@microchip.com> | 2016-12-08 13:51:04 +0100 |
---|---|---|
committer | Christian Gromm <christian.gromm@microchip.com> | 2016-12-08 13:51:04 +0100 |
commit | 8c5f2324d7aa61669324aec1a0ad091fe1379489 (patch) | |
tree | 29cbdcdbe3657e1400d596ec3d560a2573d80817 /mnsl/mns_timer.c | |
parent | e89ba11bd9111f02a940f227cf979f1947e975ac (diff) |
src: unicens: import sources
This patch adds the source tree of the NetworkManager v3.0.4.
Additionally, it provides the needed configuration scripts.
Change-Id: I23778b51423b51a4f87741957e0fb208bceb79b3
Signed-off-by: Christian Gromm <christian.gromm@microchip.com>
Diffstat (limited to 'mnsl/mns_timer.c')
-rw-r--r-- | mnsl/mns_timer.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/mnsl/mns_timer.c b/mnsl/mns_timer.c new file mode 100644 index 0000000..c28d32b --- /dev/null +++ b/mnsl/mns_timer.c @@ -0,0 +1,454 @@ +/* + * MOST NetServices "Light" V3.2.7.0.1796 MultiInstance Patch + * + * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * You may also obtain this software under a propriety license from Microchip. + * Please contact Microchip for further information. + * + */ + +/*! + * \file + * \brief Implementation of the timer management module. + * + * \cond MNS_INTERNAL_DOC + * \addtogroup G_TIMER + * @{ + */ + +/*------------------------------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------------------------------*/ +#include "mns_timer.h" +#include "mns_misc.h" +#include "mns_trace.h" + +/*------------------------------------------------------------------------------------------------*/ +/* Service parameters */ +/*------------------------------------------------------------------------------------------------*/ +/*! Priority of the TM service used by scheduler */ +static const uint8_t TM_SRV_PRIO = 255U; /* parasoft-suppress MISRA2004-8_7 "Value shall be part of the module, not part of a function." */ +/*! Main event for the TM service */ +static const Srv_Event_t TM_EVENT_UPDATE_TIMERS = 1U; + +/*------------------------------------------------------------------------------------------------*/ +/* Internal prototypes */ +/*------------------------------------------------------------------------------------------------*/ +static void Tm_Service(void *self); +static void Tm_UpdateTimers(CTimerManagement *self); +static bool Tm_HandleElapsedTimer(CTimerManagement *self); +static bool Tm_UpdateTimersAdd(void *c_timer_ptr, void *n_timer_ptr); +static void Tm_SetTimerInternal(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period); + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CTimerManagement */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the timer management class. + * \param self Instance pointer + * \param scd Scheduler instance + * \param init_ptr Reference to the initialization data + */ +void Tm_Ctor(CTimerManagement *self, CScheduler *scd, const Tm_InitData_t *init_ptr) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); + self->mns_inst_id = init_ptr->mns_inst_id; + /* Initialize subjects and add observers */ + Ssub_Ctor(&self->get_tick_count_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->get_tick_count_subject, + init_ptr->get_tick_count_obs_ptr); + if(init_ptr->set_application_timer_obs_ptr != NULL) + { + self->delayed_tm_service_enabled = true; + Ssub_Ctor(&self->set_application_timer_subject, self->mns_inst_id); + (void)Ssub_AddObserver(&self->set_application_timer_subject, + init_ptr->set_application_timer_obs_ptr); + } + /* Initialize timer management service */ + Srv_Ctor(&self->tm_srv, TM_SRV_PRIO, self, &Tm_Service); + /* Add timer management service to scheduler */ + (void)Scd_AddService(scd, &self->tm_srv); +} + +/*! \brief Service function of the timer management. + * \param self Instance pointer + */ +static void Tm_Service(void *self) +{ + CTimerManagement *self_ = (CTimerManagement *)self; + Srv_Event_t event_mask; + + Srv_GetEvent(&self_->tm_srv, &event_mask); + + if(TM_EVENT_UPDATE_TIMERS == (event_mask & TM_EVENT_UPDATE_TIMERS)) /* Is event pending? */ + { + Srv_ClearEvent(&self_->tm_srv, TM_EVENT_UPDATE_TIMERS); + Tm_UpdateTimers(self_); + } +} + +/*! \brief If event TM_EVENT_UPDATE_TIMERS is set this function is called. Handles the update + * of the timer list. If a timer has expired the corresponding callback function is + * executed. If the expired timer is a periodic timer, the timer will be set again. + * \param self Instance pointer + */ +static void Tm_UpdateTimers(CTimerManagement *self) +{ + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + bool continue_loop = true; + /* Calculate time difference between the current and the last TM service run */ + uint16_t tick_count_diff = (uint16_t)(current_tick_count - self->last_tick_count); + /* Save current tick count for next service run */ + self->last_tick_count = current_tick_count; + + /* Loop while timer list is not empty */ + while((self->timer_list.head != NULL) && (continue_loop!= false)) + { + /* Is not first timer in list elapsed yet? */ + if(tick_count_diff <= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Update delta of first timer in list */ + ((CTimer *)self->timer_list.head->data_ptr)->delta -= tick_count_diff; + tick_count_diff = 0U; + } + else /* At least first timer in list elapsed */ + { + /* Update tick count difference for next timer in list */ + tick_count_diff -= ((CTimer *)self->timer_list.head->data_ptr)->delta; + /* First timer elapsed */ + ((CTimer *)self->timer_list.head->data_ptr)->delta = 0U; + } + + /* First timer in list elapsed? */ + if(0U == ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + /* Handle elapsed timer */ + continue_loop = Tm_HandleElapsedTimer(self); + } + else /* No elapsed timer in list. */ + { + /* First timer in list updated! Set trigger to inform application (see + Tm_CheckForNextService()) and stop TM service. */ + self->set_service_timer = true; + continue_loop = false; + } + } + } +} + +/*! \brief This function is called if the first timer in list is elapsed. The timer handler + * callback function is invoked. If the timer is a periodic timer it is wound up again. + * \param self Instance pointer + * \return \c true if the next timer must be check. + * \return \c false if the wound up timer (periodic timer) is new head of timer list + */ +static bool Tm_HandleElapsedTimer(CTimerManagement *self) +{ + bool ret_val = true; + + CDlNode *node = self->timer_list.head; + /* Reset flag to be able to check if timer object has changed within handler + callback function */ + ((CTimer *)node->data_ptr)->changed = false; + /* Call timer handler callback function */ + ((CTimer *)node->data_ptr)->handler_fptr(((CTimer *)node->data_ptr)->args_ptr); + + /* Timer object hasn't changed within handler callback function? */ + if(false == ((CTimer *)node->data_ptr)->changed) + { + /* Remove current timer from list */ + (void)Dl_Remove(&self->timer_list, node); + /* Mark timer as unused */ + ((CTimer *)node->data_ptr)->in_use = false; + /* Is current timer a periodic timer? */ + if(((CTimer *)node->data_ptr)->period > 0U) + { + /* Reload current timer */ + Tm_SetTimerInternal(self, + ((CTimer *)node->data_ptr), + ((CTimer *)node->data_ptr)->handler_fptr, + ((CTimer *)node->data_ptr)->args_ptr, + ((CTimer *)node->data_ptr)->period, + ((CTimer *)node->data_ptr)->period); + + if(node == self->timer_list.head) /* Is current timer new head of list? */ + { + /* Set trigger to inform application (see Tm_CheckForNextService()) and + stop TM service. */ + self->set_service_timer = true; + ret_val = false; + } + } + } + + return ret_val; +} + +/*! \brief Calls an application callback function to inform the application that the MNS must be + * serviced not later than the passed time period. If the timer list is empty a possible + * running application timer will be stopped. This function is called at the end of + * Mns_Service(). + * \param self Instance pointer + */ +void Tm_CheckForNextService(CTimerManagement *self) +{ + if(self->delayed_tm_service_enabled != false) + { + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + /* Has head of timer list changed? */ + if(self->set_service_timer != false) + { + uint16_t new_time; + uint16_t diff = current_tick_count - self->last_tick_count; + self->set_service_timer = false; + /* Timer expired since last TM service? */ + if(diff >= ((CTimer *)self->timer_list.head->data_ptr)->delta) + { + new_time = 1U; /* Return minimum value */ + } + else + { + /* Calculate new timeout */ + new_time = (uint16_t)(((CTimer *)self->timer_list.head->data_ptr)->delta - diff); + } + /* Inform the application that the MNS must be serviced not later than the passed + time period. */ + Ssub_Notify(&self->set_application_timer_subject, &new_time, false); + } + } + else + { + Tm_TriggerService(self); /* Application timer not implemented -> Retrigger TM */ + } +} + +/*! \brief Helper function to set the TM service event. + * \details This function is used by the application to trigger a service call of the Timer + * Management if the application timer has expired. + * \param self Instance pointer + */ +void Tm_TriggerService(CTimerManagement *self) +{ + if(self->timer_list.head != NULL) /* At least one timer is running? */ + { + Srv_SetEvent(&self->tm_srv, TM_EVENT_UPDATE_TIMERS); + } +} + +/*! \brief Helper function to stop the TM service. + * \param self Instance pointer + */ +void Tm_StopService(CTimerManagement *self) +{ + uint16_t new_time = 0U; + + /* Clear probable running application timer */ + Ssub_Notify(&self->set_application_timer_subject, &new_time, false); + + /* Reset the service timer. Not necessary ? */ + self->set_service_timer = false; + + /* Clear the timer head queue to prevent any event to be set */ + self->timer_list.head = NULL; +} + +/*! \brief Creates a new timer. The timer expires at the specified elapse time and then after + * every specified period. When the timer expires the specified callback function is + * called. + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \param handler_fptr Callback function which is called when the timer expires + * \param args_ptr Reference to an optional parameter which is passed to the specified + * callback function + * \param elapse The elapse value before the timer expires for the first time, in + * milliseconds + * \param period The period of the timer, in milliseconds. If this parameter is zero, the + * timer is signaled once. If the parameter is greater than zero, the timer + * is periodic. + */ +void Tm_SetTimer(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period) +{ + (void)Tm_ClearTimer(self, timer_ptr); /* Clear timer if running */ + /* Call the internal method to set the new timer (-> does not trigger TM service!) */ + Tm_SetTimerInternal(self, timer_ptr, handler_fptr, args_ptr, elapse, period); + Tm_TriggerService(self); /* New timer added -> trigger timer list update */ +} + +/*! \brief This function contains the internal part when adding a new timer. The function is + * called within Tm_SetTimer() and within Tm_UpdateTimers(). + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \param handler_fptr Callback function which is called when the timer expires + * \param args_ptr Reference to an optional parameter which is passed to the specified + * callback function + * \param elapse The elapse value before the timer expires for the first time, in + * milliseconds + * \param period The period of the timer, in milliseconds. If this parameter is zero, the + * timer is signaled once. If the parameter is greater than zero, the timer + * is periodic. + */ +static void Tm_SetTimerInternal(CTimerManagement *self, + CTimer *timer_ptr, + Tm_Handler_t handler_fptr, + void *args_ptr, + uint16_t elapse, + uint16_t period) +{ + uint16_t current_tick_count; + Ssub_Notify(&self->get_tick_count_subject, ¤t_tick_count, false); + + /* Save timer specific values */ + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + timer_ptr->in_use = true; + timer_ptr->handler_fptr = handler_fptr; + timer_ptr->args_ptr = args_ptr; + timer_ptr->elapse = elapse; + timer_ptr->period = period; + timer_ptr->delta = elapse; + + /* Create back link to be able to point from node to timer object */ + timer_ptr->node.data_ptr = (void *)timer_ptr; + + if(NULL == self->timer_list.head) /* Is timer list empty? */ + { + Dl_InsertHead(&self->timer_list, &timer_ptr->node); /* Add first timer to list */ + /* Save current tick count */ + Ssub_Notify(&self->get_tick_count_subject, &self->last_tick_count, false); + } + else /* Timer list is not empty */ + { + CDlNode *result_ptr = NULL; + + /* Set delta value in relation to last saved tick count (last TM service) */ + timer_ptr->delta += (uint16_t)(current_tick_count - self->last_tick_count); + + /* Search slot where new timer must be inserted. Update delta of new timer + and delta of the following timer in the list. */ + result_ptr = Dl_Foreach(&self->timer_list, &Tm_UpdateTimersAdd, (void *)timer_ptr); + + if(NULL != result_ptr) /* Slot found? */ + { + /* Insert new timer at found position */ + Dl_InsertBefore(&self->timer_list, result_ptr, &timer_ptr->node); + } + else /* No slot found -> Insert as last node */ + { + /* Add new timer to end of list */ + Dl_InsertTail(&self->timer_list, &timer_ptr->node); + } + } +} + +/*! \brief Removes the specified timer from the timer list. + * \param self Instance pointer + * \param timer_ptr Reference to the timer object + * \attention Make sure that for a timer object Tm_SetTimer() is called before Tm_ClearTimer() + * is called! + */ +void Tm_ClearTimer(CTimerManagement *self, CTimer *timer_ptr) +{ + if(timer_ptr->in_use != false) /* Is timer currently in use? */ + { + timer_ptr->changed = true; /* Flag is needed by Tm_UpdateTimers() */ + + if(NULL != timer_ptr->node.next) /* Has deleted timer a follower? */ + { + /* Adjust delta of following timer */ + ((CTimer *)timer_ptr->node.next->data_ptr)->delta += timer_ptr->delta; + } + + (void)Dl_Remove(&self->timer_list, &timer_ptr->node); + timer_ptr->in_use = false; + + Tm_TriggerService(self); /* Timer removed -> trigger timer list update */ + } +} + +/*! \brief Used by Tm_SetTimer() to find the slot where the new timer must be inserted. + * \param c_timer_ptr Reference to current timer processed by foreach loop + * \param n_timer_ptr Reference to new timer + * \return \c true: Slot found, stop foreach loop + * \return \c false: Slot not found, continue foreach loop + */ +static bool Tm_UpdateTimersAdd(void *c_timer_ptr, void *n_timer_ptr) +{ + CTimer *current_timer_ptr = (CTimer *)c_timer_ptr; + CTimer *new_timer_ptr = (CTimer *)n_timer_ptr; + bool ret_val; + + /* Is current timer lesser than new timer? */ + if(current_timer_ptr->delta <= new_timer_ptr->delta) + { + /* Update delta of new timer and continue foreach loop */ + new_timer_ptr->delta -= current_timer_ptr->delta; + ret_val = false; + } + else /* Slot found! */ + { + /* Correct delta of current timer and stop foreach loop */ + current_timer_ptr->delta -= new_timer_ptr->delta; + ret_val = true; + } + + return ret_val; +} + + +/*------------------------------------------------------------------------------------------------*/ +/* Implementation of class CTimer */ +/*------------------------------------------------------------------------------------------------*/ +/*! \brief Constructor of the Timer class. + * \param self Instance pointer + */ +void T_Ctor(CTimer *self) +{ + MISC_MEM_SET(self, 0, sizeof(*self)); +} + +/*! \brief Returns the status of the given timer. + * \param self Instance pointer + * \return \c true if the timer is currently in use + * \return \c false if the timer is not currently in use + */ +bool T_IsTimerInUse(CTimer *self) +{ + return self->in_use; +} + +/*! + * @} + * \endcond + */ + +/*------------------------------------------------------------------------------------------------*/ +/* End of file */ +/*------------------------------------------------------------------------------------------------*/ + |