/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    hw_timerserver.c
  * @author  MCD Application Team
  * @brief   Hardware timerserver source file for STM32WPAN Middleware.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "app_common.h"
#include "hw_conf.h"

/* Private typedef -----------------------------------------------------------*/
typedef enum
{
  TimerID_Free,
  TimerID_Created,
  TimerID_Running
}TimerIDStatus_t;

typedef enum
{
  SSR_Read_Requested,
  SSR_Read_Not_Requested
}RequestReadSSR_t;

typedef enum
{
  WakeupTimerValue_Overpassed,
  WakeupTimerValue_LargeEnough
}WakeupTimerLimitation_Status_t;

typedef struct
{
  HW_TS_pTimerCb_t  pTimerCallBack;
  uint32_t        CounterInit;
  uint32_t        CountLeft;
  TimerIDStatus_t     TimerIDStatus;
  HW_TS_Mode_t   TimerMode;
  uint32_t        TimerProcessID;
  uint8_t         PreviousID;
  uint8_t         NextID;
}TimerContext_t;

/* Private defines -----------------------------------------------------------*/
#define SSR_FORBIDDEN_VALUE   0xFFFFFFFF
#define TIMER_LIST_EMPTY      0xFFFF

/* Private macros ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

/**
 * START of Section TIMERSERVER_CONTEXT
 */

static volatile TimerContext_t aTimerContext[CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER];
static volatile uint8_t CurrentRunningTimerID;
static volatile uint8_t PreviousRunningTimerID;
static volatile uint32_t SSRValueOnLastSetup;
static volatile WakeupTimerLimitation_Status_t  WakeupTimerLimitation;

/**
 * END of Section TIMERSERVER_CONTEXT
 */

static uint8_t  WakeupTimerDivider;
static uint8_t  AsynchPrescalerUserConfig;
static uint16_t SynchPrescalerUserConfig;
static volatile uint16_t MaxWakeupTimerSetup;

/* Global variables ----------------------------------------------------------*/
extern RTC_HandleTypeDef hrtc;

/* Private function prototypes -----------------------------------------------*/
static void RestartWakeupCounter(uint16_t Value);
static uint16_t ReturnTimeElapsed(void);
static void RescheduleTimerList(void);
static void UnlinkTimer(uint8_t TimerID, RequestReadSSR_t RequestReadSSR);
static void LinkTimerBefore(uint8_t TimerID, uint8_t RefTimerID);
static void LinkTimerAfter(uint8_t TimerID, uint8_t RefTimerID);
static uint16_t linkTimer(uint8_t TimerID);
static uint32_t ReadRtcSsrValue(void);

__weak void HW_TS_RTC_CountUpdated_AppNot(void);

/* Functions Definition ------------------------------------------------------*/

/**
 * @brief  Read the RTC_SSR value
 *         As described in the reference manual, the RTC_SSR shall be read twice to ensure
 *         reliability of the value
 * @param  None
 * @retval SSR value read
 */
static uint32_t ReadRtcSsrValue(void)
{
  uint32_t first_read;
  uint32_t second_read;

  first_read = (uint32_t)(READ_BIT(RTC->SSR, RTC_SSR_SS));

  second_read = (uint32_t)(READ_BIT(RTC->SSR, RTC_SSR_SS));

  while(first_read != second_read)
  {
    first_read = second_read;

    second_read = (uint32_t)(READ_BIT(RTC->SSR, RTC_SSR_SS));
  }

  return second_read;
}

/**
 * @brief  Insert a Timer in the list after the Timer ID specified
 * @param  TimerID:   The ID of the Timer
 * @param  RefTimerID: The ID of the Timer to be linked after
 * @retval None
 */
static void LinkTimerAfter(uint8_t TimerID, uint8_t RefTimerID)
{
  uint8_t next_id;

  next_id = aTimerContext[RefTimerID].NextID;

  if(next_id != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER)
  {
    aTimerContext[next_id].PreviousID = TimerID;
  }
  aTimerContext[TimerID].NextID = next_id;
  aTimerContext[TimerID].PreviousID = RefTimerID ;
  aTimerContext[RefTimerID].NextID = TimerID;

  return;
}

/**
 * @brief  Insert a Timer in the list before the ID specified
 * @param  TimerID:   The ID of the Timer
 * @param  RefTimerID: The ID of the Timer to be linked before
 * @retval None
 */
static void LinkTimerBefore(uint8_t TimerID, uint8_t RefTimerID)
{
  uint8_t previous_id;

  if(RefTimerID != CurrentRunningTimerID)
  {
    previous_id = aTimerContext[RefTimerID].PreviousID;

    aTimerContext[previous_id].NextID = TimerID;
    aTimerContext[TimerID].NextID = RefTimerID;
    aTimerContext[TimerID].PreviousID = previous_id ;
    aTimerContext[RefTimerID].PreviousID = TimerID;
  }
  else
  {
    aTimerContext[TimerID].NextID = RefTimerID;
    aTimerContext[RefTimerID].PreviousID = TimerID;
  }

  return;
}

/**
 * @brief  Insert a Timer in the list
 * @param  TimerID:   The ID of the Timer
 * @retval None
 */
static uint16_t linkTimer(uint8_t TimerID)
{
  uint32_t time_left;
  uint16_t time_elapsed;
  uint8_t timer_id_lookup;
  uint8_t next_id;

  if(CurrentRunningTimerID == CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER)
  {
    /**
     * No timer in the list
     */
    PreviousRunningTimerID = CurrentRunningTimerID;
    CurrentRunningTimerID = TimerID;
    aTimerContext[TimerID].NextID = CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER;

    SSRValueOnLastSetup = SSR_FORBIDDEN_VALUE;
    time_elapsed = 0;
  }
  else
  {
    time_elapsed = ReturnTimeElapsed();

    /**
     * update count of the timer to be linked
     */
    aTimerContext[TimerID].CountLeft += time_elapsed;
    time_left = aTimerContext[TimerID].CountLeft;

    /**
     * Search for index where the new timer shall be linked
     */
    if(aTimerContext[CurrentRunningTimerID].CountLeft <= time_left)
    {
      /**
       * Search for the ID after the first one
       */
      timer_id_lookup = CurrentRunningTimerID;
      next_id = aTimerContext[timer_id_lookup].NextID;
      while((next_id != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) && (aTimerContext[next_id].CountLeft <= time_left))
      {
        timer_id_lookup = aTimerContext[timer_id_lookup].NextID;
        next_id = aTimerContext[timer_id_lookup].NextID;
      }

      /**
       * Link after the ID
       */
      LinkTimerAfter(TimerID, timer_id_lookup);
    }
    else
    {
      /**
       * Link before the first ID
       */
      LinkTimerBefore(TimerID, CurrentRunningTimerID);
      PreviousRunningTimerID = CurrentRunningTimerID;
      CurrentRunningTimerID = TimerID;
    }
  }

  return time_elapsed;
}

/**
 * @brief  Remove a Timer from the list
 * @param  TimerID:   The ID of the Timer
 * @param  RequestReadSSR: Request to read the SSR register or not
 * @retval None
 */
static void UnlinkTimer(uint8_t TimerID, RequestReadSSR_t RequestReadSSR)
{
  uint8_t previous_id;
  uint8_t next_id;

  if(TimerID == CurrentRunningTimerID)
  {
    PreviousRunningTimerID = CurrentRunningTimerID;
    CurrentRunningTimerID = aTimerContext[TimerID].NextID;
  }
  else
  {
    previous_id = aTimerContext[TimerID].PreviousID;
    next_id = aTimerContext[TimerID].NextID;

    aTimerContext[previous_id].NextID = aTimerContext[TimerID].NextID;
    if(next_id != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER)
    {
      aTimerContext[next_id].PreviousID = aTimerContext[TimerID].PreviousID;
    }
  }

  /**
   * Timer is out of the list
   */
  aTimerContext[TimerID].TimerIDStatus = TimerID_Created;

  if((CurrentRunningTimerID == CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) && (RequestReadSSR == SSR_Read_Requested))
  {
    SSRValueOnLastSetup = SSR_FORBIDDEN_VALUE;
  }

  return;
}

/**
 * @brief  Return the number of ticks counted by the wakeuptimer since it has been started
 * @note  The API is reading the SSR register to get how many ticks have been counted
 *        since the time the timer has been started
 * @param  None
 * @retval Time expired in Ticks
 */
static uint16_t ReturnTimeElapsed(void)
{
  uint32_t  return_value;
  uint32_t  wrap_counter;

  if(SSRValueOnLastSetup != SSR_FORBIDDEN_VALUE)
  {
    return_value = ReadRtcSsrValue(); /**< Read SSR register first */

    if (SSRValueOnLastSetup >= return_value)
    {
      return_value = SSRValueOnLastSetup - return_value;
    }
    else
    {
      wrap_counter = SynchPrescalerUserConfig - return_value;
      return_value = SSRValueOnLastSetup + wrap_counter;
    }

    /**
     * At this stage, ReturnValue holds the number of ticks counted by SSR
     * Need to translate in number of ticks counted by the Wakeuptimer
     */
    return_value = return_value*AsynchPrescalerUserConfig;
    return_value = return_value >> WakeupTimerDivider;
  }
  else
  {
    return_value = 0;
  }

  return (uint16_t)return_value;
}

/**
 * @brief  Set the wakeup counter
 * @note  The API is writing the counter value so that the value is decreased by one to cope with the fact
 *    the interrupt is generated with 1 extra clock cycle (See RefManuel)
 *    It assumes all condition are met to be allowed to write the wakeup counter
 * @param  Value: Value to be written in the counter
 * @retval None
 */
static void RestartWakeupCounter(uint16_t Value)
{
  /**
   * The wakeuptimer has been disabled in the calling function to reduce the time to poll the WUTWF
   * FLAG when the new value will have to be written
   *  __HAL_RTC_WAKEUPTIMER_DISABLE(&hrtc);
   */

  if(Value == 0)
  {
    SSRValueOnLastSetup = ReadRtcSsrValue();

    /**
     * Simulate that the Timer expired
     */
    HAL_NVIC_SetPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);
  }
  else
  {
    if((Value > 1) ||(WakeupTimerDivider != 1))
    {
      Value -= 1;
    }

    while(__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_FLAG_WUTWF) == RESET);

    /**
     * make sure to clear the flags after checking the WUTWF.
     * It takes 2 RTCCLK between the time the WUTE bit is disabled and the
     * time the timer is disabled. The WUTWF bit somehow guarantee the system is stable
     * Otherwise, when the timer is periodic with 1 Tick, it may generate an extra interrupt in between
     * due to the autoreload feature
     */
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);   /**<  Clear flag in RTC module */
    __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**<  Clear flag in EXTI module */
    HAL_NVIC_ClearPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);   /**<  Clear pending bit in NVIC */

    MODIFY_REG(RTC->WUTR, RTC_WUTR_WUT, Value);

    /**
     * Update the value here after the WUTWF polling that may take some time
     */
    SSRValueOnLastSetup = ReadRtcSsrValue();

    __HAL_RTC_WAKEUPTIMER_ENABLE(&hrtc);    /**<  Enable the Wakeup Timer */

    HW_TS_RTC_CountUpdated_AppNot();
  }

  return ;
}

/**
 * @brief  Reschedule the list of timer
 * @note  1) Update the count left for each timer in the list
 *    2) Setup the wakeuptimer
 * @param  None
 * @retval None
 */
static void RescheduleTimerList(void)
{
  uint8_t   localTimerID;
  uint32_t  timecountleft;
  uint16_t  wakeup_timer_value;
  uint16_t  time_elapsed;

  /**
   * The wakeuptimer is disabled now to reduce the time to poll the WUTWF
   * FLAG when the new value will have to be written
   */
  if((READ_BIT(RTC->CR, RTC_CR_WUTE) == (RTC_CR_WUTE)) == SET)
  {
    /**
     * Wait for the flag to be back to 0 when the wakeup timer is enabled
     */
    while(__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_FLAG_WUTWF) == SET);
  }
  __HAL_RTC_WAKEUPTIMER_DISABLE(&hrtc);   /**<  Disable the Wakeup Timer */

  localTimerID = CurrentRunningTimerID;

  /**
   * Calculate what will be the value to write in the wakeuptimer
   */
  timecountleft = aTimerContext[localTimerID].CountLeft;

  /**
   * Read how much has been counted
   */
  time_elapsed = ReturnTimeElapsed();

  if(timecountleft < time_elapsed )
  {
    /**
     * There is no tick left to count
     */
    wakeup_timer_value = 0;
    WakeupTimerLimitation = WakeupTimerValue_LargeEnough;
  }
  else
  {
    if(timecountleft > (time_elapsed + MaxWakeupTimerSetup))
    {
      /**
       * The number of tick left is greater than the Wakeuptimer maximum value
       */
      wakeup_timer_value = MaxWakeupTimerSetup;

      WakeupTimerLimitation = WakeupTimerValue_Overpassed;
    }
    else
    {
      wakeup_timer_value = timecountleft - time_elapsed;
      WakeupTimerLimitation = WakeupTimerValue_LargeEnough;
    }

  }

  /**
   * update ticks left to be counted for each timer
   */
  while(localTimerID != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER)
  {
    if (aTimerContext[localTimerID].CountLeft < time_elapsed)
    {
      aTimerContext[localTimerID].CountLeft = 0;
    }
    else
    {
      aTimerContext[localTimerID].CountLeft -= time_elapsed;
    }
    localTimerID = aTimerContext[localTimerID].NextID;
  }

  /**
   * Write next count
   */
  RestartWakeupCounter(wakeup_timer_value);

  return ;
}

/* Public functions ----------------------------------------------------------*/

/**
 * For all public interface except that may need write access to the RTC, the RTC
 * shall be unlock at the beginning and locked at the output
 * In order to ease maintainability, the unlock is done at the top and the lock at then end
 * in case some new implementation is coming in the future
 */

void HW_TS_RTC_Wakeup_Handler(void)
{
  HW_TS_pTimerCb_t ptimer_callback;
  uint32_t timer_process_id;
  uint8_t local_current_running_timer_id;
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  uint32_t primask_bit;
#endif

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  primask_bit = __get_PRIMASK();  /**< backup PRIMASK bit */
  __disable_irq();          /**< Disable all interrupts by setting PRIMASK bit on Cortex*/
#endif

/* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );

  /**
   * Disable the Wakeup Timer
   * This may speed up a bit the processing to wait the timer to be disabled
   * The timer is still counting 2 RTCCLK
   */
  __HAL_RTC_WAKEUPTIMER_DISABLE(&hrtc);

  local_current_running_timer_id = CurrentRunningTimerID;

  if(aTimerContext[local_current_running_timer_id].TimerIDStatus == TimerID_Running)
  {
    ptimer_callback = aTimerContext[local_current_running_timer_id].pTimerCallBack;
    timer_process_id = aTimerContext[local_current_running_timer_id].TimerProcessID;

    /**
     * It should be good to check whether the TimeElapsed is greater or not than the tick left to be counted
     * However, due to the inaccuracy of the reading of the time elapsed, it may return there is 1 tick
     * to be left whereas the count is over
     * A more secure implementation has been done with a flag to state whereas the full count has been written
     * in the wakeuptimer or not
     */
    if(WakeupTimerLimitation != WakeupTimerValue_Overpassed)
    {
      if(aTimerContext[local_current_running_timer_id].TimerMode == hw_ts_Repeated)
      {
        UnlinkTimer(local_current_running_timer_id, SSR_Read_Not_Requested);
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
        __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif
        HW_TS_Start(local_current_running_timer_id, aTimerContext[local_current_running_timer_id].CounterInit);

        /* Disable the write protection for RTC registers */
        __HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );
        }
      else
      {
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
        __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif
        HW_TS_Stop(local_current_running_timer_id);

        /* Disable the write protection for RTC registers */
        __HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );
        }

      HW_TS_RTC_Int_AppNot(timer_process_id, local_current_running_timer_id, ptimer_callback);
    }
    else
    {
      RescheduleTimerList();
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
      __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif
    }
  }
  else
  {
    /**
     * We should never end up in this case
     * However, if due to any bug in the timer server this is the case, the mistake may not impact the user.
     * We could just clean the interrupt flag and get out from this unexpected interrupt
     */
    while(__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_FLAG_WUTWF) == RESET);

    /**
     * make sure to clear the flags after checking the WUTWF.
     * It takes 2 RTCCLK between the time the WUTE bit is disabled and the
     * time the timer is disabled. The WUTWF bit somehow guarantee the system is stable
     * Otherwise, when the timer is periodic with 1 Tick, it may generate an extra interrupt in between
     * due to the autoreload feature
     */
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);   /**<  Clear flag in RTC module */
    __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**<  Clear flag in EXTI module */

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
    __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif
  }

  /* Enable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_ENABLE( &hrtc );

  return;
}

void HW_TS_Init(HW_TS_InitMode_t TimerInitMode, RTC_HandleTypeDef *phrtc)
{
  uint8_t loop;
  uint32_t localmaxwakeuptimersetup;

 /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );

  SET_BIT(RTC->CR, RTC_CR_BYPSHAD);

  /**
   * Readout the user config
   */
  WakeupTimerDivider = (4 - ((uint32_t)(READ_BIT(RTC->CR, RTC_CR_WUCKSEL))));

  AsynchPrescalerUserConfig = (uint8_t)(READ_BIT(RTC->PRER, RTC_PRER_PREDIV_A) >> (uint32_t)POSITION_VAL(RTC_PRER_PREDIV_A)) + 1;

  SynchPrescalerUserConfig = (uint16_t)(READ_BIT(RTC->PRER, RTC_PRER_PREDIV_S)) + 1;

  /**
   *  Margin is taken to avoid wrong calculation when the wrap around is there and some
   *  application interrupts may have delayed the reading
   */
  localmaxwakeuptimersetup = ((((SynchPrescalerUserConfig - 1)*AsynchPrescalerUserConfig) - CFG_HW_TS_RTC_HANDLER_MAX_DELAY) >> WakeupTimerDivider);

  if(localmaxwakeuptimersetup >= 0xFFFF)
  {
    MaxWakeupTimerSetup = 0xFFFF;
  }
  else
  {
    MaxWakeupTimerSetup = (uint16_t)localmaxwakeuptimersetup;
  }

  /**
   * Configure EXTI module
   */
  LL_EXTI_EnableRisingTrig_0_31(RTC_EXTI_LINE_WAKEUPTIMER_EVENT);
  LL_EXTI_EnableIT_0_31(RTC_EXTI_LINE_WAKEUPTIMER_EVENT);

  if(TimerInitMode == hw_ts_InitMode_Full)
  {
    WakeupTimerLimitation = WakeupTimerValue_LargeEnough;
    SSRValueOnLastSetup = SSR_FORBIDDEN_VALUE;

    /**
     * Initialize the timer server
     */
    for(loop = 0; loop < CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER; loop++)
    {
      aTimerContext[loop].TimerIDStatus = TimerID_Free;
    }

    CurrentRunningTimerID = CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER;   /**<  Set ID to non valid value */

    __HAL_RTC_WAKEUPTIMER_DISABLE(&hrtc);                       /**<  Disable the Wakeup Timer */
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);     /**<  Clear flag in RTC module */
    __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**<  Clear flag in EXTI module  */
    HAL_NVIC_ClearPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);       /**<  Clear pending bit in NVIC  */
    __HAL_RTC_WAKEUPTIMER_ENABLE_IT(&hrtc, RTC_IT_WUT);         /**<  Enable interrupt in RTC module  */
  }
  else
  {
    if(__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_FLAG_WUTF) != RESET)
    {
      /**
       * Simulate that the Timer expired
       */
      HAL_NVIC_SetPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);
    }
  }

  /* Enable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_ENABLE( &hrtc );

  HAL_NVIC_SetPriority(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID, CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO, CFG_HW_TS_NVIC_RTC_WAKEUP_IT_SUBPRIO);   /**<  Set NVIC priority */
  HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**<  Enable NVIC */

  return;
}

HW_TS_ReturnStatus_t HW_TS_Create(uint32_t TimerProcessID, uint8_t *pTimerId, HW_TS_Mode_t TimerMode, HW_TS_pTimerCb_t pftimeout_handler)
{
  HW_TS_ReturnStatus_t localreturnstatus;
  uint8_t loop = 0;
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  uint32_t primask_bit;
#endif

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  primask_bit = __get_PRIMASK();  /**< backup PRIMASK bit */
  __disable_irq();          /**< Disable all interrupts by setting PRIMASK bit on Cortex*/
#endif

  while((loop < CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) && (aTimerContext[loop].TimerIDStatus != TimerID_Free))
  {
    loop++;
  }

  if(loop != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER)
  {
    aTimerContext[loop].TimerIDStatus = TimerID_Created;

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
    __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif

    aTimerContext[loop].TimerProcessID = TimerProcessID;
    aTimerContext[loop].TimerMode = TimerMode;
    aTimerContext[loop].pTimerCallBack = pftimeout_handler;
    *pTimerId = loop;

    localreturnstatus = hw_ts_Successful;
  }
  else
  {
#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
    __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif

    localreturnstatus = hw_ts_Failed;
  }

  return(localreturnstatus);
}

void HW_TS_Delete(uint8_t timer_id)
{
  HW_TS_Stop(timer_id);

  aTimerContext[timer_id].TimerIDStatus = TimerID_Free; /**<  release ID */

  return;
}

void HW_TS_Stop(uint8_t timer_id)
{
  uint8_t localcurrentrunningtimerid;

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  uint32_t primask_bit;
#endif

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  primask_bit = __get_PRIMASK();  /**< backup PRIMASK bit */
  __disable_irq();          /**< Disable all interrupts by setting PRIMASK bit on Cortex*/
#endif

  HAL_NVIC_DisableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);    /**<  Disable NVIC */

  /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );

  if(aTimerContext[timer_id].TimerIDStatus == TimerID_Running)
  {
    UnlinkTimer(timer_id, SSR_Read_Requested);
    localcurrentrunningtimerid = CurrentRunningTimerID;

    if(localcurrentrunningtimerid == CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER)
    {
      /**
       * List is empty
       */

      /**
       * Disable the timer
       */
      if((READ_BIT(RTC->CR, RTC_CR_WUTE) == (RTC_CR_WUTE)) == SET)
      {
        /**
         * Wait for the flag to be back to 0 when the wakeup timer is enabled
         */
        while(__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_FLAG_WUTWF) == SET);
      }
      __HAL_RTC_WAKEUPTIMER_DISABLE(&hrtc);   /**<  Disable the Wakeup Timer */

      while(__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_FLAG_WUTWF) == RESET);

      /**
       * make sure to clear the flags after checking the WUTWF.
       * It takes 2 RTCCLK between the time the WUTE bit is disabled and the
       * time the timer is disabled. The WUTWF bit somehow guarantee the system is stable
       * Otherwise, when the timer is periodic with 1 Tick, it may generate an extra interrupt in between
       * due to the autoreload feature
       */
      __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);   /**<  Clear flag in RTC module */
      __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**<  Clear flag in EXTI module */
      HAL_NVIC_ClearPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);   /**<  Clear pending bit in NVIC */
    }
    else if(PreviousRunningTimerID != localcurrentrunningtimerid)
    {
      RescheduleTimerList();
    }
  }

  /* Enable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_ENABLE( &hrtc );

  HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**<  Enable NVIC */

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif

  return;
}

void HW_TS_Start(uint8_t timer_id, uint32_t timeout_ticks)
{
  uint16_t time_elapsed;
  uint8_t localcurrentrunningtimerid;

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  uint32_t primask_bit;
#endif

  if(aTimerContext[timer_id].TimerIDStatus == TimerID_Running)
  {
    HW_TS_Stop( timer_id );
  }

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  primask_bit = __get_PRIMASK();  /**< backup PRIMASK bit */
  __disable_irq();          /**< Disable all interrupts by setting PRIMASK bit on Cortex*/
#endif

  HAL_NVIC_DisableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID);    /**<  Disable NVIC */

  /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_DISABLE( &hrtc );

  aTimerContext[timer_id].TimerIDStatus = TimerID_Running;

  aTimerContext[timer_id].CountLeft = timeout_ticks;
  aTimerContext[timer_id].CounterInit = timeout_ticks;

  time_elapsed =  linkTimer(timer_id);

  localcurrentrunningtimerid = CurrentRunningTimerID;

  if(PreviousRunningTimerID != localcurrentrunningtimerid)
  {
    RescheduleTimerList();
  }
  else
  {
    aTimerContext[timer_id].CountLeft -= time_elapsed;
  }

  /* Enable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_ENABLE( &hrtc );

  HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**<  Enable NVIC */

#if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1)
  __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/
#endif

  return;
}

uint16_t HW_TS_RTC_ReadLeftTicksToCount(void)
{
  uint32_t primask_bit;
  uint16_t return_value, auro_reload_value, elapsed_time_value;

  primask_bit = __get_PRIMASK();  /**< backup PRIMASK bit */
  __disable_irq();                /**< Disable all interrupts by setting PRIMASK bit on Cortex*/

  if((READ_BIT(RTC->CR, RTC_CR_WUTE) == (RTC_CR_WUTE)) == SET)
  {
    auro_reload_value = (uint32_t)(READ_BIT(RTC->WUTR, RTC_WUTR_WUT));

    elapsed_time_value = ReturnTimeElapsed();

    if(auro_reload_value > elapsed_time_value)
    {
      return_value = auro_reload_value - elapsed_time_value;
    }
    else
    {
      return_value = 0;
    }
  }
  else
  {
    return_value = TIMER_LIST_EMPTY;
  }

  __set_PRIMASK(primask_bit);     /**< Restore PRIMASK bit*/

  return (return_value);
}

__weak void HW_TS_RTC_Int_AppNot(uint32_t TimerProcessID, uint8_t TimerID, HW_TS_pTimerCb_t pTimerCallBack)
{
  pTimerCallBack();

  return;
}
