os_timer_hash.c
2026/7/19大约 12 分钟附录源码附录
os_timer_hash.c
路径: kernel\source\os_timer_hash.c
/**
***********************************************************************************************************************
* Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @file os_timer.c
*
* @brief This file implements the timer functions.
*
* @revision
* Date Author Notes
* 2020-11-18 OneOS Team First version.
***********************************************************************************************************************
*/
#include <oneos_config.h>
#include <os_errno.h>
#include <os_timer.h>
#include <os_task.h>
#include <os_assert.h>
#include <os_clock.h>
#include <string.h>
#include <os_spinlock.h>
#include <arch_misc.h>
#include "os_kernel_internal.h"
#ifdef OS_USING_HASH_BUCKET_TIMER
#define OS_TIMER_TO_ID(timer) ((os_timer_id)(timer))
#define OS_TIMER_FROM_ID(tid) ((os_timer_t *)(tid))
#define OS_TIMER_LOW_MASK ((1U << OS_HASH_BUCKET_TIMER_POWER) - 1U)
#define OS_TIMER_MOD(tick) ((tick)&OS_TIMER_LOW_MASK)
#define OS_TIMER_ROUND(tick) ((tick) & (~OS_TIMER_LOW_MASK))
#define OS_TIMER_LIST_ENTRIES (1U << OS_HASH_BUCKET_TIMER_POWER)
#define OS_TIMER_IS_HEAD(node) ((node)->info & 2U)
#define OS_TIMER_GET_HANDLE_FLAG_INDEX(node) ((node)->info & 1U)
/* Invisible to user */
#define OS_TIMER_FLAG_DEACTIVATED 0x0U /* Timer is deactive. */
/* the 0x01 used to Periodic timer */
#define OS_TIMER_FLAG_ACTIVATED 0x2U /* Timer is active. */
#define OS_TIMER_FLAG_STATIC 0x0U
#define OS_TIMER_FLAG_DYNAMIC 0x4U
#define OS_TIMER_FLAG_NOT_INITED 0x0U
#define OS_TIMER_FLAG_INITED 0x08U
#define OS_TIMER_INDEX_INVALID OS_UINT32_MAX
#define TIMER_TAG "TIMER"
#define OS_TIMER_TASK_PRIO 0
#if OS_TIMER_LIST_ENTRIES > 32
#error "OS_TIMER_LIST_ENTRIES can not be greater than 32 !!!"
#endif
struct os_timer_active_list_info
{
os_timer_active_node_t active_node;
};
typedef struct os_timer_active_list_info os_timer_active_list_info_t;
OS_ALIGN(OS_ALIGN_SIZE)
static uint8_t gs_timer_task_stack[OS_TIMER_TASK_STACK_SIZE];
static os_task_t gs_os_timer_task;
static os_bool_t gs_os_timer_need_handle = OS_FALSE;
static os_list_node_t gs_os_timer_list = OS_LIST_INIT(gs_os_timer_list);
static OS_DEFINE_SPINLOCK(gs_os_timer_list_lock);
static os_timer_active_list_info_t *gs_os_timer_list_start;
static os_timer_active_list_info_t *gs_os_timer_list_end;
static os_timer_active_list_info_t *gs_os_timer_list_current;
static os_timer_active_list_info_t gs_os_timer_active_list_info[OS_TIMER_LIST_ENTRIES];
static uint32_t gs_os_timer_active_bit_mask;
static uint32_t gs_os_timer_active_bit_number[OS_TIMER_LIST_ENTRIES];
/* Indicates whether the timer is being handled by the timer task. */
static uint8_t gs_os_timer_handle_flag[OS_TIMER_LIST_ENTRIES][2];
OS_INLINE void _k_timer_get_active_list(os_timer_active_list_info_t **current,
os_timer_active_list_info_t **start,
os_timer_active_list_info_t **end)
{
if (OS_NULL != current)
{
*current = gs_os_timer_list_current;
}
if (OS_NULL != start)
{
*start = gs_os_timer_list_start;
}
if (OS_NULL != end)
{
*end = gs_os_timer_list_end;
}
}
OS_INLINE void _k_timer_del_from_list(os_timer_t *timer)
{
os_list_del(&timer->list);
}
OS_INLINE void _k_timer_add_to_list(os_timer_t *timer)
{
os_list_add_tail(&gs_os_timer_list, &timer->list);
}
OS_INLINE void _k_timer_set_handle_flag_index(os_timer_active_node_t *node, uint8_t index)
{
if (0 == index)
{
node->info &= (~0x1U);
}
else
{
node->info |= 0x1;
}
}
OS_INLINE void _k_timer_update_handle_flag(uint32_t current_index)
{
uint8_t handle_flag_index;
uint8_t next_handle_flag_index;
handle_flag_index = OS_TIMER_GET_HANDLE_FLAG_INDEX(&gs_os_timer_list_start[current_index].active_node);
next_handle_flag_index = (0 == handle_flag_index) ? 1 : 0;
gs_os_timer_handle_flag[current_index][handle_flag_index] = 1;
gs_os_timer_handle_flag[current_index][next_handle_flag_index] = 0;
_k_timer_set_handle_flag_index(&gs_os_timer_list_start[current_index].active_node, next_handle_flag_index);
}
#ifdef OS_HASH_BUCKET_TIMER_SORT
OS_INLINE void _k_timer_set_head(os_timer_active_node_t *node)
{
node->info |= 0x2;
}
OS_INLINE void _k_timer_clear_head(os_timer_active_node_t *node)
{
node->info &= (~0x2);
}
#endif
static void _k_timer_do_init(os_timer_t *timer,
const char *name,
void (*function)(void *parameter),
void *parameter,
os_tick_t timeout,
uint8_t flag)
{
timer->active_node.flag = (flag & (~OS_TIMER_FLAG_ACTIVATED)) | OS_TIMER_FLAG_INITED;
timer->active_node.info = 0;
timer->timeout_func = function;
timer->parameter = parameter;
timer->init_ticks = ((0 == timeout) ? 1U : timeout);
timer->round_ticks = timer->init_ticks;
timer->index = OS_TIMER_INDEX_INVALID;
if (OS_NULL != name)
{
(void)strncpy(&timer->name[0], name, OS_NAME_MAX);
timer->name[OS_NAME_MAX] = '\0';
}
else
{
timer->name[0] = '\0';
}
os_list_init(&timer->active_node.active_list);
#ifdef OS_HASH_BUCKET_TIMER_SORT
_k_timer_clear_head(&timer->active_node);
#endif
}
#ifdef OS_HASH_BUCKET_TIMER_SORT
OS_INLINE void _k_timer_add_to_sort_list(os_timer_active_node_t *active_list_head, os_timer_t *add_timer)
{
os_list_node_t *pos;
os_timer_t *tmp_timer;
/*
* If enable OS_HASH_BUCKET_TIMER_SORT,the actual round ticks of the timer is the
* sum of its round ticks plus all the round ticks of its previous timers
* which are located in the same active list.
*/
os_list_for_each(pos, &active_list_head->active_list)
{
tmp_timer = os_list_entry(pos, os_timer_t, active_node.active_list);
if (add_timer->round_ticks >= tmp_timer->round_ticks)
{
/* Round ticks of added timer should subtract the round ticks of previous timers. */
add_timer->round_ticks -= tmp_timer->round_ticks;
}
else
{
/*
* Round ticks of next timer which is located after the added timer should
* subtract the round ticks of the added timer.
*/
tmp_timer->round_ticks -= add_timer->round_ticks;
break;
}
}
/* Add timer before pos. */
os_list_add_tail(pos, &add_timer->active_node.active_list);
}
OS_INLINE void _k_timer_del_from_sort_list(os_timer_t *del_timer)
{
os_timer_t *next_timer;
os_timer_active_node_t *next_node;
next_node = os_list_entry(del_timer->active_node.active_list.next, os_timer_active_node_t, active_list);
if (!OS_TIMER_IS_HEAD(next_node))
{
/* Next timer exist. The next timer's round ticks adds round ticks of the deleted timer. */
next_timer = os_list_entry(del_timer->active_node.active_list.next, os_timer_t, active_node.active_list);
next_timer->round_ticks += del_timer->round_ticks;
}
os_list_del(&del_timer->active_node.active_list);
}
/**
***********************************************************************************************************************
* @brief This function gets the number of remaining ticks for next active timer.
*
* @param None
*
* @return Number of ticks.
***********************************************************************************************************************
*/
os_tick_t k_timer_get_next_remain_ticks(void)
{
os_timer_active_list_info_t *iter_current;
os_timer_active_list_info_t *current;
os_timer_active_list_info_t *start;
os_timer_active_list_info_t *end;
os_timer_t *timer;
os_tick_t min_timeout_tick = OS_UINT32_MAX;
os_tick_t tmp_timeout_tick;
uint32_t i;
/*
* If timer list bit mask has been set, prove there is timer need to schedule
* and the remain time must be zero.
*/
if (0 != gs_os_timer_active_bit_mask)
{
return 0;
}
_k_timer_get_active_list(¤t, &start, &end);
iter_current = current;
/*
* If enable OS_HASH_BUCKET_TIMER_SORT, each active list is sorted. So the first timer of
* each active list has the least expiration time.In the worst case,it needs to
* traverse the first timer of all active lists.
*/
for (i = 0; i < OS_TIMER_LIST_ENTRIES; i++)
{
if (!os_list_empty(&iter_current->active_node.active_list))
{
timer = os_list_entry(iter_current->active_node.active_list.next, os_timer_t, active_node.active_list);
if (0 == timer->round_ticks)
{
/*
* If the round ticks of the first timer in the active list is 0,
* there is no need to continue traversing.Because it's the next timer
* that's about to expire.
*/
min_timeout_tick = i;
break;
}
else
{
/* Calculate the expire time and update the minimal timeout ticks. */
tmp_timeout_tick = timer->round_ticks + i;
if (min_timeout_tick > tmp_timeout_tick)
{
min_timeout_tick = tmp_timeout_tick;
}
}
}
/* Increase iter_current. If it goes beyond the end,wrap around. */
if (++iter_current > end)
{
iter_current = start;
}
}
return min_timeout_tick;
}
/**
***********************************************************************************************************************
* @brief This function update timer active list after low power wakes up.
*
* @param[in] ticks Number of ticks that need to be updated.
*
* @return None.
***********************************************************************************************************************
*/
void k_timer_update_active_list(os_tick_t ticks)
{
os_timer_active_list_info_t *iter_current;
os_timer_active_list_info_t *current;
os_timer_active_list_info_t *start;
os_timer_active_list_info_t *end;
os_timer_t *timer;
uint32_t round_ticks;
uint32_t i;
if (ticks > 0)
{
_k_timer_get_active_list(¤t, &start, &end);
iter_current = current;
round_ticks = OS_TIMER_ROUND(ticks - 1);
/*
* If ticks greater than OS_TIMER_LIST_ENTRIES, it needs to traverse OS_TIMER_LIST_ENTRIES
* active lists. Otherwise just traverse ticks active lists.
*/
for (i = 0; (i < OS_TIMER_LIST_ENTRIES) && (i < ticks); i++)
{
if (!os_list_empty(&iter_current->active_node.active_list))
{
timer = os_list_entry(iter_current->active_node.active_list.next, os_timer_t, active_node.active_list);
if (timer->round_ticks >= round_ticks)
{
timer->round_ticks -= round_ticks;
}
if ((i <= OS_TIMER_MOD(ticks - 1)) && (timer->round_ticks >= OS_TIMER_LIST_ENTRIES))
{
timer->round_ticks -= OS_TIMER_LIST_ENTRIES;
}
}
/* Increase iter_current. If it goes beyond the end,wrap around. */
if (++iter_current > end)
{
iter_current = start;
}
}
i = OS_TIMER_MOD((current - start) + ticks);
gs_os_timer_list_current = &start[i];
}
return;
}
#endif
static void _k_timer_activate(os_timer_t *timer)
{
os_timer_active_list_info_t *current;
os_timer_active_list_info_t *start;
uint32_t dec_ticks;
uint32_t index;
uint8_t handle_flag_index;
OS_ASSERT(timer->init_ticks > 0);
if (os_list_empty(&timer->active_node.active_list))
{
/* Timer is inactive,activate it. */
_k_timer_get_active_list(¤t, &start, OS_NULL);
dec_ticks = timer->init_ticks - 1;
/*
* Calculate index of acitve list array to insert.
* Do not use "index = OS_TIMER_MOD((current - start) + dec_ticks);",
* because it maybe overflow.
*/
index = OS_TIMER_MOD(dec_ticks);
index = OS_TIMER_MOD((current - start) + index);
timer->round_ticks = OS_TIMER_ROUND(dec_ticks);
#ifdef OS_HASH_BUCKET_TIMER_SORT
_k_timer_add_to_sort_list(&start[index].active_node, timer);
#else
os_list_add_tail(&start[index].active_node.active_list, &timer->active_node.active_list);
#endif
timer->active_node.flag |= OS_TIMER_FLAG_ACTIVATED;
timer->index = index;
handle_flag_index = OS_TIMER_GET_HANDLE_FLAG_INDEX(&start[index].active_node);
_k_timer_set_handle_flag_index(&timer->active_node, handle_flag_index);
}
}
static void _k_timer_active_bit_clear(const os_timer_t *timer)
{
const os_timer_t *tmp_timer;
if ((OS_BIT_GET(gs_os_timer_active_bit_mask, timer->index) != 0) && (0 == timer->round_ticks))
{
#ifdef OS_HASH_BUCKET_TIMER_SORT
os_timer_active_node_t *active_node;
active_node = os_list_entry(timer->active_node.active_list.prev, os_timer_active_node_t, active_list);
if (OS_TIMER_IS_HEAD(active_node))
{
active_node = os_list_entry(timer->active_node.active_list.next, os_timer_active_node_t, active_list);
if (OS_TIMER_IS_HEAD(active_node))
{
OS_BIT_CLR(gs_os_timer_active_bit_mask, timer->index);
}
else
{
tmp_timer = os_list_entry(active_node, os_timer_t, active_node);
if (0 != tmp_timer->round_ticks)
{
OS_BIT_CLR(gs_os_timer_active_bit_mask, timer->index);
}
}
}
#else
const os_list_node_t *pos;
const os_list_node_t *head;
head = &gs_os_timer_active_list_info[timer->index].active_node.active_list;
os_list_for_each(pos, head)
{
tmp_timer = os_list_entry(pos, os_timer_t, active_node.active_list);
if ((0 == tmp_timer->round_ticks) && (tmp_timer != timer))
{
break;
}
}
if (pos == head)
{
OS_BIT_CLR(gs_os_timer_active_bit_mask, timer->index);
}
#endif
}
}
static void _k_timer_deactivate(os_timer_t *timer)
{
if ((timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED) != 0)
{
_k_timer_active_bit_clear(timer);
timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
timer->index = OS_TIMER_INDEX_INVALID;
if (!os_list_empty(&timer->active_node.active_list))
{
#ifdef OS_HASH_BUCKET_TIMER_SORT
_k_timer_del_from_sort_list(timer);
#else
os_list_del(&timer->active_node.active_list);
#endif
}
}
}
static void _k_timer_remove(os_timer_t *timer)
{
_k_timer_del_from_list(timer);
_k_timer_deactivate(timer);
timer->active_node.flag &= ~OS_TIMER_FLAG_INITED;
}
static os_tick_t _k_timer_calc_remain_ticks(os_timer_t *timer)
{
os_timer_active_list_info_t *current;
os_timer_active_list_info_t *start;
os_timer_active_list_info_t *end;
os_tick_t remain_tick;
os_tick_t diff;
uint32_t current_index;
uint8_t handle_flag_index;
os_tick_t compensate_tick;
#ifdef OS_HASH_BUCKET_TIMER_SORT
os_timer_t *tmp_timer;
os_list_node_t *pos;
os_timer_active_node_t *active_node;
#endif
if ((timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED) != 0)
{
OS_ASSERT((OS_TIMER_INDEX_INVALID != timer->index));
_k_timer_get_active_list(¤t, &start, &end);
/* Calculate the index of list pointed by gs_os_timer_list_current. */
current_index = current - start;
if (timer->index >= current_index)
{
/*
* The index of the active list where the timer is located is greater
* than index of list pointed by gs_os_timer_list_current.
*/
diff = timer->index - current_index + 1;
}
else
{
/*
* The index of the active list where the timer is located is less
* than index of list pointed by gs_os_timer_list_current. The pointer
* needs to wrap around to reach the list where the timer is located.
*/
diff = end - current + 1;
diff += timer->index + 1;
}
remain_tick = 0;
#ifdef OS_HASH_BUCKET_TIMER_SORT
/*
* If OS_HASH_BUCKET_TIMER_SORT enable,the actual round ticks of the timer is the
* sum of its round ticks plus all the round ticks of its previous timers
* which are located in the same active list.
*/
for (pos = &timer->active_node.active_list;; pos = pos->prev)
{
active_node = os_list_entry(pos, os_timer_active_node_t, active_list);
if (OS_TIMER_IS_HEAD(active_node))
{
break;
}
tmp_timer = os_list_entry(pos, os_timer_t, active_node.active_list);
remain_tick += tmp_timer->round_ticks;
}
remain_tick += diff;
#else
remain_tick = timer->round_ticks + diff;
#endif
handle_flag_index = OS_TIMER_GET_HANDLE_FLAG_INDEX(&timer->active_node);
if ((1 == gs_os_timer_handle_flag[timer->index][handle_flag_index]) ||
((OS_BIT_GET(gs_os_timer_active_bit_mask, timer->index)) != 0))
{
/* Tick needed to compensate */
compensate_tick = gs_os_timer_active_bit_number[timer->index] * (OS_TIMER_LIST_ENTRIES);
/* Timer is being handled by timer task. */
if (remain_tick <= compensate_tick)
{
remain_tick = 0;
}
else
{
remain_tick -= compensate_tick;
}
}
}
else
{
remain_tick = 0;
}
return remain_tick;
}
static void _k_timer_task_entry(void *parameter)
{
os_timer_active_node_t expired_timer_head;
os_timer_active_node_t restart_timer_head;
os_list_node_t *current_pos;
os_list_node_t *restart_pos;
os_timer_t *restart_timer;
os_timer_t *current_timer;
uint8_t handle_flag_index;
void (*timeout_func)(void *timeout_param);
void *timeout_param;
os_task_t *current_task;
uint16_t current_list_idx;
os_timer_active_list_info_t *iter_current;
os_tick_t compensate_tick;
OS_KERNEL_INIT();
OS_UNREFERENCE(parameter);
#ifdef OS_HASH_BUCKET_TIMER_SORT
expired_timer_head.info = 0;
restart_timer_head.info = 0;
_k_timer_set_head(&expired_timer_head);
_k_timer_set_head(&restart_timer_head);
#endif
while (OS_TRUE)
{
OS_KERNEL_ENTER();
if (!gs_os_timer_need_handle)
{
/* No timer needs handle,suspend myself. */
current_task = _k_task_self();
k_readyq_remove(current_task);
current_task->state &= ~OS_TASK_STATE_READY;
current_task->state |= OS_TASK_STATE_SUSPEND;
OS_KERNEL_EXIT_SCHED();
}
else
{
/*
* There are timers need handle.
* 1.Move the current active list to expire list.
* 2.Handle timers of expire list one by one.
* 3.If the timer does not expire or timer is periodic,add the timer to restart list.
* 4.Call timeout function of expired timer.
* 5.Add the timer of restart list to active list.
*/
/* Move the current active list pointed by gs_os_timer_list_current to expire list.
* ----- ----- add ----- ----- ----- del ----- ----- -----
* | cur |<-->| node| ----> | cur |<-->| exp |<-->| node| ----> | cur | | exp |<-->| node|
* | head|<-->| | | head|<-->| head|<-->| | | head| | head|<-->| |
* ----- ----- ----- ----- ----- ----- ----- -----
*/
/*
* Clear flag. The timer tick handler will set the flag when
* it find list pointed by gs_os_timer_list_current is not empty.
*/
gs_os_timer_need_handle = OS_FALSE;
while (0 != gs_os_timer_active_bit_mask)
{
/* Get the timer's current list idx,which is needed to handle. */
current_list_idx = os_ffs(gs_os_timer_active_bit_mask) - 1;
iter_current = &gs_os_timer_active_list_info[current_list_idx];
/* Move the current active list pointed to expire list. */
os_list_add(&iter_current->active_node.active_list, &expired_timer_head.active_list);
os_list_del(&iter_current->active_node.active_list);
_k_timer_update_handle_flag((uint32_t)current_list_idx);
/* Clear timer's list bit mask. */
OS_BIT_CLR(gs_os_timer_active_bit_mask, current_list_idx);
/* Handle timers of expire list one by one. */
current_pos = os_list_first(&expired_timer_head.active_list);
while (OS_NULL != current_pos)
{
current_timer = os_list_entry(current_pos, os_timer_t, active_node.active_list);
/* Delete current timer from expire list. */
#ifdef OS_HASH_BUCKET_TIMER_SORT
_k_timer_del_from_sort_list(current_timer);
#else
os_list_del(current_pos);
#endif
os_list_init(&restart_timer_head.active_list);
OS_ASSERT(0 == OS_TIMER_MOD(current_timer->round_ticks));
/*
* Get timeout funciton and parameter. If the timer does not expire or timer is periodic,
* add the timer to restart list in order to activate it again after call timeout funcion.
*/
/* Tick needed to compensate */
compensate_tick = (gs_os_timer_active_bit_number[current_list_idx] - 1) * (OS_TIMER_LIST_ENTRIES);
if (current_timer->round_ticks <= compensate_tick)
{
/* Timer expire. */
current_timer->round_ticks = 0;
if ((current_timer->active_node.flag & OS_TIMER_FLAG_PERIODIC) != 0)
{
/* Add current timer to restart timer list in order to activate it again afterwards. */
os_list_add(&restart_timer_head.active_list, current_pos);
}
/* Get timeout function and parameter. The timeout funciton will be called afterwards. */
timeout_func = current_timer->timeout_func;
timeout_param = current_timer->parameter;
}
else
{
/* Timer do not expire */
current_timer->round_ticks -= compensate_tick;
/* Add current timer to restart timer list. */
os_list_add(&restart_timer_head.active_list, current_pos);
timeout_func = OS_NULL;
}
/* The timer is oneshot, deactivate it. */
if (os_list_empty(&restart_timer_head.active_list))
{
_k_timer_deactivate(current_timer);
}
OS_KERNEL_EXIT();
/* Call timeout function if it is expired. */
if (OS_NULL != timeout_func)
{
timeout_func(timeout_param);
}
OS_KERNEL_ENTER();
/* If the timer does not expire or timer is periodic, add timer to active list. */
restart_pos = os_list_first(&restart_timer_head.active_list);
if (OS_NULL != restart_pos)
{
/* Remove timer from restart list. */
os_list_del(restart_pos);
restart_timer = os_list_entry(restart_pos, os_timer_t, active_node.active_list);
if (0 == restart_timer->round_ticks)
{
/* Timer expire, reactivate timer. */
_k_timer_activate(restart_timer);
}
else
{
/* Timer does not expire, round_ticks subtract active list entries. */
restart_timer->round_ticks -= OS_TIMER_LIST_ENTRIES;
/*
* There is no need to recalculate active list's index where the
* unexpired timer adds. Just add timer to its original active list.
*/
#ifdef OS_HASH_BUCKET_TIMER_SORT
_k_timer_add_to_sort_list(&gs_os_timer_list_start[restart_timer->index].active_node,
restart_timer);
#else
os_list_add_tail(&(gs_os_timer_list_start[restart_timer->index].active_node.active_list),
restart_pos);
#endif
handle_flag_index = OS_TIMER_GET_HANDLE_FLAG_INDEX(
&gs_os_timer_list_start[restart_timer->index].active_node);
_k_timer_set_handle_flag_index(&restart_timer->active_node, handle_flag_index);
}
}
current_pos = os_list_first(&expired_timer_head.active_list);
}
gs_os_timer_active_bit_number[current_list_idx] = 0;
}
OS_KERNEL_EXIT();
}
}
}
void k_move_timer_list_one_step(void)
{
if (++gs_os_timer_list_current > gs_os_timer_list_end)
{
gs_os_timer_list_current = gs_os_timer_list_start;
}
}
os_bool_t k_timer_need_handle(void)
{
os_bool_t need_handle;
int32_t current_list_idx;
/*
* If the list pointed by gs_os_timer_list_current is not expty,set flag gs_os_timer_need_handle
* and resume timer task to handle the timer list pointed by gs_os_timer_list_current.
*/
if (!os_list_empty(&gs_os_timer_list_current->active_node.active_list))
{
#ifdef OS_HASH_BUCKET_TIMER_SORT
os_timer_t *timer;
timer =
os_list_entry(gs_os_timer_list_current->active_node.active_list.next, os_timer_t, active_node.active_list);
if (timer->round_ticks >= OS_TIMER_LIST_ENTRIES)
{
/* There is no timer timeout, do not need wake up timer task. */
timer->round_ticks -= OS_TIMER_LIST_ENTRIES;
}
else
#endif
{
/* List is not empty,set flag and set the list bit mask.*/
gs_os_timer_need_handle = OS_TRUE;
current_list_idx = gs_os_timer_list_current - gs_os_timer_list_start;
OS_BIT_SET(gs_os_timer_active_bit_mask, current_list_idx);
gs_os_timer_active_bit_number[current_list_idx]++;
}
}
need_handle = OS_FALSE;
/* List pointed by gs_os_timer_list_current is not empty,resume timer task to handle the list. */
if (gs_os_timer_need_handle && (OS_TASK_STATE_SUSPEND == gs_os_timer_task.state))
{
gs_os_timer_task.state &= ~OS_TASK_STATE_SUSPEND;
gs_os_timer_task.state |= OS_TASK_STATE_READY;
k_readyq_put(&gs_os_timer_task);
need_handle = OS_TRUE;
}
return need_handle;
}
void k_timer_module_init(void)
{
int32_t i;
os_err_t ret;
os_task_id tid;
for (i = 0; i < OS_TIMER_LIST_ENTRIES; i++)
{
os_list_init(&gs_os_timer_active_list_info[i].active_node.active_list);
_k_timer_set_handle_flag_index(&gs_os_timer_active_list_info[i].active_node, 0);
#ifdef OS_HASH_BUCKET_TIMER_SORT
_k_timer_set_head(&gs_os_timer_active_list_info[i].active_node);
#endif
}
gs_os_timer_list_start = &gs_os_timer_active_list_info[0];
gs_os_timer_list_current = &gs_os_timer_active_list_info[0];
gs_os_timer_list_end = &gs_os_timer_active_list_info[OS_TIMER_LIST_ENTRIES - 1];
(void)memset(gs_os_timer_handle_flag, 0, sizeof(gs_os_timer_handle_flag));
/* Create and start timer task. */
tid = os_task_create((os_task_dummy_t *)&gs_os_timer_task,
&gs_timer_task_stack[0],
OS_TIMER_TASK_STACK_SIZE,
"timer",
_k_timer_task_entry,
OS_NULL,
OS_TIMER_TASK_PRIO);
if (tid == OS_NULL)
{
OS_ASSERT_EX(OS_FALSE, "Why initialize timer task failed?");
}
ret = os_task_startup(tid);
if (OS_SUCCESS != ret)
{
OS_ASSERT_EX(OS_FALSE, "Why startup timer task failed?");
}
return;
}
/**
***********************************************************************************************************************
* @brief This function create a timer and places it on the list of timer object.
*
* @details This function create a timer and places it on the list of timer object.The memory space of the
* timer control block descriptor is applied from the heap.
*
* @param[in] name Pointer to timer name string.
* @param[in] timeout Entry function of timeout.
* @param[in] parameter Parameter of entry function.
* @param[in] timeout Number of ticks that need to be delayed.
* @param[in] flag Flag of timer.
*
* @return On success, return a timer control block descriptor; on error, OS_NULL is returned.
* @retval not OS_NULL Return a timer control block descriptor.
* @retval OS_NULL Call os_timer_create failed.
***********************************************************************************************************************
*/
os_timer_id os_timer_create(os_timer_dummy_t *timer_cb,
const char *name,
void (*function)(void *parameter),
void *parameter,
os_tick_t timeout,
uint8_t flag)
{
os_timer_t *timer;
os_timer_t *iter_timer;
os_bool_t status;
#ifndef OS_USING_HEAP
OS_ASSERT(OS_NULL != timer_cb);
#endif
OS_ASSERT((OS_FALSE == os_is_irq_active()));
if (OS_NULL != timer_cb)
{
timer = OS_TYPE_CONVERT(os_timer_t *, timer_cb);
status = OS_TRUE;
os_spin_lock(&gs_os_timer_list_lock);
os_list_for_each_entry(iter_timer, &gs_os_timer_list, os_timer_t, list)
{
if (iter_timer == timer)
{
timer = OS_NULL;
OS_KERN_LOG(KERN_ERROR, TIMER_TAG, "%s", (OS_KERNEL_ERR_INFO));
status = OS_FALSE;
break;
}
}
if (OS_TRUE == status)
{
os_spin_unlock(&gs_os_timer_list_lock);
_k_timer_do_init(timer, name, function, parameter, timeout, flag & (~OS_TIMER_FLAG_DYNAMIC));
os_spin_lock(&gs_os_timer_list_lock);
_k_timer_add_to_list(timer);
}
os_spin_unlock(&gs_os_timer_list_lock);
}
#ifdef OS_USING_HEAP
else
{
timer = (os_timer_t *)OS_KERNEL_MALLOC(sizeof(os_timer_t));
if (OS_NULL == timer)
{
OS_KERN_LOG(KERN_ERROR, TIMER_TAG, "malloc timer fail");
}
else
{
_k_timer_do_init(timer, name, function, parameter, timeout, flag | OS_TIMER_FLAG_DYNAMIC);
os_spin_lock(&gs_os_timer_list_lock);
_k_timer_add_to_list(timer);
os_spin_unlock(&gs_os_timer_list_lock);
}
}
#endif
return OS_TYPE_CONVERT(os_timer_id, timer);
}
/**
***********************************************************************************************************************
* @brief This function will destroy the specified timer by timer descriptor.
*
* @attention It corresponds to os_timer_create.
*
* @param[in] timer_id The ID of timer control block.
*
* @return Will only return OS_SUCCESS
***********************************************************************************************************************
*/
os_err_t os_timer_destroy(os_timer_id timer_id)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_ASSERT(OS_FALSE == os_is_irq_active());
OS_ASSERT((OS_FALSE == os_is_irq_disabled()));
OS_ASSERT((OS_FALSE == os_is_schedule_locked()));
#ifndef OS_USING_HEAP
OS_ASSERT(OS_TIMER_FLAG_STATIC == (timer->active_node.flag & OS_TIMER_FLAG_DYNAMIC));
#endif
OS_KERNEL_ENTER();
_k_timer_remove(timer);
OS_KERNEL_EXIT();
#ifdef OS_USING_HEAP
if ((timer->active_node.flag & OS_TIMER_FLAG_DYNAMIC) != 0)
{
OS_KERNEL_FREE(timer);
}
#endif
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function will starup the specified timer by timer descriptor.
*
* @param[in] timer_id The ID of timer control block.
*
* @return Will only return OS_SUCCESS
***********************************************************************************************************************
*/
os_err_t os_timer_start(os_timer_id timer_id)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
/* First deactivate timer, then start it. */
_k_timer_deactivate(timer);
_k_timer_activate(timer);
OS_KERNEL_EXIT();
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function will stop the specified timer by timer descriptor.
*
* @param[in] timer_id The ID of timer control block.
*
* @return Stop success or fail.
* @retval OS_SUCCESS success.
* @retval OS_FAILURE fail.
***********************************************************************************************************************
*/
os_err_t os_timer_stop(os_timer_id timer_id)
{
os_err_t ret;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
if (!(timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED))
{
ret = OS_FAILURE;
}
else
{
OS_KERNEL_ENTER();
_k_timer_deactivate(timer);
OS_KERNEL_EXIT();
ret = OS_SUCCESS;
}
return ret;
}
/**
***********************************************************************************************************************
* @brief This function set delay time of timer.
*
* @param timer_id The ID of timer control block.
*
* @return Will only return OS_SUCCESS
***********************************************************************************************************************
*/
os_err_t os_timer_set_timeout_ticks(os_timer_id timer_id, os_tick_t timeout)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
timer->init_ticks = timeout;
OS_KERNEL_EXIT();
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function gets the initial ticks that timer needs to delay.
*
* @param timer_id TThe ID of timer control block.
*
* @return Number of ticks.
***********************************************************************************************************************
*/
os_tick_t os_timer_get_timeout_ticks(os_timer_id timer_id)
{
os_tick_t init_ticks;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
init_ticks = timer->init_ticks;
OS_KERNEL_EXIT();
return init_ticks;
}
/**
***********************************************************************************************************************
* @brief This function gets the remaining ticks before expiration.
*
* @param timer_id The ID of timer control block.
*
* @return Number of ticks.
***********************************************************************************************************************
*/
os_tick_t os_timer_get_remain_ticks(os_timer_id timer_id)
{
os_tick_t remain_ticks;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
remain_ticks = _k_timer_calc_remain_ticks(timer);
OS_KERNEL_EXIT();
return remain_ticks;
}
/**
***********************************************************************************************************************
* @brief Get the name of the specified timer.
*
* @param[in] timer_id The ID of timer control block.
*
* @return The name of the specified timer.
***********************************************************************************************************************
*/
const char *os_timer_get_name(os_timer_id timer_id)
{
os_timer_t *timer;
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
return timer->name;
}
/**
***********************************************************************************************************************
* @brief This function return the timer is active or inactive.
*
* @param timer_id The ID of timer control block.
*
* @return Timer is active or inactive.
* @retval OS_FASLE Timer is inactive.
* @retval OS_TRUE Timer is active.
***********************************************************************************************************************
*/
os_bool_t os_timer_is_active(os_timer_id timer_id)
{
os_bool_t is_active;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
is_active = ((timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED) != 0) ? OS_TRUE : OS_FALSE;
OS_KERNEL_EXIT();
return is_active;
}
/**
***********************************************************************************************************************
* @brief This function set the timer to be oneshot.
*
* @param timer_id The ID of timer control block.
*
* @return Will only return OS_SUCCESS
***********************************************************************************************************************
*/
os_err_t os_timer_set_oneshot(os_timer_id timer_id)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
timer->active_node.flag &= ~OS_TIMER_FLAG_PERIODIC;
OS_KERNEL_EXIT();
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function set the timer to be periodic.
*
* @param timer_id The ID of timer control block.
*
* @return Will only return OS_SUCCESS
***********************************************************************************************************************
*/
os_err_t os_timer_set_periodic(os_timer_id timer_id)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
timer->active_node.flag |= OS_TIMER_FLAG_PERIODIC;
OS_KERNEL_EXIT();
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function return the timer is one shot or periodic.
*
* @param timer_id The ID of timer control block.
*
* @return Timer is one shot or periodic.
* @retval OS_FASLE Timer is one shot.
* @retval OS_TRUE Timer is periodic.
***********************************************************************************************************************
*/
os_bool_t os_timer_is_periodic(os_timer_id timer_id)
{
os_bool_t is_periodic;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
is_periodic = ((timer->active_node.flag & OS_TIMER_FLAG_PERIODIC) != 0U) ? OS_TRUE : OS_FALSE;
OS_KERNEL_EXIT();
return is_periodic;
}
os_bool_t os_timer_check_exist(os_timer_id timer_id)
{
os_timer_t *iter_timer;
os_bool_t valid_timer;
os_timer_t *timer;
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
if (OS_NULL == timer)
{
os_kprintf("Invalid Parameter\r\n");
return OS_FALSE;
}
valid_timer = OS_FALSE;
os_spin_lock(&gs_os_timer_list_lock);
os_list_for_each_entry(iter_timer, &gs_os_timer_list, os_timer_t, list)
{
if (iter_timer == timer)
{
valid_timer = OS_TRUE;
break;
}
}
os_spin_unlock(&gs_os_timer_list_lock);
return valid_timer;
}
#if defined(OS_USING_SHELL)
#include <shell.h>
#define SH_SHOW_TIMER_CNT_MAX 32
typedef struct
{
os_timer_t *timer;
os_tick_t init_ticks;
os_tick_t remain_ticks;
uint8_t flag;
} sh_timer_info_t;
static void sh_show_one_timer_info(sh_timer_info_t *timer_info)
{
os_kprintf("%-*.*s %-12lu %-12lu ",
OS_NAME_MAX,
OS_NAME_MAX,
timer_info->timer->name,
timer_info->init_ticks,
timer_info->remain_ticks);
if ((timer_info->flag & OS_TIMER_FLAG_ACTIVATED) != 0)
{
os_kprintf("%-*s ", strlen("inactive"), "active");
}
else
{
os_kprintf("%-*s ", strlen("inactive"), "inactive");
}
if ((timer_info->flag & OS_TIMER_FLAG_PERIODIC) != 0)
{
os_kprintf("%-*s", strlen("periodic,"), "periodic,");
}
else
{
os_kprintf("%-*s", strlen("periodic,"), "oneshot,");
}
if ((timer_info->flag & OS_TIMER_FLAG_DYNAMIC) != 0)
{
os_kprintf("%-*s\r\n", strlen("dynamic,"), "dynamic");
}
else
{
os_kprintf("%-*s\r\n", strlen("dynamic,"), "static");
}
}
static os_err_t sh_show_timer_info(int32_t argc, char *const *argv)
{
uint32_t i;
os_timer_t *iter_timer;
sh_timer_info_t timer_info[SH_SHOW_TIMER_CNT_MAX];
uint32_t info_index;
os_tick_t current_tick;
uint32_t timer_cnt;
OS_KERNEL_INIT();
OS_UNREFERENCE(argc);
OS_UNREFERENCE(argv);
os_kprintf("%-*s Interval Remain State Type\r\n", OS_NAME_MAX, "Hash-timer");
i = OS_NAME_MAX;
while ((i--) != 0)
{
os_kprintf("-");
}
os_kprintf(" ------------ ------------ -------- ---------------------\r\n");
os_spin_lock(&gs_os_timer_list_lock);
timer_cnt = os_list_len(&gs_os_timer_list);
if (timer_cnt > SH_SHOW_TIMER_CNT_MAX)
{
current_tick = os_tick_get_value();
os_list_for_each_entry(iter_timer, &gs_os_timer_list, os_timer_t, list)
{
OS_KERNEL_ENTER();
timer_info[0].timer = iter_timer;
timer_info[0].remain_ticks = _k_timer_calc_remain_ticks(iter_timer);
timer_info[0].init_ticks = iter_timer->init_ticks;
timer_info[0].flag = iter_timer->active_node.flag;
OS_KERNEL_EXIT();
sh_show_one_timer_info(&timer_info[0]);
}
}
else
{
OS_KERNEL_ENTER();
current_tick = os_tick_get_value();
info_index = 0;
os_list_for_each_entry(iter_timer, &gs_os_timer_list, os_timer_t, list)
{
timer_info[info_index].timer = iter_timer;
timer_info[info_index].remain_ticks = _k_timer_calc_remain_ticks(iter_timer);
timer_info[info_index].init_ticks = iter_timer->init_ticks;
timer_info[info_index].flag = iter_timer->active_node.flag;
info_index++;
}
OS_KERNEL_EXIT();
for (i = 0; i < info_index; i++)
{
sh_show_one_timer_info(&timer_info[i]);
}
}
os_kprintf("current tick:%-lu\r\n", current_tick);
os_spin_unlock(&gs_os_timer_list_lock);
return OS_SUCCESS;
}
SH_CMD_EXPORT(show_timer, sh_show_timer_info, "Show timer information");
#endif
#endif /* End of OS_USING_HASH_BUCKET_TIMER */