1 /*
2  * Copyright (c) 2006-2025 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2012-10-27     heyuanjie87       first version.
9  * 2013-05-17     aozima            initial alarm event & mutex in system init.
10  * 2020-10-15     zhangsz           add alarm flags hour minute second.
11  * 2020-11-09     zhangsz           fix alarm set when modify rtc time.
12  * 2024-09-29     milo              make internal thread's attributes configurable.
13  * 2025-6-4       RCSN              support the alarm using local time for calculation
14  */
15 
16 #include <rtthread.h>
17 #include <rtdevice.h>
18 #include <sys/time.h>
19 
20 #define RT_RTC_YEARS_MAX         137
21 #ifdef RT_USING_SOFT_RTC
22 #define RT_ALARM_DELAY             0
23 #else
24 #define RT_ALARM_DELAY             2
25 #endif
26 
27 #if (defined(RT_USING_RTC) && defined(RT_USING_ALARM))
28 #ifndef RT_ALARM_STACK_SIZE
29 #define RT_ALARM_STACK_SIZE       2048
30 #endif
31 #ifndef RT_ALARM_TIMESLICE
32 #define RT_ALARM_TIMESLICE        5
33 #endif
34 #ifndef RT_ALARM_PRIORITY
35 #define RT_ALARM_PRIORITY         10
36 #endif
37 static struct rt_alarm_container _container;
38 
alarm_mkdaysec(struct tm * time)39 rt_inline rt_uint32_t alarm_mkdaysec(struct tm *time)
40 {
41     rt_uint32_t sec;
42 
43     sec = time->tm_sec;
44     sec += time->tm_min * 60;
45     sec += time->tm_hour * 3600;
46 
47     return (sec);
48 }
49 
alarm_set(struct rt_alarm * alarm)50 static rt_err_t alarm_set(struct rt_alarm *alarm)
51 {
52     rt_device_t device;
53     struct rt_rtc_wkalarm wkalarm;
54     rt_err_t ret;
55 
56     device = rt_device_find("rtc");
57 
58     if (device == RT_NULL)
59     {
60         return (RT_ERROR);
61     }
62 
63     if (alarm->flag & RT_ALARM_STATE_START)
64         wkalarm.enable = RT_TRUE;
65     else
66         wkalarm.enable = RT_FALSE;
67 
68     wkalarm.tm_sec = alarm->wktime.tm_sec;
69     wkalarm.tm_min = alarm->wktime.tm_min;
70     wkalarm.tm_hour = alarm->wktime.tm_hour;
71     wkalarm.tm_mday = alarm->wktime.tm_mday;
72     wkalarm.tm_mon = alarm->wktime.tm_mon;
73     wkalarm.tm_year = alarm->wktime.tm_year;
74 
75     ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_SET_ALARM, &wkalarm);
76     if ((ret == RT_EOK) && wkalarm.enable)
77     {
78         ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_GET_ALARM, &wkalarm);
79         if (ret == RT_EOK)
80         {
81             /*
82               some RTC device like RX8025,it's alarms precision is 1 minute.
83               in this case,low level RTC driver should set wkalarm->tm_sec to 0.
84             */
85             alarm->wktime.tm_sec = wkalarm.tm_sec;
86             alarm->wktime.tm_min = wkalarm.tm_min;
87             alarm->wktime.tm_hour = wkalarm.tm_hour;
88             alarm->wktime.tm_mday = wkalarm.tm_mday;
89             alarm->wktime.tm_mon = wkalarm.tm_mon;
90             alarm->wktime.tm_year = wkalarm.tm_year;
91         }
92     }
93 
94     return (ret);
95 }
96 
alarm_wakeup(struct rt_alarm * alarm,struct tm * now)97 static void alarm_wakeup(struct rt_alarm *alarm, struct tm *now)
98 {
99     rt_uint32_t sec_alarm, sec_now;
100     rt_bool_t wakeup = RT_FALSE;
101     time_t timestamp;
102 
103     sec_alarm = alarm_mkdaysec(&alarm->wktime);
104     sec_now = alarm_mkdaysec(now);
105 
106     if (alarm->flag & RT_ALARM_STATE_START)
107     {
108         switch (alarm->flag & 0xFF00)
109         {
110         case RT_ALARM_ONESHOT:
111         {
112 #ifdef RT_ALARM_USING_LOCAL_TIME
113             sec_alarm = mktime(&alarm->wktime);
114             sec_now = mktime(now);
115 #else
116             sec_alarm = timegm(&alarm->wktime);
117             sec_now = timegm(now);
118 #endif
119             if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
120             {
121                 /* stop alarm */
122                 alarm->flag &= ~RT_ALARM_STATE_START;
123                 alarm_set(alarm);
124                 wakeup = RT_TRUE;
125             }
126         }
127         break;
128         case RT_ALARM_SECOND:
129         {
130             alarm->wktime.tm_hour = now->tm_hour;
131             alarm->wktime.tm_min = now->tm_min;
132             alarm->wktime.tm_sec = now->tm_sec + 1;
133             if (alarm->wktime.tm_sec > 59)
134             {
135                 alarm->wktime.tm_sec = 0;
136                 alarm->wktime.tm_min = alarm->wktime.tm_min + 1;
137                 if (alarm->wktime.tm_min > 59)
138                 {
139                     alarm->wktime.tm_min = 0;
140                     alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
141                     if (alarm->wktime.tm_hour > 23)
142                     {
143                         alarm->wktime.tm_hour = 0;
144                     }
145                 }
146             }
147             wakeup = RT_TRUE;
148         }
149         break;
150         case RT_ALARM_MINUTE:
151         {
152             alarm->wktime.tm_hour = now->tm_hour;
153             if (alarm->wktime.tm_sec == now->tm_sec)
154             {
155                 alarm->wktime.tm_min = now->tm_min + 1;
156                 if (alarm->wktime.tm_min > 59)
157                 {
158                     alarm->wktime.tm_min = 0;
159                     alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
160                     if (alarm->wktime.tm_hour > 23)
161                     {
162                         alarm->wktime.tm_hour = 0;
163                     }
164                 }
165                 wakeup = RT_TRUE;
166             }
167         }
168         break;
169         case RT_ALARM_HOUR:
170         {
171             if ((alarm->wktime.tm_min == now->tm_min) &&
172                 (alarm->wktime.tm_sec == now->tm_sec))
173             {
174                 alarm->wktime.tm_hour = now->tm_hour + 1;
175                 if (alarm->wktime.tm_hour > 23)
176                 {
177                     alarm->wktime.tm_hour = 0;
178                 }
179                 wakeup = RT_TRUE;
180             }
181         }
182         break;
183         case RT_ALARM_DAILY:
184         {
185             if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
186                 wakeup = RT_TRUE;
187         }
188         break;
189         case RT_ALARM_WEEKLY:
190         {
191             /* alarm at wday */
192             if (alarm->wktime.tm_wday == now->tm_wday)
193             {
194                 sec_alarm += alarm->wktime.tm_wday * 24 * 3600;
195                 sec_now += now->tm_wday * 24 * 3600;
196 
197                 if (sec_now == sec_alarm)
198                     wakeup = RT_TRUE;
199             }
200         }
201         break;
202         case RT_ALARM_MONTHLY:
203         {
204             /* monthly someday generate alarm signals */
205             if (alarm->wktime.tm_mday == now->tm_mday)
206             {
207                 if ((sec_now - sec_alarm) <= RT_ALARM_DELAY)
208                     wakeup = RT_TRUE;
209             }
210         }
211         break;
212         case RT_ALARM_YAERLY:
213         {
214             if ((alarm->wktime.tm_mday == now->tm_mday) && \
215                     (alarm->wktime.tm_mon == now->tm_mon))
216             {
217                 if ((sec_now - sec_alarm) <= RT_ALARM_DELAY)
218                     wakeup = RT_TRUE;
219             }
220         }
221         break;
222         }
223 
224         if ((wakeup == RT_TRUE) && (alarm->callback != RT_NULL))
225         {
226             timestamp = (time_t)0;
227             get_timestamp(&timestamp);
228             alarm->callback(alarm, timestamp);
229         }
230     }
231 }
232 
alarm_update(rt_uint32_t event)233 static void alarm_update(rt_uint32_t event)
234 {
235     struct rt_alarm *alm_prev = RT_NULL, *alm_next = RT_NULL;
236     struct rt_alarm *alarm;
237     rt_int32_t sec_now, sec_alarm, sec_tmp;
238     rt_int32_t sec_next = 24 * 3600, sec_prev = 0;
239     time_t timestamp = (time_t)0;
240     struct tm now;
241     rt_list_t *next;
242 
243     rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
244     if (!rt_list_isempty(&_container.head))
245     {
246         /* get time of now */
247         get_timestamp(&timestamp);
248 #ifdef RT_ALARM_USING_LOCAL_TIME
249         localtime_r(&timestamp, &now);
250 #else
251         gmtime_r(&timestamp, &now);
252 #endif
253 
254         for (next = _container.head.next; next != &_container.head; next = next->next)
255         {
256             alarm = rt_list_entry(next, struct rt_alarm, list);
257             /* check the overtime alarm */
258             alarm_wakeup(alarm, &now);
259         }
260 
261         /* get time of now */
262         get_timestamp(&timestamp);
263 #ifdef RT_ALARM_USING_LOCAL_TIME
264         localtime_r(&timestamp, &now);
265 #else
266         gmtime_r(&timestamp, &now);
267 #endif
268         sec_now = alarm_mkdaysec(&now);
269 
270         for (next = _container.head.next; next != &_container.head; next = next->next)
271         {
272             alarm = rt_list_entry(next, struct rt_alarm, list);
273             /* calculate seconds from 00:00:00 */
274             sec_alarm = alarm_mkdaysec(&alarm->wktime);
275             if (alarm->flag & RT_ALARM_STATE_START)
276             {
277                 sec_tmp = sec_alarm - sec_now;
278                 if (sec_tmp > 0)
279                 {
280                     /* find alarm after now(now to 23:59:59) and the most recent */
281                     if (sec_tmp < sec_next)
282                     {
283                         sec_next = sec_tmp;
284                         alm_next = alarm;
285                     }
286                 }
287                 else
288                 {
289                     /* find alarm before now(00:00:00 to now) and furthest from now */
290                     if (sec_tmp < sec_prev)
291                     {
292                         sec_prev = sec_tmp;
293                         alm_prev = alarm;
294                     }
295                 }
296             }
297         }
298 
299         /* enable the alarm after now first */
300         if (sec_next < 24 * 3600)
301         {
302             if (alarm_set(alm_next) == RT_EOK)
303                 _container.current = alm_next;
304         }
305         else if (sec_prev < 0)
306         {
307             /* enable the alarm before now */
308             if (alarm_set(alm_prev) == RT_EOK)
309                 _container.current = alm_prev;
310         }
311         else
312         {
313             if (_container.current != RT_NULL)
314             {
315                 alarm_set(_container.current);
316                 if (!(_container.current->flag & RT_ALARM_STATE_START))
317                     _container.current = RT_NULL;
318             }
319         }
320     }
321     rt_mutex_release(&_container.mutex);
322 }
323 
days_of_year_month(int tm_year,int tm_mon)324 static int days_of_year_month(int tm_year, int tm_mon)
325 {
326     int ret, year;
327 
328     year = tm_year + 1900;
329     if (tm_mon == 1)
330     {
331         ret = 28 + ((!(year % 4) && (year % 100)) || !(year % 400));
332     }
333     else if (((tm_mon <= 6) && (tm_mon % 2 == 0)) || ((tm_mon > 6) && (tm_mon % 2 == 1)))
334     {
335         ret = 31;
336     }
337     else
338     {
339         ret = 30;
340     }
341 
342     return (ret);
343 }
344 
is_valid_date(struct tm * date)345 static rt_bool_t is_valid_date(struct tm *date)
346 {
347     if ((date->tm_year < 0) || (date->tm_year > RT_RTC_YEARS_MAX))
348     {
349         return (RT_FALSE);
350     }
351 
352     if ((date->tm_mon < 0) || (date->tm_mon > 11))
353     {
354         return (RT_FALSE);
355     }
356 
357     if ((date->tm_mday < 1) || \
358             (date->tm_mday > days_of_year_month(date->tm_year, date->tm_mon)))
359     {
360         return (RT_FALSE);
361     }
362 
363     return (RT_TRUE);
364 }
365 
alarm_setup(rt_alarm_t alarm,struct tm * wktime)366 static rt_err_t alarm_setup(rt_alarm_t alarm, struct tm *wktime)
367 {
368     rt_err_t ret = -RT_ERROR;
369     time_t timestamp = (time_t)0;
370     struct tm *setup, now;
371 
372     setup = &alarm->wktime;
373     *setup = *wktime;
374     /* get time of now */
375     get_timestamp(&timestamp);
376 #ifdef RT_ALARM_USING_LOCAL_TIME
377     localtime_r(&timestamp, &now);
378 #else
379     gmtime_r(&timestamp, &now);
380 #endif
381 
382     /* if these are a "don't care" value,we set them to now*/
383     if ((setup->tm_sec > 59) || (setup->tm_sec < 0))
384         setup->tm_sec = now.tm_sec;
385     if ((setup->tm_min > 59) || (setup->tm_min < 0))
386         setup->tm_min = now.tm_min;
387     if ((setup->tm_hour > 23) || (setup->tm_hour < 0))
388         setup->tm_hour = now.tm_hour;
389 
390     switch (alarm->flag & 0xFF00)
391     {
392     case RT_ALARM_SECOND:
393     {
394         alarm->wktime.tm_hour = now.tm_hour;
395         alarm->wktime.tm_min = now.tm_min;
396         alarm->wktime.tm_sec = now.tm_sec + 1;
397         if (alarm->wktime.tm_sec > 59)
398         {
399             alarm->wktime.tm_sec = 0;
400             alarm->wktime.tm_min = alarm->wktime.tm_min + 1;
401             if (alarm->wktime.tm_min > 59)
402             {
403                 alarm->wktime.tm_min = 0;
404                 alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
405                 if (alarm->wktime.tm_hour > 23)
406                 {
407                     alarm->wktime.tm_hour = 0;
408                 }
409             }
410         }
411     }
412     break;
413     case RT_ALARM_MINUTE:
414     {
415         alarm->wktime.tm_hour = now.tm_hour;
416         alarm->wktime.tm_min = now.tm_min + 1;
417         if (alarm->wktime.tm_min > 59)
418         {
419             alarm->wktime.tm_min = 0;
420             alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
421             if (alarm->wktime.tm_hour > 23)
422             {
423                 alarm->wktime.tm_hour = 0;
424             }
425         }
426     }
427     break;
428     case RT_ALARM_HOUR:
429     {
430         alarm->wktime.tm_hour = now.tm_hour + 1;
431         if (alarm->wktime.tm_hour > 23)
432         {
433             alarm->wktime.tm_hour = 0;
434         }
435     }
436     break;
437     case RT_ALARM_DAILY:
438     {
439         /* do nothing but needed */
440     }
441     break;
442     case RT_ALARM_ONESHOT:
443     {
444         /* if these are "don't care" value we set them to now */
445         if (setup->tm_year == RT_ALARM_TM_NOW)
446             setup->tm_year = now.tm_year;
447         if (setup->tm_mon == RT_ALARM_TM_NOW)
448             setup->tm_mon = now.tm_mon;
449         if (setup->tm_mday == RT_ALARM_TM_NOW)
450             setup->tm_mday = now.tm_mday;
451         /* make sure the setup is valid */
452         if (!is_valid_date(setup))
453             goto _exit;
454     }
455     break;
456     case RT_ALARM_WEEKLY:
457     {
458         /* if tm_wday is a "don't care" value we set it to now */
459         if ((setup->tm_wday < 0) || (setup->tm_wday > 6))
460             setup->tm_wday = now.tm_wday;
461     }
462     break;
463     case RT_ALARM_MONTHLY:
464     {
465         /* if tm_mday is a "don't care" value we set it to now */
466         if ((setup->tm_mday < 1) || (setup->tm_mday > 31))
467             setup->tm_mday = now.tm_mday;
468     }
469     break;
470     case RT_ALARM_YAERLY:
471     {
472         /* if tm_mon is a "don't care" value we set it to now */
473         if ((setup->tm_mon < 0) || (setup->tm_mon > 11))
474             setup->tm_mon = now.tm_mon;
475 
476         if (setup->tm_mon == 1)
477         {
478             /* tm_mon is February */
479 
480             /* tm_mday should be 1~29.otherwise,it's a "don't care" value */
481             if ((setup->tm_mday < 1) || (setup->tm_mday > 29))
482                 setup->tm_mday = now.tm_mday;
483         }
484         else if (((setup->tm_mon <= 6) && (setup->tm_mon % 2 == 0)) || \
485                  ((setup->tm_mon > 6) && (setup->tm_mon % 2 == 1)))
486         {
487             /* Jan,Mar,May,Jul,Aug,Oct,Dec */
488 
489             /* tm_mday should be 1~31.otherwise,it's a "don't care" value */
490             if ((setup->tm_mday < 1) || (setup->tm_mday > 31))
491                 setup->tm_mday = now.tm_mday;
492         }
493         else
494         {
495             /* tm_mday should be 1~30.otherwise,it's a "don't care" value */
496             if ((setup->tm_mday < 1) || (setup->tm_mday > 30))
497                 setup->tm_mday = now.tm_mday;
498         }
499     }
500     break;
501     default:
502     {
503         goto _exit;
504     }
505     }
506 
507     if ((setup->tm_hour == 23) && (setup->tm_min == 59) && (setup->tm_sec == 59))
508     {
509         /*
510            for insurance purposes, we will generate an alarm
511            signal two seconds ahead of.
512         */
513         setup->tm_sec = 60 - RT_ALARM_DELAY;
514     }
515     /* set initialized state */
516     alarm->flag |= RT_ALARM_STATE_INITED;
517     ret = RT_EOK;
518 
519 _exit:
520 
521     return (ret);
522 }
523 
524 /** \brief send a rtc alarm event
525  *
526  * \param dev pointer to RTC device(currently unused,you can ignore it)
527  * \param event RTC event(currently unused)
528  * \return none
529  */
rt_alarm_update(rt_device_t dev,rt_uint32_t event)530 void rt_alarm_update(rt_device_t dev, rt_uint32_t event)
531 {
532     rt_event_send(&_container.event, 1);
533 }
534 
535 /** \brief modify the alarm setup
536  *
537  * \param alarm pointer to alarm
538  * \param cmd control command
539  * \param arg argument
540  */
rt_alarm_control(rt_alarm_t alarm,int cmd,void * arg)541 rt_err_t rt_alarm_control(rt_alarm_t alarm, int cmd, void *arg)
542 {
543     rt_err_t ret = -RT_ERROR;
544 
545     RT_ASSERT(alarm != RT_NULL);
546 
547     rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
548     switch (cmd)
549     {
550     case RT_ALARM_CTRL_MODIFY:
551     {
552         struct rt_alarm_setup *setup;
553 
554         RT_ASSERT(arg != RT_NULL);
555         setup = arg;
556         rt_alarm_stop(alarm);
557         alarm->flag = setup->flag & 0xFF00;
558         alarm->wktime = setup->wktime;
559         ret = alarm_setup(alarm, &alarm->wktime);
560     }
561     break;
562     }
563 
564     rt_mutex_release(&_container.mutex);
565 
566     return (ret);
567 }
568 
569 /** \brief start an alarm
570  *
571  * \param alarm pointer to alarm
572  * \return RT_EOK
573  */
rt_alarm_start(rt_alarm_t alarm)574 rt_err_t rt_alarm_start(rt_alarm_t alarm)
575 {
576     rt_int32_t sec_now, sec_old, sec_new;
577     rt_err_t ret = RT_EOK;
578     time_t timestamp = (time_t)0;
579     struct tm now;
580 
581     if (alarm == RT_NULL)
582         return (RT_ERROR);
583     rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
584 
585     if (!(alarm->flag & RT_ALARM_STATE_START))
586     {
587         if (alarm_setup(alarm, &alarm->wktime) != RT_EOK)
588         {
589             ret = -RT_ERROR;
590             goto _exit;
591         }
592 
593         /* get time of now */
594         get_timestamp(&timestamp);
595 #ifdef RT_ALARM_USING_LOCAL_TIME
596         localtime_r(&timestamp, &now);
597 #else
598         gmtime_r(&timestamp, &now);
599 #endif
600 
601         alarm->flag |= RT_ALARM_STATE_START;
602 
603         /* set alarm */
604         if (_container.current == RT_NULL)
605         {
606             ret = alarm_set(alarm);
607         }
608         else
609         {
610             sec_now = alarm_mkdaysec(&now);
611             sec_old = alarm_mkdaysec(&_container.current->wktime);
612             sec_new = alarm_mkdaysec(&alarm->wktime);
613 
614             if ((sec_new < sec_old) && (sec_new > sec_now))
615             {
616                 ret = alarm_set(alarm);
617             }
618             else if ((sec_new > sec_now) && (sec_old < sec_now))
619             {
620                 ret = alarm_set(alarm);
621             }
622             else if ((sec_new < sec_old) && (sec_old < sec_now))
623             {
624                 ret = alarm_set(alarm);
625             }
626             else
627             {
628                 ret = RT_EOK;
629                 goto _exit;
630             }
631         }
632 
633         if (ret == RT_EOK)
634         {
635             _container.current = alarm;
636         }
637     }
638 
639 _exit:
640     rt_mutex_release(&_container.mutex);
641 
642     return (ret);
643 }
644 
645 /** \brief stop an alarm
646  *
647  * \param alarm pointer to alarm
648  * \return RT_EOK
649  */
rt_alarm_stop(rt_alarm_t alarm)650 rt_err_t rt_alarm_stop(rt_alarm_t alarm)
651 {
652     rt_err_t ret = RT_EOK;
653 
654     if (alarm == RT_NULL)
655         return (RT_ERROR);
656     rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
657     if (!(alarm->flag & RT_ALARM_STATE_START))
658         goto _exit;
659     /* stop alarm */
660     alarm->flag &= ~RT_ALARM_STATE_START;
661 
662     if (_container.current == alarm)
663     {
664         ret = alarm_set(alarm);
665         _container.current = RT_NULL;
666     }
667 
668     if (ret == RT_EOK)
669         alarm_update(0);
670 
671 _exit:
672     rt_mutex_release(&_container.mutex);
673 
674     return (ret);
675 }
676 
677 /** \brief delete an alarm
678  *
679  * \param alarm pointer to alarm
680  * \return RT_EOK
681  */
rt_alarm_delete(rt_alarm_t alarm)682 rt_err_t rt_alarm_delete(rt_alarm_t alarm)
683 {
684     rt_err_t ret = RT_EOK;
685 
686     if (alarm == RT_NULL)
687         return -RT_ERROR;
688     rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
689     /* stop the alarm */
690     alarm->flag &= ~RT_ALARM_STATE_START;
691     if (_container.current == alarm)
692     {
693         ret = alarm_set(alarm);
694         _container.current = RT_NULL;
695         /* set new alarm if necessary */
696         alarm_update(0);
697     }
698     rt_list_remove(&alarm->list);
699     rt_free(alarm);
700 
701     rt_mutex_release(&_container.mutex);
702 
703     return (ret);
704 }
705 
706 /** \brief create an alarm
707  *
708  * \param flag set alarm mode e.g: RT_ALARM_DAILY
709  * \param setup pointer to setup infomation
710  */
rt_alarm_create(rt_alarm_callback_t callback,struct rt_alarm_setup * setup)711 rt_alarm_t rt_alarm_create(rt_alarm_callback_t callback, struct rt_alarm_setup *setup)
712 {
713     struct rt_alarm *alarm;
714 
715     if (setup == RT_NULL)
716         return (RT_NULL);
717 
718     alarm = rt_malloc(sizeof(struct rt_alarm));
719     if (alarm == RT_NULL)
720         return (RT_NULL);
721 
722     rt_list_init(&alarm->list);
723 
724     alarm->wktime = setup->wktime;
725     alarm->flag = setup->flag & 0xFF00;
726     alarm->callback = callback;
727     rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
728     rt_list_insert_after(&_container.head, &alarm->list);
729     rt_mutex_release(&_container.mutex);
730 
731     return (alarm);
732 }
733 
734 /** \brief rtc alarm service thread entry
735  *
736  */
rt_alarmsvc_thread_init(void * param)737 static void rt_alarmsvc_thread_init(void *param)
738 {
739     rt_uint32_t recv;
740 
741     _container.current = RT_NULL;
742 
743     while (1)
744     {
745         if (rt_event_recv(&_container.event, 0xFFFF,
746                           RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
747                           RT_WAITING_FOREVER, &recv) == RT_EOK)
748         {
749             alarm_update(recv);
750         }
751     }
752 }
753 
754 struct _alarm_flag
755 {
756     const char* name;
757     rt_uint32_t flag;
758 };
759 
760 static const struct _alarm_flag _alarm_flag_tbl[] =
761 {
762     {"N",        0xffff}, /* none */
763     {"O",       RT_ALARM_ONESHOT}, /* only alarm once */
764     {"D",       RT_ALARM_DAILY}, /* alarm everyday */
765     {"W",       RT_ALARM_WEEKLY}, /* alarm weekly at Monday or Friday etc. */
766     {"Mo",      RT_ALARM_MONTHLY}, /* alarm monthly at someday */
767     {"Y",       RT_ALARM_YAERLY}, /* alarm yearly at a certain date */
768     {"H",       RT_ALARM_HOUR}, /* alarm each hour at a certain min:second */
769     {"M",       RT_ALARM_MINUTE}, /* alarm each minute at a certain second */
770     {"S",       RT_ALARM_SECOND}, /* alarm each second */
771 };
772 
773 static rt_uint8_t _alarm_flag_tbl_size = sizeof(_alarm_flag_tbl) / sizeof(_alarm_flag_tbl[0]);
774 
get_alarm_flag_index(rt_uint32_t alarm_flag)775 static rt_uint8_t get_alarm_flag_index(rt_uint32_t alarm_flag)
776 {
777     for (rt_uint8_t index = 0; index < _alarm_flag_tbl_size; index++)
778     {
779         alarm_flag &= 0xff00;
780         if (alarm_flag == _alarm_flag_tbl[index].flag)
781         {
782             return index;
783         }
784     }
785 
786     return 0;
787 }
788 
rt_alarm_dump(void)789 void rt_alarm_dump(void)
790 {
791     rt_list_t *next;
792     rt_alarm_t alarm;
793     int32_t tz_offset_sec = 0;
794     uint32_t abs_tz_offset_sec = 0U;
795 #ifdef RT_ALARM_USING_LOCAL_TIME
796 #if defined(RT_LIBC_USING_LIGHT_TZ_DST)
797     tz_offset_sec = rt_tz_get();
798 #endif /* RT_LIBC_USING_LIGHT_TZ_DST */
799     abs_tz_offset_sec = tz_offset_sec > 0 ? tz_offset_sec : -tz_offset_sec;
800 #endif
801     rt_kprintf("| hh:mm:ss | week | flag | en | timezone     |\n");
802     rt_kprintf("+----------+------+------+----+--------------+\n");
803     for (next = _container.head.next; next != &_container.head; next = next->next)
804     {
805         alarm = rt_list_entry(next, struct rt_alarm, list);
806         rt_uint8_t flag_index = get_alarm_flag_index(alarm->flag);
807         rt_kprintf("| %02d:%02d:%02d |  %2d  |  %2s  | %2d | UTC%c%02d:%02d:%02d |\n",
808             alarm->wktime.tm_hour, alarm->wktime.tm_min, alarm->wktime.tm_sec,
809             alarm->wktime.tm_wday, _alarm_flag_tbl[flag_index].name, alarm->flag & RT_ALARM_STATE_START,
810             tz_offset_sec > 0 ? '+' : '-', abs_tz_offset_sec / 3600U, abs_tz_offset_sec % 3600U / 60U, abs_tz_offset_sec % 3600U % 60U);
811     }
812     rt_kprintf("+----------+------+------+----+--------------+\n");
813 }
814 
815 MSH_CMD_EXPORT_ALIAS(rt_alarm_dump, list_alarm, list alarm info);
816 
817 /** \brief initialize alarm service system
818  *
819  * \param none
820  * \return none
821  */
rt_alarm_system_init(void)822 int rt_alarm_system_init(void)
823 {
824     rt_thread_t tid;
825 
826     rt_list_init(&_container.head);
827     rt_event_init(&_container.event, "alarmsvc", RT_IPC_FLAG_FIFO);
828     rt_mutex_init(&_container.mutex, "alarmsvc", RT_IPC_FLAG_PRIO);
829 
830     tid = rt_thread_create("alarmsvc",
831                            rt_alarmsvc_thread_init, RT_NULL,
832                            RT_ALARM_STACK_SIZE,
833                            RT_ALARM_PRIORITY,
834                            RT_ALARM_TIMESLICE);
835     if (tid != RT_NULL)
836         rt_thread_startup(tid);
837 
838     return 0;
839 }
840 
841 INIT_PREV_EXPORT(rt_alarm_system_init);
842 #endif
843