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(×tamp);
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(×tamp);
248 #ifdef RT_ALARM_USING_LOCAL_TIME
249 localtime_r(×tamp, &now);
250 #else
251 gmtime_r(×tamp, &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(×tamp);
263 #ifdef RT_ALARM_USING_LOCAL_TIME
264 localtime_r(×tamp, &now);
265 #else
266 gmtime_r(×tamp, &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(×tamp);
376 #ifdef RT_ALARM_USING_LOCAL_TIME
377 localtime_r(×tamp, &now);
378 #else
379 gmtime_r(×tamp, &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(×tamp);
595 #ifdef RT_ALARM_USING_LOCAL_TIME
596 localtime_r(×tamp, &now);
597 #else
598 gmtime_r(×tamp, &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