dev_alarm.c 23 KB

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