/* * Copyright (C) 2015-2017 Alibaba Group Holding Limited */ #include "k_api.h" #if (RHINO_CONFIG_TIMER > 0) static void timer_list_pri_insert(klist_t *head, ktimer_t *timer) { tick_t val; klist_t *q; klist_t *start; klist_t *end; ktimer_t *task_iter_temp; start = head; end = head; val = timer->remain; for (q = start->next; q != end; q = q->next) { task_iter_temp = krhino_list_entry(q, ktimer_t, timer_list); if ((task_iter_temp->match - g_timer_count) > val) { break; } } klist_insert(q, &timer->timer_list); } static void timer_list_rm(ktimer_t *timer) { klist_t *head; head = timer->to_head; if (head != NULL) { klist_rm(&timer->timer_list); timer->to_head = NULL; } } static kstat_t timer_create(ktimer_t *timer, const name_t *name, timer_cb_t cb, tick_t first, tick_t round, void *arg, uint8_t auto_run, uint8_t mm_alloc_flag) { kstat_t err = RHINO_SUCCESS; NULL_PARA_CHK(timer); NULL_PARA_CHK(name); NULL_PARA_CHK(cb); if (first == 0u) { return RHINO_INV_PARAM; } if (first > RHINO_MAX_TICKS) { return RHINO_INV_PARAM; } if (round > RHINO_MAX_TICKS) { return RHINO_INV_PARAM; } timer->name = name; timer->cb = cb; timer->init_count = first; timer->round_ticks = round; timer->remain = 0u; timer->match = 0u; timer->timer_state = TIMER_DEACTIVE; timer->to_head = NULL; timer->mm_alloc_flag = mm_alloc_flag; timer->timer_cb_arg = arg; klist_init(&timer->timer_list); timer->obj_type = RHINO_TIMER_OBJ_TYPE; if (auto_run > 0u) { err = krhino_timer_start(timer); } TRACE_TIMER_CREATE(krhino_cur_task_get(), timer); return err; } kstat_t krhino_timer_create(ktimer_t *timer, const name_t *name, timer_cb_t cb, tick_t first, tick_t round, void *arg, uint8_t auto_run) { return timer_create(timer, name, cb, first, round, arg, auto_run, K_OBJ_STATIC_ALLOC); } kstat_t krhino_timer_del(ktimer_t *timer) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); cb.timer = timer; cb.cb_num = TIMER_CMD_DEL; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } #if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0) kstat_t krhino_timer_dyn_create(ktimer_t **timer, const name_t *name, timer_cb_t cb, tick_t first, tick_t round, void *arg, uint8_t auto_run) { kstat_t ret; ktimer_t *timer_obj; NULL_PARA_CHK(timer); timer_obj = krhino_mm_alloc(sizeof(ktimer_t)); if (timer_obj == NULL) { return RHINO_NO_MEM; } ret = timer_create(timer_obj, name, cb, first, round, arg, auto_run, K_OBJ_DYN_ALLOC); if (ret != RHINO_SUCCESS) { krhino_mm_free(timer_obj); return ret; } *timer = timer_obj; return ret; } kstat_t krhino_timer_dyn_del(ktimer_t *timer) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); cb.timer = timer; cb.cb_num = TIMER_CMD_DYN_DEL; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } #endif kstat_t krhino_timer_start(ktimer_t *timer) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); cb.timer = timer; cb.cb_num = TIMER_CMD_START; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } kstat_t krhino_timer_stop(ktimer_t *timer) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); cb.timer = timer; cb.cb_num = TIMER_CMD_STOP; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } kstat_t krhino_timer_change(ktimer_t *timer, tick_t first, tick_t round) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); if (first == 0u) { return RHINO_INV_PARAM; } if (first > RHINO_MAX_TICKS) { return RHINO_INV_PARAM; } if (round > RHINO_MAX_TICKS) { return RHINO_INV_PARAM; } cb.timer = timer; cb.first = first; cb.u.round = round; cb.cb_num = TIMER_CMD_CHG; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } kstat_t krhino_timer_arg_change(ktimer_t *timer, void *arg) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); cb.timer = timer; cb.u.arg = arg; cb.cb_num = TIMER_ARG_CHG; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } kstat_t krhino_timer_arg_change_auto(ktimer_t *timer, void *arg) { k_timer_queue_cb cb; NULL_PARA_CHK(timer); cb.timer = timer; cb.u.arg = arg; cb.cb_num = TIMER_ARG_CHG_AUTO; return krhino_buf_queue_send(&g_timer_queue, &cb, sizeof(k_timer_queue_cb)); } static void timer_cb_proc(void) { klist_t *q; klist_t *start; klist_t *end; ktimer_t *timer; tick_i_t delta; start = end = &g_timer_head; for (q = start->next; q != end; q = q->next) { timer = krhino_list_entry(q, ktimer_t, timer_list); delta = (tick_i_t)timer->match - (tick_i_t)g_timer_count; if (delta <= 0) { timer->cb(timer, timer->timer_cb_arg); timer_list_rm(timer); if (timer->round_ticks > 0u) { timer->remain = timer->round_ticks; timer->match = g_timer_count + timer->remain; timer->to_head = &g_timer_head; timer_list_pri_insert(&g_timer_head, timer); } else { timer->timer_state = TIMER_DEACTIVE; } } else { break; } } } static void cmd_proc(k_timer_queue_cb *cb, uint8_t cmd) { ktimer_t *timer = cb->timer; if (timer->obj_type != RHINO_TIMER_OBJ_TYPE) { return; } switch (cmd) { case TIMER_CMD_START: if (timer->timer_state == TIMER_ACTIVE) { break; } timer->match = g_timer_count + timer->init_count; /* sort by remain time */ timer->remain = timer->init_count; /* used by timer delete */ timer->to_head = &g_timer_head; timer_list_pri_insert(&g_timer_head, timer); timer->timer_state = TIMER_ACTIVE; break; case TIMER_CMD_STOP: if (timer->timer_state == TIMER_DEACTIVE) { break; } timer_list_rm(timer); timer->timer_state = TIMER_DEACTIVE; break; case TIMER_CMD_CHG: if (cb->first == 0u) { break; } if (timer->timer_state != TIMER_DEACTIVE) { /* should stop timer before change attributes */ break; } timer->init_count = cb->first; timer->round_ticks = cb->u.round; break; case TIMER_ARG_CHG: if (timer->timer_state != TIMER_DEACTIVE) { break; } timer->timer_cb_arg = cb->u.arg; break; case TIMER_CMD_DEL: if (timer->timer_state != TIMER_DEACTIVE) { break; } if (timer->mm_alloc_flag != K_OBJ_STATIC_ALLOC) { break; } timer->obj_type = RHINO_OBJ_TYPE_NONE; TRACE_TIMER_DEL(krhino_cur_task_get(), timer); break; #if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0) case TIMER_CMD_DYN_DEL: if (timer->timer_state != TIMER_DEACTIVE) { break; } if (timer->mm_alloc_flag != K_OBJ_DYN_ALLOC) { break; } timer->obj_type = RHINO_OBJ_TYPE_NONE; TRACE_TIMER_DEL(krhino_cur_task_get(), timer); krhino_mm_free(timer); break; #endif default: k_err_proc(RHINO_SYS_FATAL_ERR); break; } } static void timer_cmd_proc(k_timer_queue_cb *cb) { if (cb->cb_num == TIMER_ARG_CHG_AUTO) { cmd_proc(cb, TIMER_CMD_STOP); cmd_proc(cb, TIMER_ARG_CHG); cmd_proc(cb, TIMER_CMD_START); } else { cmd_proc(cb, cb->cb_num); } } static void timer_task(void *pa) { ktimer_t *timer; k_timer_queue_cb cb_msg; kstat_t err; tick_t tick_start; tick_t tick_end; tick_i_t delta; size_t msg_size; (void)pa; while (RHINO_TRUE) { err = krhino_buf_queue_recv(&g_timer_queue, RHINO_WAIT_FOREVER, &cb_msg, &msg_size); tick_end = krhino_sys_tick_get(); if (err == RHINO_SUCCESS) { g_timer_count = tick_end; } else { k_err_proc(RHINO_SYS_FATAL_ERR); } timer_cmd_proc(&cb_msg); while (!is_klist_empty(&g_timer_head)) { timer = krhino_list_entry(g_timer_head.next, ktimer_t, timer_list); tick_start = krhino_sys_tick_get(); delta = (tick_i_t)timer->match - (tick_i_t)tick_start; if (delta > 0) { err = krhino_buf_queue_recv(&g_timer_queue, (tick_t)delta, &cb_msg, &msg_size); tick_end = krhino_sys_tick_get(); if (err == RHINO_BLK_TIMEOUT) { g_timer_count = tick_end; } else if (err == RHINO_SUCCESS) { g_timer_count = tick_end; timer_cb_proc(); timer_cmd_proc(&cb_msg); } else { k_err_proc(RHINO_SYS_FATAL_ERR); } } else { g_timer_count = tick_start; } timer_cb_proc(); } } } void ktimer_init(void) { klist_init(&g_timer_head); krhino_fix_buf_queue_create(&g_timer_queue, "timer_queue", timer_queue_cb, sizeof(k_timer_queue_cb), RHINO_CONFIG_TIMER_MSG_NUM); krhino_task_create(&g_timer_task, "timer_task", NULL, RHINO_CONFIG_TIMER_TASK_PRI, 0u, g_timer_task_stack, RHINO_CONFIG_TIMER_TASK_STACK_SIZE, timer_task, 1u); } #endif /* RHINO_CONFIG_TIMER */