1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-03-12     Bernard      first version
9  * 2006-04-29     Bernard      implement thread timer
10  * 2006-06-04     Bernard      implement rt_timer_control
11  * 2006-08-10     Bernard      fix the periodic timer bug
12  * 2006-09-03     Bernard      implement rt_timer_detach
13  * 2009-11-11     LiJin        add soft timer
14  * 2010-05-12     Bernard      fix the timer check bug.
15  * 2010-11-02     Charlie      re-implement tick overflow issue
16  * 2012-12-15     Bernard      fix the next timeout issue in soft timer
17  * 2014-07-12     Bernard      does not lock scheduler when invoking soft-timer
18  *                             timeout function.
19  * 2021-08-15     supperthomas add the comment
20  * 2022-01-07     Gabriel      Moving __on_rt_xxxxx_hook to timer.c
21  * 2022-04-19     Stanley      Correct descriptions
22  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
23  * 2024-01-25     Shell        add RT_TIMER_FLAG_THREAD_TIMER for timer to sync with sched
24  * 2024-05-01     wdfk-prog    The rt_timer_check and _soft_timer_check functions are merged
25  */
26 
27 #include <rtthread.h>
28 #include <rthw.h>
29 
30 #define DBG_TAG           "kernel.timer"
31 #define DBG_LVL           DBG_INFO
32 #include <rtdbg.h>
33 
34 #ifndef RT_USING_TIMER_ALL_SOFT
35 /* hard timer list */
36 static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL];
37 static struct rt_spinlock _htimer_lock;
38 #endif
39 
40 #ifdef RT_USING_TIMER_SOFT
41 
42 #ifndef RT_TIMER_THREAD_STACK_SIZE
43 #define RT_TIMER_THREAD_STACK_SIZE     512
44 #endif /* RT_TIMER_THREAD_STACK_SIZE */
45 
46 #ifndef RT_TIMER_THREAD_PRIO
47 #define RT_TIMER_THREAD_PRIO           0
48 #endif /* RT_TIMER_THREAD_PRIO */
49 
50 /* soft timer list */
51 static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
52 static struct rt_spinlock _stimer_lock;
53 static struct rt_thread _timer_thread;
54 static struct rt_semaphore _soft_timer_sem;
55 rt_align(RT_ALIGN_SIZE)
56 static rt_uint8_t _timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
57 #endif /* RT_USING_TIMER_SOFT */
58 
59 #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
60 extern void (*rt_object_take_hook)(struct rt_object *object);
61 extern void (*rt_object_put_hook)(struct rt_object *object);
62 static void (*rt_timer_enter_hook)(struct rt_timer *timer);
63 static void (*rt_timer_exit_hook)(struct rt_timer *timer);
64 
65 /**
66  * @addtogroup group_hook
67  */
68 
69 /**@{*/
70 
71 /**
72  * @brief This function will set a hook function on timer,
73  *        which will be invoked when enter timer timeout callback function.
74  *
75  * @param hook is the function point of timer
76  */
rt_timer_enter_sethook(void (* hook)(struct rt_timer * timer))77 void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer))
78 {
79     rt_timer_enter_hook = hook;
80 }
81 
82 /**
83  * @brief This function will set a hook function, which will be
84  *        invoked when exit timer timeout callback function.
85  *
86  * @param hook is the function point of timer
87  */
rt_timer_exit_sethook(void (* hook)(struct rt_timer * timer))88 void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer))
89 {
90     rt_timer_exit_hook = hook;
91 }
92 
93 /**@}*/
94 #endif /* RT_USING_HOOK */
95 
_timerlock_idx(struct rt_timer * timer)96 rt_inline struct rt_spinlock* _timerlock_idx(struct rt_timer *timer)
97 {
98 #ifdef RT_USING_TIMER_ALL_SOFT
99     return &_stimer_lock;
100 #else
101 #ifdef RT_USING_TIMER_SOFT
102     if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
103     {
104         return &_stimer_lock;
105     }
106     else
107 #endif /* RT_USING_TIMER_SOFT */
108     {
109         return &_htimer_lock;
110     }
111 #endif
112 }
113 
114 /**
115  * @brief [internal] The init funtion of timer
116  *
117  *        The internal called function of rt_timer_init
118  *
119  * @see rt_timer_init
120  *
121  * @param timer is timer object
122  *
123  * @param timeout is the timeout function
124  *
125  * @param parameter is the parameter of timeout function
126  *
127  * @param time is the tick of timer
128  *
129  * @param flag the flag of timer
130  */
_timer_init(rt_timer_t timer,void (* timeout)(void * parameter),void * parameter,rt_tick_t time,rt_uint8_t flag)131 static void _timer_init(rt_timer_t timer,
132                         void (*timeout)(void *parameter),
133                         void      *parameter,
134                         rt_tick_t  time,
135                         rt_uint8_t flag)
136 {
137     int i;
138 
139 #ifdef RT_USING_TIMER_ALL_SOFT
140     flag               |= RT_TIMER_FLAG_SOFT_TIMER;
141 #endif
142 
143     /* set flag */
144     timer->parent.flag  = flag;
145 
146     /* set deactivated */
147     timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
148 
149     timer->timeout_func = timeout;
150     timer->parameter    = parameter;
151 
152     timer->timeout_tick = 0;
153     timer->init_tick    = time;
154 
155     /* initialize timer list */
156     for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
157     {
158         rt_list_init(&(timer->row[i]));
159     }
160 }
161 
162 /**
163  * @brief  Find the next emtpy timer ticks
164  *
165  * @param timer_list is the array of time list
166  *
167  * @param timeout_tick is the next timer's ticks
168  *
169  * @return  Return the operation status. If the return value is RT_EOK, the function is successfully executed.
170  *          If the return value is any other values, it means this operation failed.
171  */
_timer_list_next_timeout(rt_list_t timer_list[],rt_tick_t * timeout_tick)172 static rt_err_t _timer_list_next_timeout(rt_list_t timer_list[], rt_tick_t *timeout_tick)
173 {
174     struct rt_timer *timer;
175 
176     if (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
177     {
178         timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
179                               struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
180         *timeout_tick = timer->timeout_tick;
181         return RT_EOK;
182     }
183     return -RT_ERROR;
184 }
185 
186 /**
187  * @brief Remove the timer
188  *
189  * @param timer the point of the timer
190  */
_timer_remove(rt_timer_t timer)191 rt_inline void _timer_remove(rt_timer_t timer)
192 {
193     int i;
194 
195     for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
196     {
197         rt_list_remove(&timer->row[i]);
198     }
199 }
200 
201 #if (DBG_LVL == DBG_LOG)
202 /**
203  * @brief The number of timer
204  *
205  * @param timer the head of timer
206  *
207  * @return count of timer
208  */
_timer_count_height(struct rt_timer * timer)209 static int _timer_count_height(struct rt_timer *timer)
210 {
211     int i, cnt = 0;
212 
213     for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
214     {
215         if (!rt_list_isempty(&timer->row[i]))
216             cnt++;
217     }
218     return cnt;
219 }
220 /**
221  * @brief dump the all timer information
222  *
223  * @param timer_heads the head of timer
224  */
rt_timer_dump(rt_list_t timer_heads[])225 void rt_timer_dump(rt_list_t timer_heads[])
226 {
227     rt_list_t *list;
228 
229     for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1].next;
230          list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1];
231          list = list->next)
232     {
233         struct rt_timer *timer = rt_list_entry(list,
234                                                struct rt_timer,
235                                                row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
236         rt_kprintf("%d", _timer_count_height(timer));
237     }
238     rt_kprintf("\n");
239 }
240 #endif /* (DBG_LVL == DBG_LOG) */
241 
242 /**
243  * @addtogroup group_clock_management
244  */
245 
246 /**@{*/
247 
248 /**
249  * @brief This function will initialize a timer
250  *        normally this function is used to initialize a static timer object.
251  *
252  * @param timer is the point of timer
253  *
254  * @param name is a pointer to the name of the timer
255  *
256  * @param timeout is the callback of timer
257  *
258  * @param parameter is the param of the callback
259  *
260  * @param time is timeout ticks of timer
261  *
262  *             NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
263  *
264  * @param flag is the flag of timer
265  *
266  */
rt_timer_init(rt_timer_t timer,const char * name,void (* timeout)(void * parameter),void * parameter,rt_tick_t time,rt_uint8_t flag)267 void rt_timer_init(rt_timer_t  timer,
268                    const char *name,
269                    void (*timeout)(void *parameter),
270                    void       *parameter,
271                    rt_tick_t   time,
272                    rt_uint8_t  flag)
273 {
274     /* parameter check */
275     RT_ASSERT(timer != RT_NULL);
276     RT_ASSERT(timeout != RT_NULL);
277     RT_ASSERT(time < RT_TICK_MAX / 2);
278 
279     /* timer object initialization */
280     rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);
281 
282     _timer_init(timer, timeout, parameter, time, flag);
283 }
284 RTM_EXPORT(rt_timer_init);
285 
286 /**
287  * @brief This function will detach a timer from timer management.
288  *
289  * @param timer is the timer to be detached
290  *
291  * @return the status of detach
292  */
rt_timer_detach(rt_timer_t timer)293 rt_err_t rt_timer_detach(rt_timer_t timer)
294 {
295     rt_base_t level;
296     struct rt_spinlock *spinlock;
297 
298     /* parameter check */
299     RT_ASSERT(timer != RT_NULL);
300     RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
301     RT_ASSERT(rt_object_is_systemobject(&timer->parent));
302 
303     spinlock = _timerlock_idx(timer);
304     level = rt_spin_lock_irqsave(spinlock);
305 
306     _timer_remove(timer);
307     /* stop timer */
308     timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
309 
310     rt_spin_unlock_irqrestore(spinlock, level);
311     rt_object_detach(&(timer->parent));
312 
313     return RT_EOK;
314 }
315 RTM_EXPORT(rt_timer_detach);
316 
317 #ifdef RT_USING_HEAP
318 /**
319  * @brief This function will create a timer
320  *
321  * @param name is the name of timer
322  *
323  * @param timeout is the timeout function
324  *
325  * @param parameter is the parameter of timeout function
326  *
327  * @param time is timeout ticks of the timer
328  *
329  *        NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
330  *
331  * @param flag is the flag of timer. Timer will invoke the timeout function according to the selected values of flag, if one or more of the following flags is set.
332  *
333  *          RT_TIMER_FLAG_ONE_SHOT          One shot timing
334  *          RT_TIMER_FLAG_PERIODIC          Periodic timing
335  *
336  *          RT_TIMER_FLAG_HARD_TIMER        Hardware timer
337  *          RT_TIMER_FLAG_SOFT_TIMER        Software timer
338  *          RT_TIMER_FLAG_THREAD_TIMER      Thread timer
339  *
340  *        NOTE:
341  *        You can use multiple values with "|" logical operator.  By default, system will use the RT_TIME_FLAG_HARD_TIMER.
342  *
343  * @return the created timer object
344  */
rt_timer_create(const char * name,void (* timeout)(void * parameter),void * parameter,rt_tick_t time,rt_uint8_t flag)345 rt_timer_t rt_timer_create(const char *name,
346                            void (*timeout)(void *parameter),
347                            void       *parameter,
348                            rt_tick_t   time,
349                            rt_uint8_t  flag)
350 {
351     struct rt_timer *timer;
352 
353     /* parameter check */
354     RT_ASSERT(timeout != RT_NULL);
355     RT_ASSERT(time < RT_TICK_MAX / 2);
356 
357     /* allocate a object */
358     timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
359     if (timer == RT_NULL)
360     {
361         return RT_NULL;
362     }
363 
364     _timer_init(timer, timeout, parameter, time, flag);
365 
366     return timer;
367 }
368 RTM_EXPORT(rt_timer_create);
369 
370 /**
371  * @brief This function will delete a timer and release timer memory
372  *
373  * @param timer the timer to be deleted
374  *
375  * @return the operation status, RT_EOK on OK; -RT_ERROR on error
376  */
rt_timer_delete(rt_timer_t timer)377 rt_err_t rt_timer_delete(rt_timer_t timer)
378 {
379     rt_base_t level;
380     struct rt_spinlock *spinlock;
381 
382     /* parameter check */
383     RT_ASSERT(timer != RT_NULL);
384     RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
385     RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
386 
387     spinlock = _timerlock_idx(timer);
388 
389     level = rt_spin_lock_irqsave(spinlock);
390 
391     _timer_remove(timer);
392     /* stop timer */
393     timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
394     rt_spin_unlock_irqrestore(spinlock, level);
395     rt_object_delete(&(timer->parent));
396 
397     return RT_EOK;
398 }
399 RTM_EXPORT(rt_timer_delete);
400 #endif /* RT_USING_HEAP */
401 
402 /**
403  * @brief This function will start the timer
404  *
405  * @param timer the timer to be started
406  *
407  * @return the operation status, RT_EOK on OK, -RT_ERROR on error
408  */
_timer_start(rt_list_t * timer_list,rt_timer_t timer)409 static rt_err_t _timer_start(rt_list_t *timer_list, rt_timer_t timer)
410 {
411     unsigned int row_lvl;
412     rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
413     unsigned int tst_nr;
414     static unsigned int random_nr;
415 
416     /* remove timer from list */
417     _timer_remove(timer);
418     /* change status of timer */
419     timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
420 
421     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
422 
423     timer->timeout_tick = rt_tick_get() + timer->init_tick;
424 
425     row_head[0]  = &timer_list[0];
426     for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
427     {
428         for (; row_head[row_lvl] != timer_list[row_lvl].prev;
429              row_head[row_lvl]  = row_head[row_lvl]->next)
430         {
431             struct rt_timer *t;
432             rt_list_t *p = row_head[row_lvl]->next;
433 
434             /* fix up the entry pointer */
435             t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
436 
437             /* If we have two timers that timeout at the same time, it's
438              * preferred that the timer inserted early get called early.
439              * So insert the new timer to the end the the some-timeout timer
440              * list.
441              */
442             if ((t->timeout_tick - timer->timeout_tick) == 0)
443             {
444                 continue;
445             }
446             else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
447             {
448                 break;
449             }
450         }
451         if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
452             row_head[row_lvl + 1] = row_head[row_lvl] + 1;
453     }
454 
455     /* Interestingly, this super simple timer insert counter works very very
456      * well on distributing the list height uniformly. By means of "very very
457      * well", I mean it beats the randomness of timer->timeout_tick very easily
458      * (actually, the timeout_tick is not random and easy to be attacked). */
459     random_nr++;
460     tst_nr = random_nr;
461 
462     rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
463                          &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
464     for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
465     {
466         if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
467             rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
468                                  &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
469         else
470             break;
471         /* Shift over the bits we have tested. Works well with 1 bit and 2
472          * bits. */
473         tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
474     }
475 
476     timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
477 
478     return RT_EOK;
479 }
480 
481 /**
482  * @brief This function will check timer list, if a timeout event happens,
483  *        the corresponding timeout function will be invoked.
484  *
485  * @param timer_list The timer list to check.
486  * @param lock The lock for the timer list.
487  */
_timer_check(rt_list_t * timer_list,struct rt_spinlock * lock)488 static void _timer_check(rt_list_t *timer_list, struct rt_spinlock *lock)
489 {
490     struct rt_timer *t;
491     rt_tick_t current_tick;
492     rt_base_t level;
493     rt_list_t list;
494 
495     level = rt_spin_lock_irqsave(lock);
496 
497     current_tick = rt_tick_get();
498 
499     rt_list_init(&list);
500 
501     while (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
502     {
503         t = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
504                           struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
505 
506         /* re-get tick */
507         current_tick = rt_tick_get();
508 
509         /*
510          * It supposes that the new tick shall less than the half duration of
511          * tick max.
512          */
513         if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
514         {
515             RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
516 
517             /* remove timer from timer list firstly */
518             _timer_remove(t);
519             if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
520             {
521                 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
522             }
523 
524             /* add timer to temporary list  */
525             rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
526 
527             rt_spin_unlock_irqrestore(lock, level);
528 
529             /* call timeout function */
530             t->timeout_func(t->parameter);
531 
532             RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
533 
534             level = rt_spin_lock_irqsave(lock);
535 
536             /* Check whether the timer object is detached or started again */
537             if (rt_list_isempty(&list))
538             {
539                 continue;
540             }
541             rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
542             if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
543                 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
544             {
545                 /* start it */
546                 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
547                 _timer_start(timer_list, t);
548             }
549         }
550         else break;
551     }
552     rt_spin_unlock_irqrestore(lock, level);
553 }
554 
555 /**
556  * @brief This function will start the timer
557  *
558  * @param timer the timer to be started
559  *
560  * @return the operation status, RT_EOK on OK, -RT_ERROR on error
561  */
rt_timer_start(rt_timer_t timer)562 rt_err_t rt_timer_start(rt_timer_t timer)
563 {
564     rt_sched_lock_level_t slvl;
565     int is_thread_timer = 0;
566     struct rt_spinlock *spinlock;
567     rt_list_t *timer_list;
568     rt_base_t level;
569     rt_err_t err;
570 
571     /* parameter check */
572     RT_ASSERT(timer != RT_NULL);
573     RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
574 
575 #ifdef RT_USING_TIMER_ALL_SOFT
576     timer_list = _soft_timer_list;
577     spinlock = &_stimer_lock;
578 #else
579 #ifdef RT_USING_TIMER_SOFT
580     if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
581     {
582         timer_list = _soft_timer_list;
583         spinlock = &_stimer_lock;
584     }
585     else
586 #endif /* RT_USING_TIMER_SOFT */
587     {
588         timer_list = _timer_list;
589         spinlock = &_htimer_lock;
590     }
591 #endif
592 
593     if (timer->parent.flag & RT_TIMER_FLAG_THREAD_TIMER)
594     {
595         rt_thread_t thread;
596         is_thread_timer = 1;
597         rt_sched_lock(&slvl);
598 
599         thread = rt_container_of(timer, struct rt_thread, thread_timer);
600         RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread);
601         rt_sched_thread_timer_start(thread);
602     }
603 
604     level = rt_spin_lock_irqsave(spinlock);
605 
606     err = _timer_start(timer_list, timer);
607 
608     rt_spin_unlock_irqrestore(spinlock, level);
609 
610     if (is_thread_timer)
611     {
612         rt_sched_unlock(slvl);
613     }
614 
615     return err;
616 }
617 RTM_EXPORT(rt_timer_start);
618 
619 /**
620  * @brief This function will stop the timer
621  *
622  * @param timer the timer to be stopped
623  *
624  * @return the operation status, RT_EOK on OK, -RT_ERROR on error
625  */
rt_timer_stop(rt_timer_t timer)626 rt_err_t rt_timer_stop(rt_timer_t timer)
627 {
628     rt_base_t level;
629     struct rt_spinlock *spinlock;
630 
631     /* timer check */
632     RT_ASSERT(timer != RT_NULL);
633     RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
634 
635     spinlock = _timerlock_idx(timer);
636 
637     level = rt_spin_lock_irqsave(spinlock);
638 
639     if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
640     {
641         rt_spin_unlock_irqrestore(spinlock, level);
642         return -RT_ERROR;
643     }
644     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
645 
646     _timer_remove(timer);
647     /* change status */
648     timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
649 
650     rt_spin_unlock_irqrestore(spinlock, level);
651 
652     return RT_EOK;
653 }
654 RTM_EXPORT(rt_timer_stop);
655 
656 /**
657  * @brief This function will get or set some options of the timer
658  *
659  * @param timer the timer to be get or set
660  * @param cmd the control command
661  * @param arg the argument
662  *
663  * @return the statu of control
664  */
rt_timer_control(rt_timer_t timer,int cmd,void * arg)665 rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
666 {
667     struct rt_spinlock *spinlock;
668     rt_base_t level;
669 
670     /* parameter check */
671     RT_ASSERT(timer != RT_NULL);
672     RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
673 
674     spinlock = _timerlock_idx(timer);
675 
676     level = rt_spin_lock_irqsave(spinlock);
677     switch (cmd)
678     {
679     case RT_TIMER_CTRL_GET_TIME:
680         *(rt_tick_t *)arg = timer->init_tick;
681         break;
682 
683     case RT_TIMER_CTRL_SET_TIME:
684         RT_ASSERT((*(rt_tick_t *)arg) < RT_TICK_MAX / 2);
685         if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
686         {
687             _timer_remove(timer);
688             timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
689         }
690         timer->init_tick = *(rt_tick_t *)arg;
691         break;
692 
693     case RT_TIMER_CTRL_SET_ONESHOT:
694         timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
695         break;
696 
697     case RT_TIMER_CTRL_SET_PERIODIC:
698         timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
699         break;
700 
701     case RT_TIMER_CTRL_GET_STATE:
702         if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
703         {
704             /*timer is start and run*/
705             *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
706         }
707         else
708         {
709             /*timer is stop*/
710             *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
711         }
712         break;
713 
714     case RT_TIMER_CTRL_GET_REMAIN_TIME:
715         *(rt_tick_t *)arg =  timer->timeout_tick;
716         break;
717     case RT_TIMER_CTRL_GET_FUNC:
718         *(void **)arg = (void *)timer->timeout_func;
719         break;
720 
721     case RT_TIMER_CTRL_SET_FUNC:
722         timer->timeout_func = (void (*)(void*))arg;
723         break;
724 
725     case RT_TIMER_CTRL_GET_PARM:
726         *(void **)arg = timer->parameter;
727         break;
728 
729     case RT_TIMER_CTRL_SET_PARM:
730         timer->parameter = arg;
731         break;
732 
733     default:
734         break;
735     }
736     rt_spin_unlock_irqrestore(spinlock, level);
737 
738     return RT_EOK;
739 }
740 RTM_EXPORT(rt_timer_control);
741 
742 /**
743  * @brief This function will check timer list, if a timeout event happens,
744  *        the corresponding timeout function will be invoked.
745  *
746  * @note This function shall be invoked in operating system timer interrupt.
747  */
rt_timer_check(void)748 void rt_timer_check(void)
749 {
750     RT_ASSERT(rt_interrupt_get_nest() > 0);
751 
752 #ifdef RT_USING_SMP
753     /* Running on core 0 only */
754     if (rt_cpu_get_id() != 0)
755     {
756         return;
757     }
758 #endif
759 
760 #ifdef RT_USING_TIMER_SOFT
761     rt_err_t ret = RT_ERROR;
762     rt_tick_t next_timeout;
763 
764     ret = _timer_list_next_timeout(_soft_timer_list, &next_timeout);
765     if ((ret == RT_EOK) && (next_timeout <= rt_tick_get()))
766     {
767         rt_sem_release(&_soft_timer_sem);
768     }
769 #endif
770 #ifndef RT_USING_TIMER_ALL_SOFT
771     _timer_check(_timer_list, &_htimer_lock);
772 #endif
773 }
774 
775 /**
776  * @brief This function will return the next timeout tick in the system.
777  *
778  * @return the next timeout tick in the system
779  */
rt_timer_next_timeout_tick(void)780 rt_tick_t rt_timer_next_timeout_tick(void)
781 {
782     rt_base_t level;
783     rt_tick_t htimer_next_timeout = RT_TICK_MAX, stimer_next_timeout = RT_TICK_MAX;
784 
785 #ifndef RT_USING_TIMER_ALL_SOFT
786     level = rt_spin_lock_irqsave(&_htimer_lock);
787     _timer_list_next_timeout(_timer_list, &htimer_next_timeout);
788     rt_spin_unlock_irqrestore(&_htimer_lock, level);
789 #endif
790 
791 #ifdef RT_USING_TIMER_SOFT
792     level = rt_spin_lock_irqsave(&_stimer_lock);
793     _timer_list_next_timeout(_soft_timer_list, &stimer_next_timeout);
794     rt_spin_unlock_irqrestore(&_stimer_lock, level);
795 #endif
796 
797     return htimer_next_timeout < stimer_next_timeout ? htimer_next_timeout : stimer_next_timeout;
798 }
799 
800 #ifdef RT_USING_TIMER_SOFT
801 /**
802  * @brief System timer thread entry
803  *
804  * @param parameter is the arg of the thread
805  */
_timer_thread_entry(void * parameter)806 static void _timer_thread_entry(void *parameter)
807 {
808     RT_UNUSED(parameter);
809 
810     while (1)
811     {
812         _timer_check(_soft_timer_list, &_stimer_lock); /* check software timer */
813         rt_sem_take(&_soft_timer_sem, RT_WAITING_FOREVER);
814     }
815 }
816 #endif /* RT_USING_TIMER_SOFT */
817 
818 /**
819  * @ingroup group_system_init
820  *
821  * @brief This function will initialize system timer
822  */
rt_system_timer_init(void)823 void rt_system_timer_init(void)
824 {
825 #ifndef RT_USING_TIMER_ALL_SOFT
826     rt_size_t i;
827 
828     for (i = 0; i < sizeof(_timer_list) / sizeof(_timer_list[0]); i++)
829     {
830         rt_list_init(_timer_list + i);
831     }
832 
833     rt_spin_lock_init(&_htimer_lock);
834 #endif
835 }
836 
837 /**
838  * @ingroup group_system_init
839  *
840  * @brief This function will initialize system timer thread
841  */
rt_system_timer_thread_init(void)842 void rt_system_timer_thread_init(void)
843 {
844 #ifdef RT_USING_TIMER_SOFT
845     int i;
846 
847     for (i = 0;
848          i < sizeof(_soft_timer_list) / sizeof(_soft_timer_list[0]);
849          i++)
850     {
851         rt_list_init(_soft_timer_list + i);
852     }
853     rt_spin_lock_init(&_stimer_lock);
854     rt_sem_init(&_soft_timer_sem, "stimer", 0, RT_IPC_FLAG_PRIO);
855     rt_sem_control(&_soft_timer_sem, RT_IPC_CMD_SET_VLIMIT, (void*)1);
856     /* start software timer thread */
857     rt_thread_init(&_timer_thread,
858                    "timer",
859                    _timer_thread_entry,
860                    RT_NULL,
861                    &_timer_thread_stack[0],
862                    sizeof(_timer_thread_stack),
863                    RT_TIMER_THREAD_PRIO,
864                    10);
865 
866     /* startup */
867     rt_thread_startup(&_timer_thread);
868 #endif /* RT_USING_TIMER_SOFT */
869 }
870 
871 /**@}*/
872