1 /* 2 * Copyright (c) 2006-2018, 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 */ 11 12 #include <rtthread.h> 13 #include <rtdevice.h> 14 15 #define RT_RTC_YEARS_MAX 137 16 #define RT_ALARM_DELAY 2 17 #define RT_ALARM_STATE_INITED 0x02 18 #define RT_ALARM_STATE_START 0x01 19 #define RT_ALARM_STATE_STOP 0x00 20 21 #if (defined(RT_USING_RTC) && defined(RT_USING_ALARM)) 22 static struct rt_alarm_container _container; 23 24 rt_inline rt_uint32_t alarm_mkdaysec(struct tm *time) 25 { 26 rt_uint32_t sec; 27 28 sec = time->tm_sec; 29 sec += time->tm_min * 60; 30 sec += time->tm_hour * 3600; 31 32 return (sec); 33 } 34 35 static rt_err_t alarm_set(struct rt_alarm *alarm) 36 { 37 rt_device_t device; 38 struct rt_rtc_wkalarm wkalarm; 39 rt_err_t ret; 40 41 device = rt_device_find("rtc"); 42 if (device == RT_NULL) 43 { 44 return (RT_ERROR); 45 } 46 if (alarm->flag & RT_ALARM_STATE_START) 47 wkalarm.enable = RT_TRUE; 48 else 49 wkalarm.enable = RT_FALSE; 50 51 wkalarm.tm_sec = alarm->wktime.tm_sec; 52 wkalarm.tm_min = alarm->wktime.tm_min; 53 wkalarm.tm_hour = alarm->wktime.tm_hour; 54 55 ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_SET_ALARM, &wkalarm); 56 if ((ret == RT_EOK) && wkalarm.enable) 57 { 58 ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_GET_ALARM, &wkalarm); 59 if (ret == RT_EOK) 60 { 61 /* 62 some RTC device like RX8025,it's alarms precision is 1 minute. 63 in this case,low level RTC driver should set wkalarm->tm_sec to 0. 64 */ 65 alarm->wktime.tm_sec = wkalarm.tm_sec; 66 alarm->wktime.tm_min = wkalarm.tm_min; 67 alarm->wktime.tm_hour = wkalarm.tm_hour; 68 } 69 } 70 71 return (ret); 72 } 73 74 static void alarm_wakeup(struct rt_alarm *alarm, struct tm *now) 75 { 76 rt_uint32_t sec_alarm, sec_now; 77 rt_bool_t wakeup = RT_FALSE; 78 time_t timestamp; 79 80 sec_alarm = alarm_mkdaysec(&alarm->wktime); 81 sec_now = alarm_mkdaysec(now); 82 83 if (alarm->flag & RT_ALARM_STATE_START) 84 { 85 switch (alarm->flag & 0xFF00) 86 { 87 case RT_ALARM_ONESHOT: 88 { 89 sec_alarm = mktime(&alarm->wktime); 90 sec_now = mktime(now); 91 if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm)) 92 { 93 /* stop alarm */ 94 alarm->flag &= ~RT_ALARM_STATE_START; 95 alarm_set(alarm); 96 wakeup = RT_TRUE; 97 } 98 } 99 break; 100 case RT_ALARM_DAILY: 101 { 102 if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm)) 103 wakeup = RT_TRUE; 104 } 105 break; 106 case RT_ALARM_WEEKLY: 107 { 108 /* alarm at wday */ 109 sec_alarm += alarm->wktime.tm_wday * 24 * 3600; 110 sec_now += now->tm_wday * 24 * 3600; 111 112 if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm)) 113 wakeup = RT_TRUE; 114 } 115 break; 116 case RT_ALARM_MONTHLY: 117 { 118 /* monthly someday generate alarm signals */ 119 if (alarm->wktime.tm_mday == now->tm_mday) 120 { 121 if ((sec_now - sec_alarm) <= RT_ALARM_DELAY) 122 wakeup = RT_TRUE; 123 } 124 } 125 break; 126 case RT_ALARM_YAERLY: 127 { 128 if ((alarm->wktime.tm_mday == now->tm_mday) && \ 129 (alarm->wktime.tm_mon == now->tm_mon)) 130 { 131 if ((sec_now - sec_alarm) <= RT_ALARM_DELAY) 132 wakeup = RT_TRUE; 133 } 134 } 135 break; 136 } 137 138 if ((wakeup == RT_TRUE) && (alarm->callback != RT_NULL)) 139 { 140 timestamp = time(RT_NULL); 141 alarm->callback(alarm, timestamp); 142 } 143 } 144 } 145 146 static void alarm_update(rt_uint32_t event) 147 { 148 struct rt_alarm *alm_prev = RT_NULL, *alm_next = RT_NULL; 149 struct rt_alarm *alarm; 150 rt_int32_t sec_now, sec_alarm, sec_tmp; 151 rt_int32_t sec_next = 24 * 3600, sec_prev = 0; 152 time_t timestamp; 153 struct tm now; 154 rt_list_t *next; 155 156 rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER); 157 if (!rt_list_isempty(&_container.head)) 158 { 159 /* get time of now */ 160 timestamp = time(RT_NULL); 161 localtime_r(×tamp, &now); 162 163 for (next = _container.head.next; next != &_container.head; next = next->next) 164 { 165 alarm = rt_list_entry(next, struct rt_alarm, list); 166 /* check the overtime alarm */ 167 alarm_wakeup(alarm, &now); 168 } 169 170 timestamp = time(RT_NULL); 171 localtime_r(×tamp, &now); 172 sec_now = alarm_mkdaysec(&now); 173 174 for (next = _container.head.next; next != &_container.head; next = next->next) 175 { 176 alarm = rt_list_entry(next, struct rt_alarm, list); 177 /* calculate seconds from 00:00:00 */ 178 sec_alarm = alarm_mkdaysec(&alarm->wktime); 179 180 if ((alarm->flag & RT_ALARM_STATE_START) && (alarm != _container.current)) 181 { 182 sec_tmp = sec_alarm - sec_now; 183 if (sec_tmp > 0) 184 { 185 /* find alarm after now(now to 23:59:59) and the most recent */ 186 if (sec_tmp < sec_next) 187 { 188 sec_next = sec_tmp; 189 alm_next = alarm; 190 } 191 } 192 else 193 { 194 /* find alarm before now(00:00:00 to now) and furthest from now */ 195 if (sec_tmp < sec_prev) 196 { 197 sec_prev = sec_tmp; 198 alm_prev = alarm; 199 } 200 } 201 } 202 } 203 /* enable the alarm after now first */ 204 if (sec_next < 24 * 3600) 205 { 206 if (alarm_set(alm_next) == RT_EOK) 207 _container.current = alm_next; 208 } 209 else if (sec_prev < 0) 210 { 211 /* enable the alarm before now */ 212 if (alarm_set(alm_prev) == RT_EOK) 213 _container.current = alm_prev; 214 } 215 } 216 rt_mutex_release(&_container.mutex); 217 } 218 219 static rt_uint32_t days_of_year_month(int tm_year, int tm_mon) 220 { 221 rt_uint32_t ret, year; 222 223 year = tm_year + 1900; 224 if (tm_mon == 1) 225 { 226 ret = 28 + ((!(year % 4) && (year % 100)) || !(year % 400)); 227 } 228 else if (((tm_mon <= 6) && (tm_mon % 2 == 0)) || ((tm_mon > 6) && (tm_mon % 2 == 1))) 229 { 230 ret = 31; 231 } 232 else 233 { 234 ret = 30; 235 } 236 237 return (ret); 238 } 239 240 static rt_bool_t is_valid_date(struct tm *date) 241 { 242 if ((date->tm_year < 0) || (date->tm_year > RT_RTC_YEARS_MAX)) 243 { 244 return (RT_FALSE); 245 } 246 247 if ((date->tm_mon < 0) || (date->tm_mon > 11)) 248 { 249 return (RT_FALSE); 250 } 251 252 if ((date->tm_mday < 1) || \ 253 (date->tm_mday > days_of_year_month(date->tm_year, date->tm_mon))) 254 { 255 return (RT_FALSE); 256 } 257 258 return (RT_TRUE); 259 } 260 261 static rt_err_t alarm_setup(rt_alarm_t alarm, struct tm *wktime) 262 { 263 rt_err_t ret = RT_ERROR; 264 time_t timestamp; 265 struct tm *setup, now; 266 267 setup = &alarm->wktime; 268 *setup = *wktime; 269 timestamp = time(RT_NULL); 270 localtime_r(×tamp, &now); 271 272 /* if these are a "don't care" value,we set them to now*/ 273 if ((setup->tm_sec > 59) || (setup->tm_sec < 0)) 274 setup->tm_sec = now.tm_sec; 275 if ((setup->tm_min > 59) || (setup->tm_min < 0)) 276 setup->tm_min = now.tm_min; 277 if ((setup->tm_hour > 23) || (setup->tm_hour < 0)) 278 setup->tm_hour = now.tm_hour; 279 280 switch (alarm->flag & 0xFF00) 281 { 282 case RT_ALARM_DAILY: 283 { 284 /* do nothing but needed */ 285 } 286 break; 287 case RT_ALARM_ONESHOT: 288 { 289 /* if these are "don't care" value we set them to now */ 290 if (setup->tm_year == RT_ALARM_TM_NOW) 291 setup->tm_year = now.tm_year; 292 if (setup->tm_mon == RT_ALARM_TM_NOW) 293 setup->tm_mon = now.tm_mon; 294 if (setup->tm_mday == RT_ALARM_TM_NOW) 295 setup->tm_mday = now.tm_mday; 296 /* make sure the setup is valid */ 297 if (!is_valid_date(setup)) 298 goto _exit; 299 } 300 break; 301 case RT_ALARM_WEEKLY: 302 { 303 /* if tm_wday is a "don't care" value we set it to now */ 304 if ((setup->tm_wday < 0) || (setup->tm_wday > 6)) 305 setup->tm_wday = now.tm_wday; 306 } 307 break; 308 case RT_ALARM_MONTHLY: 309 { 310 /* if tm_mday is a "don't care" value we set it to now */ 311 if ((setup->tm_mday < 1) || (setup->tm_mday > 31)) 312 setup->tm_mday = now.tm_mday; 313 } 314 break; 315 case RT_ALARM_YAERLY: 316 { 317 /* if tm_mon is a "don't care" value we set it to now */ 318 if ((setup->tm_mon < 0) || (setup->tm_mon > 11)) 319 setup->tm_mon = now.tm_mon; 320 321 if (setup->tm_mon == 1) 322 { 323 /* tm_mon is February */ 324 325 /* tm_mday should be 1~29.otherwise,it's a "don't care" value */ 326 if ((setup->tm_mday < 1) || (setup->tm_mday > 29)) 327 setup->tm_mday = now.tm_mday; 328 } 329 else if (((setup->tm_mon <= 6) && (setup->tm_mon % 2 == 0)) || \ 330 ((setup->tm_mon > 6) && (setup->tm_mon % 2 == 1))) 331 { 332 /* Jan,Mar,May,Jul,Aug,Oct,Dec */ 333 334 /* tm_mday should be 1~31.otherwise,it's a "don't care" value */ 335 if ((setup->tm_mday < 1) || (setup->tm_mday > 31)) 336 setup->tm_mday = now.tm_mday; 337 } 338 else 339 { 340 /* tm_mday should be 1~30.otherwise,it's a "don't care" value */ 341 if ((setup->tm_mday < 1) || (setup->tm_mday > 30)) 342 setup->tm_mday = now.tm_mday; 343 } 344 } 345 break; 346 default: 347 { 348 goto _exit; 349 } 350 } 351 352 if ((setup->tm_hour == 23) && (setup->tm_min == 59) && (setup->tm_sec == 59)) 353 { 354 /* 355 for insurance purposes, we will generate an alarm 356 signal two seconds ahead of. 357 */ 358 setup->tm_sec = 60 - RT_ALARM_DELAY; 359 } 360 /* set initialized state */ 361 alarm->flag |= RT_ALARM_STATE_INITED; 362 ret = RT_EOK; 363 364 _exit: 365 366 return (ret); 367 } 368 369 /** \brief send a rtc alarm event 370 * 371 * \param dev pointer to RTC device(currently unused,you can ignore it) 372 * \param event RTC event(currently unused) 373 * \return none 374 */ 375 void rt_alarm_update(rt_device_t dev, rt_uint32_t event) 376 { 377 rt_event_send(&_container.event, 1); 378 } 379 380 /** \brief modify the alarm setup 381 * 382 * \param alarm pointer to alarm 383 * \param cmd control command 384 * \param arg argument 385 */ 386 rt_err_t rt_alarm_control(rt_alarm_t alarm, int cmd, void *arg) 387 { 388 rt_err_t ret = RT_ERROR; 389 390 RT_ASSERT(alarm != RT_NULL); 391 392 rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER); 393 switch (cmd) 394 { 395 case RT_ALARM_CTRL_MODIFY: 396 { 397 struct rt_alarm_setup *setup; 398 399 RT_ASSERT(arg != RT_NULL); 400 setup = arg; 401 rt_alarm_stop(alarm); 402 alarm->flag = setup->flag & 0xFF00; 403 alarm->wktime = setup->wktime; 404 ret = alarm_setup(alarm, &alarm->wktime); 405 } 406 break; 407 } 408 409 rt_mutex_release(&_container.mutex); 410 411 return (ret); 412 } 413 414 /** \brief start an alarm 415 * 416 * \param alarm pointer to alarm 417 * \return RT_EOK 418 */ 419 rt_err_t rt_alarm_start(rt_alarm_t alarm) 420 { 421 rt_int32_t sec_now, sec_old, sec_new; 422 rt_err_t ret = RT_ERROR; 423 time_t timestamp; 424 struct tm now; 425 426 if (alarm == RT_NULL) 427 return (ret); 428 rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER); 429 if (!(alarm->flag & RT_ALARM_STATE_INITED)) 430 { 431 if (alarm_setup(alarm, &alarm->wktime) != RT_EOK) 432 goto _exit; 433 } 434 if ((alarm->flag & 0x01) == RT_ALARM_STATE_STOP) 435 { 436 timestamp = time(RT_NULL); 437 localtime_r(×tamp, &now); 438 439 alarm->flag |= RT_ALARM_STATE_START; 440 /* set alarm */ 441 if (_container.current == RT_NULL) 442 { 443 ret = alarm_set(alarm); 444 } 445 else 446 { 447 sec_now = alarm_mkdaysec(&now); 448 sec_old = alarm_mkdaysec(&_container.current->wktime); 449 sec_new = alarm_mkdaysec(&alarm->wktime); 450 451 if ((sec_new < sec_old) && (sec_new > sec_now)) 452 { 453 ret = alarm_set(alarm); 454 } 455 else if ((sec_new > sec_now) && (sec_old < sec_now)) 456 { 457 ret = alarm_set(alarm); 458 } 459 else if ((sec_new < sec_old) && (sec_old < sec_now)) 460 { 461 ret = alarm_set(alarm); 462 } 463 else 464 { 465 ret = RT_EOK; 466 goto _exit; 467 } 468 } 469 470 if (ret == RT_EOK) 471 { 472 _container.current = alarm; 473 } 474 } 475 476 _exit: 477 rt_mutex_release(&_container.mutex); 478 479 return (ret); 480 } 481 482 /** \brief stop an alarm 483 * 484 * \param alarm pointer to alarm 485 * \return RT_EOK 486 */ 487 rt_err_t rt_alarm_stop(rt_alarm_t alarm) 488 { 489 rt_err_t ret = RT_ERROR; 490 491 if (alarm == RT_NULL) 492 return (ret); 493 rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER); 494 if (!(alarm->flag & RT_ALARM_STATE_START)) 495 goto _exit; 496 /* stop alarm */ 497 alarm->flag &= ~RT_ALARM_STATE_START; 498 499 if (_container.current == alarm) 500 { 501 ret = alarm_set(alarm); 502 _container.current = RT_NULL; 503 } 504 505 if (ret == RT_EOK) 506 alarm_update(0); 507 508 _exit: 509 rt_mutex_release(&_container.mutex); 510 511 return (ret); 512 } 513 514 /** \brief delete an alarm 515 * 516 * \param alarm pointer to alarm 517 * \return RT_EOK 518 */ 519 rt_err_t rt_alarm_delete(rt_alarm_t alarm) 520 { 521 rt_err_t ret = RT_ERROR; 522 523 if (alarm == RT_NULL) 524 return (ret); 525 rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER); 526 /* stop the alarm */ 527 alarm->flag &= ~RT_ALARM_STATE_START; 528 if (_container.current == alarm) 529 { 530 ret = alarm_set(alarm); 531 _container.current = RT_NULL; 532 /* set new alarm if necessary */ 533 alarm_update(0); 534 } 535 rt_list_remove(&alarm->list); 536 rt_free(alarm); 537 538 rt_mutex_release(&_container.mutex); 539 540 return (ret); 541 } 542 543 /** \brief create an alarm 544 * 545 * \param flag set alarm mode e.g: RT_ALARM_DAILY 546 * \param setup pointer to setup infomation 547 */ 548 rt_alarm_t rt_alarm_create(rt_alarm_callback_t callback, struct rt_alarm_setup *setup) 549 { 550 struct rt_alarm *alarm; 551 552 if (setup == RT_NULL) 553 return (RT_NULL); 554 alarm = rt_malloc(sizeof(struct rt_alarm)); 555 if (alarm == RT_NULL) 556 return (RT_NULL); 557 rt_list_init(&alarm->list); 558 559 alarm->wktime = setup->wktime; 560 alarm->flag = setup->flag & 0xFF00; 561 alarm->callback = callback; 562 rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER); 563 rt_list_insert_after(&_container.head, &alarm->list); 564 rt_mutex_release(&_container.mutex); 565 566 return (alarm); 567 } 568 569 /** \brief rtc alarm service thread entry 570 * 571 */ 572 static void rt_alarmsvc_thread_init(void *param) 573 { 574 rt_uint32_t recv; 575 576 _container.current = RT_NULL; 577 578 while (1) 579 { 580 if (rt_event_recv(&_container.event, 0xFFFF, 581 RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 582 RT_WAITING_FOREVER, &recv) == RT_EOK) 583 { 584 alarm_update(recv); 585 } 586 } 587 } 588 589 590 /** \brief initialize alarm service system 591 * 592 * \param none 593 * \return none 594 */ 595 void rt_alarm_system_init(void) 596 { 597 rt_thread_t tid; 598 599 rt_list_init(&_container.head); 600 rt_event_init(&_container.event, "alarmsvc", RT_IPC_FLAG_FIFO); 601 rt_mutex_init(&_container.mutex, "alarmsvc", RT_IPC_FLAG_FIFO); 602 603 tid = rt_thread_create("alarmsvc", 604 rt_alarmsvc_thread_init, RT_NULL, 605 512, 8, 1); 606 if (tid != RT_NULL) 607 rt_thread_startup(tid); 608 } 609 #endif 610