timer_settime.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /****************************************************************************
  2. * sched/timer/timer_settime.c
  3. *
  4. * Copyright (C) 2007-2010, 2013-2016, 2018 Gregory Nutt. All rights
  5. * reserved.
  6. * Author: Gregory Nutt <gnutt@nuttx.org>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. * 3. Neither the name NuttX nor the names of its contributors may be
  19. * used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  29. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  30. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. ****************************************************************************/
  36. /****************************************************************************
  37. * Included Files
  38. ****************************************************************************/
  39. #include <nuttx/config.h>
  40. #include <stdint.h>
  41. #include <time.h>
  42. #include <string.h>
  43. #include <errno.h>
  44. #include <nuttx/irq.h>
  45. #include "clock/clock.h"
  46. #include "timer/timer.h"
  47. #ifndef CONFIG_DISABLE_POSIX_TIMERS
  48. /****************************************************************************
  49. * Private Function Prototypes
  50. ****************************************************************************/
  51. static inline void timer_signotify(FAR struct posix_timer_s *timer);
  52. static inline void timer_restart(FAR struct posix_timer_s *timer,
  53. wdparm_t itimer);
  54. static void timer_timeout(int argc, wdparm_t itimer);
  55. /****************************************************************************
  56. * Private Functions
  57. ****************************************************************************/
  58. /****************************************************************************
  59. * Name: timer_signotify
  60. *
  61. * Description:
  62. * This function basically re-implements nxsig_queue() so that the si_code
  63. * can be correctly set to SI_TIMER
  64. *
  65. * Input Parameters:
  66. * timer - A reference to the POSIX timer that just timed out
  67. *
  68. * Returned Value:
  69. * None
  70. *
  71. * Assumptions:
  72. * This function executes in the context of the watchod timer interrupt.
  73. *
  74. ****************************************************************************/
  75. static inline void timer_signotify(FAR struct posix_timer_s *timer)
  76. {
  77. DEBUGVERIFY(nxsig_notification(timer->pt_owner, &timer->pt_event,
  78. SI_TIMER, &timer->pt_work));
  79. }
  80. /****************************************************************************
  81. * Name: timer_restart
  82. *
  83. * Description:
  84. * If a periodic timer has been selected, then restart the watchdog.
  85. *
  86. * Input Parameters:
  87. * timer - A reference to the POSIX timer that just timed out
  88. *
  89. * Returned Value:
  90. * None
  91. *
  92. * Assumptions:
  93. * This function executes in the context of the watchdog timer interrupt.
  94. *
  95. ****************************************************************************/
  96. static inline void timer_restart(FAR struct posix_timer_s *timer,
  97. wdparm_t itimer)
  98. {
  99. /* If this is a repetitive timer, then restart the watchdog */
  100. if (timer->pt_delay)
  101. {
  102. timer->pt_last = timer->pt_delay;
  103. (void)wd_start(timer->pt_wdog, timer->pt_delay,
  104. (wdentry_t)timer_timeout, 1, itimer);
  105. }
  106. }
  107. /****************************************************************************
  108. * Name: timer_timeout
  109. *
  110. * Description:
  111. * This function is called if the timeout elapses before the condition is
  112. * signaled.
  113. *
  114. * Input Parameters:
  115. * argc - the number of arguments (should be 1)
  116. * itimer - A reference to the POSIX timer that just timed out
  117. * signo - The signal to use to wake up the task
  118. *
  119. * Returned Value:
  120. * None
  121. *
  122. * Assumptions:
  123. * This function executes in the context of the watchod timer interrupt.
  124. *
  125. ****************************************************************************/
  126. static void timer_timeout(int argc, wdparm_t itimer)
  127. {
  128. #ifndef CONFIG_CAN_PASS_STRUCTS
  129. /* On many small machines, pointers are encoded and cannot be simply cast
  130. * from wdparm_t to struct tcb_s *. The following union works around this
  131. * (see wdogparm_t).
  132. */
  133. union
  134. {
  135. FAR struct posix_timer_s *timer;
  136. wdparm_t itimer;
  137. } u;
  138. u.itimer = itimer;
  139. /* Send the specified signal to the specified task. Increment the
  140. * reference count on the timer first so that will not be deleted until
  141. * after the signal handler returns.
  142. */
  143. u.timer->pt_crefs++;
  144. timer_signotify(u.timer);
  145. /* Release the reference. timer_release will return nonzero if the timer
  146. * was not deleted.
  147. */
  148. if (timer_release(u.timer))
  149. {
  150. /* If this is a repetitive timer, then restart the watchdog */
  151. timer_restart(u.timer, itimer);
  152. }
  153. #else
  154. FAR struct posix_timer_s *timer = (FAR struct posix_timer_s *)itimer;
  155. /* Send the specified signal to the specified task. Increment the
  156. * reference count on the timer first so that will not be deleted until
  157. * after the signal handler returns.
  158. */
  159. timer->pt_crefs++;
  160. timer_signotify(timer);
  161. /* Release the reference. timer_release will return nonzero if the timer
  162. * was not deleted.
  163. */
  164. if (timer_release(timer))
  165. {
  166. /* If this is a repetitive timer, the restart the watchdog */
  167. timer_restart(timer, itimer);
  168. }
  169. #endif
  170. }
  171. /****************************************************************************
  172. * Public Functions
  173. ****************************************************************************/
  174. /****************************************************************************
  175. * Name: timer_settime
  176. *
  177. * Description:
  178. * The timer_settime() function sets the time until the next expiration of
  179. * the timer specified by timerid from the it_value member of the value
  180. * argument and arm the timer if the it_value member of value is non-zero.
  181. * If the specified timer was already armed when timer_settime() is
  182. * called, this call will reset the time until next expiration to the
  183. * value specified. If the it_value member of value is zero, the timer
  184. * will be disarmed. The effect of disarming or resetting a timer with
  185. * pending expiration notifications is unspecified.
  186. *
  187. * If the flag TIMER_ABSTIME is not set in the argument flags,
  188. * timer_settime() will behave as if the time until next expiration is set
  189. * to be equal to the interval specified by the it_value member of value.
  190. * That is, the timer will expire in it_value nanoseconds from when the
  191. * call is made. If the flag TIMER_ABSTIME is set in the argument flags,
  192. * timer_settime() will behave as if the time until next expiration is set
  193. * to be equal to the difference between the absolute time specified by
  194. * the it_value member of value and the current value of the clock
  195. * associated with timerid. That is, the timer will expire when the clock
  196. * reaches the value specified by the it_value member of value. If the
  197. * specified time has already passed, the function will succeed and the
  198. * expiration notification will be made.
  199. *
  200. * The reload value of the timer will be set to the value specified by the
  201. * it_interval member of value. When a timer is armed with a non-zero
  202. * it_interval, a periodic (or repetitive) timer is specified.
  203. *
  204. * Time values that are between two consecutive non-negative integer
  205. * multiples of the resolution of the specified timer will be rounded up
  206. * to the larger multiple of the resolution. Quantization error will not
  207. * cause the timer to expire earlier than the rounded time value.
  208. *
  209. * If the argument ovalue is not NULL, the timer_settime() function will
  210. * store, in the location referenced by ovalue, a value representing the
  211. * previous amount of time before the timer would have expired, or zero if
  212. * the timer was disarmed, together with the previous timer reload value.
  213. * Timers will not expire before their scheduled time.
  214. *
  215. * Input Parameters:
  216. * timerid - The pre-thread timer, previously created by the call to
  217. * timer_create(), to be be set.
  218. * flags - Specifies characteristics of the timer (see above)
  219. * value - Specifies the timer value to set
  220. * ovalue - A location in which to return the time remaining from the
  221. * previous timer setting. (ignored)
  222. *
  223. * Returned Value:
  224. * If the timer_settime() succeeds, a value of 0 (OK) will be returned.
  225. * If an error occurs, the value -1 (ERROR) will be returned, and errno set
  226. * to indicate the error.
  227. *
  228. * EINVAL - The timerid argument does not correspond to an ID returned by
  229. * timer_create() but not yet deleted by timer_delete().
  230. * EINVAL - A value structure specified a nanosecond value less than zero or
  231. * greater than or equal to 1000 million, and the it_value member of that
  232. * structure did not specify zero seconds and nanoseconds.
  233. *
  234. * Assumptions:
  235. *
  236. ****************************************************************************/
  237. int timer_settime(timer_t timerid, int flags,
  238. FAR const struct itimerspec *value,
  239. FAR struct itimerspec *ovalue)
  240. {
  241. FAR struct posix_timer_s *timer = (FAR struct posix_timer_s *)timerid;
  242. irqstate_t intflags;
  243. sclock_t delay;
  244. int ret = OK;
  245. /* Some sanity checks */
  246. if (!timer || !value)
  247. {
  248. set_errno(EINVAL);
  249. return ERROR;
  250. }
  251. /* Disarm the timer (in case the timer was already armed when timer_settime()
  252. * is called).
  253. */
  254. (void)wd_cancel(timer->pt_wdog);
  255. /* Cancel any pending notification */
  256. nxsig_cancel_notification(&timer->pt_work);
  257. /* If the it_value member of value is zero, the timer will not be re-armed */
  258. if (value->it_value.tv_sec <= 0 && value->it_value.tv_nsec <= 0)
  259. {
  260. return OK;
  261. }
  262. /* Setup up any repetitive timer */
  263. if (value->it_interval.tv_sec > 0 || value->it_interval.tv_nsec > 0)
  264. {
  265. (void)clock_time2ticks(&value->it_interval, &delay);
  266. /* REVISIT: Should pt_delay be sclock_t? */
  267. timer->pt_delay = (int)delay;
  268. }
  269. else
  270. {
  271. timer->pt_delay = 0;
  272. }
  273. /* We need to disable timer interrupts through the following section so
  274. * that the system timer is stable.
  275. */
  276. intflags = enter_critical_section();
  277. /* Check if abstime is selected */
  278. if ((flags & TIMER_ABSTIME) != 0)
  279. {
  280. /* Calculate a delay corresponding to the absolute time in 'value'.
  281. * NOTE: We have internal knowledge the clock_abstime2ticks only
  282. * returns an error if clockid != CLOCK_REALTIME.
  283. */
  284. (void)clock_abstime2ticks(CLOCK_REALTIME, &value->it_value, &delay);
  285. }
  286. else
  287. {
  288. /* Calculate a delay assuming that 'value' holds the relative time
  289. * to wait. We have internal knowledge that clock_time2ticks always
  290. * returns success.
  291. */
  292. (void)clock_time2ticks(&value->it_value, &delay);
  293. }
  294. /* If the time is in the past or now, then set up the next interval
  295. * instead (assuming a repetitive timer).
  296. */
  297. if (delay <= 0)
  298. {
  299. delay = timer->pt_delay;
  300. }
  301. /* Then start the watchdog */
  302. if (delay > 0)
  303. {
  304. /* REVISIT: Should pt_last be sclock_t? Should wd_start delay be
  305. * sclock_t?
  306. */
  307. timer->pt_last = delay;
  308. ret = wd_start(timer->pt_wdog, delay, (wdentry_t)timer_timeout,
  309. 1, (uint32_t)((wdparm_t)timer));
  310. if (ret < 0)
  311. {
  312. set_errno(-ret);
  313. ret = ERROR;
  314. }
  315. else
  316. {
  317. ret = OK;
  318. }
  319. }
  320. leave_critical_section(intflags);
  321. return ret;
  322. }
  323. #endif /* CONFIG_DISABLE_POSIX_TIMERS */