Line data Source code
1 1 : /*
2 : * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 : * Copyright (c) 2025 Tenstorrent AI ULC
4 : *
5 : * SPDX-License-Identifier: Apache-2.0
6 : */
7 :
8 : /**
9 : * @file
10 : * @brief Utilities supporting operation on time data structures.
11 : *
12 : * POSIX defines gmtime() to convert from time_t to struct tm, but all
13 : * inverse transformations are non-standard or require access to time
14 : * zone information. timeutil_timegm() implements the functionality
15 : * of the GNU extension timegm() function, but changes the error value
16 : * as @c EOVERFLOW is not a standard C error identifier.
17 : *
18 : * timeutil_timegm64() is provided to support full precision
19 : * conversion on platforms where @c time_t is limited to 32 bits.
20 : */
21 :
22 : #ifndef ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_
23 : #define ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_
24 :
25 : #include <limits.h>
26 : #include <stdbool.h>
27 : #include <stddef.h>
28 : #include <stdint.h>
29 : #include <time.h>
30 :
31 : #include <zephyr/sys_clock.h>
32 : #include <zephyr/sys/__assert.h>
33 : #include <zephyr/sys/math_extras.h>
34 : #include <zephyr/sys/time_units.h>
35 : #include <zephyr/sys/util.h>
36 : #include <zephyr/toolchain.h>
37 : #include <zephyr/types.h>
38 :
39 : #ifdef __cplusplus
40 : extern "C" {
41 : #endif
42 :
43 : /* Maximum and minimum value TIME_T can hold */
44 0 : #define SYS_TIME_T_MAX ((((time_t)1 << (8 * sizeof(time_t) - 2)) - 1) * 2 + 1)
45 0 : #define SYS_TIME_T_MIN (-SYS_TIME_T_MAX - 1)
46 :
47 : /* Converts ticks to seconds, discarding any fractional seconds */
48 0 : #define SYS_TICKS_TO_SECS(ticks) \
49 : (((uint64_t)(ticks) >= (uint64_t)K_TICKS_FOREVER) ? SYS_TIME_T_MAX \
50 : : k_ticks_to_sec_floor64(ticks))
51 :
52 : /* Converts ticks to nanoseconds, modulo NSEC_PER_SEC */
53 0 : #define SYS_TICKS_TO_NSECS(ticks) \
54 : (((uint64_t)(ticks) >= (uint64_t)K_TICKS_FOREVER) \
55 : ? (NSEC_PER_SEC - 1) \
56 : : k_ticks_to_ns_floor64((uint64_t)(ticks) % CONFIG_SYS_CLOCK_TICKS_PER_SEC))
57 :
58 : /* Define a timespec */
59 0 : #define SYS_TIMESPEC(sec, nsec) \
60 : ((struct timespec){ \
61 : .tv_sec = (time_t)CLAMP((int64_t)(sec), SYS_TIME_T_MIN, SYS_TIME_T_MAX), \
62 : .tv_nsec = (long)(nsec), \
63 : })
64 :
65 : /* Initialize a struct timespec object from a tick count */
66 0 : #define SYS_TICKS_TO_TIMESPEC(ticks) SYS_TIMESPEC(SYS_TICKS_TO_SECS(ticks), \
67 : SYS_TICKS_TO_NSECS(ticks))
68 :
69 : /* The semantic equivalent of K_NO_WAIT but expressed as a timespec object*/
70 0 : #define SYS_TIMESPEC_NO_WAIT SYS_TICKS_TO_TIMESPEC(0)
71 :
72 : /* The semantic equivalent of K_TICK_MIN but expressed as a timespec object */
73 0 : #define SYS_TIMESPEC_MIN SYS_TICKS_TO_TIMESPEC(K_TICK_MIN)
74 :
75 : /* The semantic equivalent of K_TICK_MAX but expressed as a timespec object */
76 0 : #define SYS_TIMESPEC_MAX SYS_TICKS_TO_TIMESPEC(K_TICK_MAX)
77 :
78 : /* The semantic equivalent of K_FOREVER but expressed as a timespec object*/
79 0 : #define SYS_TIMESPEC_FOREVER SYS_TIMESPEC(SYS_TIME_T_MAX, NSEC_PER_SEC - 1)
80 :
81 : /**
82 : * @defgroup timeutil_apis Time Utility APIs
83 : * @ingroup utilities
84 : * @defgroup timeutil_repr_apis Time Representation APIs
85 : * @ingroup timeutil_apis
86 : * @{
87 : */
88 :
89 : /* Base Year value use in calculations in "timeutil_timegm64" API */
90 0 : #define TIME_UTILS_BASE_YEAR 1900
91 :
92 : /**
93 : * @brief Convert broken-down time to a POSIX epoch offset in seconds.
94 : *
95 : * @param tm pointer to broken down time.
96 : *
97 : * @return the corresponding time in the POSIX epoch time scale.
98 : *
99 : * @see http://man7.org/linux/man-pages/man3/timegm.3.html
100 : */
101 1 : int64_t timeutil_timegm64(const struct tm *tm);
102 :
103 : /**
104 : * @brief Convert broken-down time to a POSIX epoch offset in seconds.
105 : *
106 : * @param tm pointer to broken down time.
107 : *
108 : * @return the corresponding time in the POSIX epoch time scale. If
109 : * the time cannot be represented then @c (time_t)-1 is returned and
110 : * @c errno is set to @c ERANGE`.
111 : *
112 : * @see http://man7.org/linux/man-pages/man3/timegm.3.html
113 : */
114 1 : time_t timeutil_timegm(const struct tm *tm);
115 :
116 : /**
117 : * @}
118 : * @defgroup timeutil_sync_apis Time Synchronization APIs
119 : * @ingroup timeutil_apis
120 : * @{
121 : */
122 :
123 : /**
124 : * @brief Immutable state for synchronizing two clocks.
125 : *
126 : * Values required to convert durations between two time scales.
127 : *
128 : * @note The accuracy of the translation and calculated skew between sources
129 : * depends on the resolution of these frequencies. A reference frequency with
130 : * microsecond or nanosecond resolution would produce the most accurate
131 : * tracking when the local reference is the Zephyr tick counter. A reference
132 : * source like an RTC chip with 1 Hz resolution requires a much larger
133 : * interval between sampled instants to detect relative clock drift.
134 : */
135 1 : struct timeutil_sync_config {
136 : /** The nominal instance counter rate in Hz.
137 : *
138 : * This value is assumed to be precise, but may drift depending on
139 : * the reference clock source.
140 : *
141 : * The value must be positive.
142 : */
143 1 : uint32_t ref_Hz;
144 :
145 : /** The nominal local counter rate in Hz.
146 : *
147 : * This value is assumed to be inaccurate but reasonably stable. For
148 : * a local clock driven by a crystal oscillator an error of 25 ppm is
149 : * common; for an RC oscillator larger errors should be expected. The
150 : * timeutil_sync infrastructure can calculate the skew between the
151 : * local and reference clocks and apply it when converting between
152 : * time scales.
153 : *
154 : * The value must be positive.
155 : */
156 1 : uint32_t local_Hz;
157 : };
158 :
159 : /**
160 : * @brief Representation of an instant in two time scales.
161 : *
162 : * Capturing the same instant in two time scales provides a
163 : * registration point that can be used to convert between those time
164 : * scales.
165 : */
166 1 : struct timeutil_sync_instant {
167 : /** An instant in the reference time scale.
168 : *
169 : * This must never be zero in an initialized timeutil_sync_instant
170 : * object.
171 : */
172 1 : uint64_t ref;
173 :
174 : /** The corresponding instance in the local time scale.
175 : *
176 : * This may be zero in a valid timeutil_sync_instant object.
177 : */
178 1 : uint64_t local;
179 : };
180 :
181 : /**
182 : * @brief State required to convert instants between time scales.
183 : *
184 : * This state in conjunction with functions that manipulate it capture
185 : * the offset information necessary to convert between two timescales
186 : * along with information that corrects for skew due to inaccuracies
187 : * in clock rates.
188 : *
189 : * State objects should be zero-initialized before use.
190 : */
191 1 : struct timeutil_sync_state {
192 : /** Pointer to reference and local rate information. */
193 1 : const struct timeutil_sync_config *cfg;
194 :
195 : /** The base instant in both time scales. */
196 1 : struct timeutil_sync_instant base;
197 :
198 : /** The most recent instant in both time scales.
199 : *
200 : * This is captured here to provide data for skew calculation.
201 : */
202 1 : struct timeutil_sync_instant latest;
203 :
204 : /** The scale factor used to correct for clock skew.
205 : *
206 : * The nominal rate for the local counter is assumed to be
207 : * inaccurate but stable, i.e. it will generally be some
208 : * parts-per-million faster or slower than specified.
209 : *
210 : * A duration in observed local clock ticks must be multiplied by
211 : * this value to produce a duration in ticks of a clock operating at
212 : * the nominal local rate.
213 : *
214 : * A zero value indicates that the skew has not been initialized.
215 : * If the value is zero when #base is initialized the skew will be
216 : * set to 1. Otherwise the skew is assigned through
217 : * timeutil_sync_state_set_skew().
218 : */
219 1 : float skew;
220 : };
221 :
222 : /**
223 : * @brief Record a new instant in the time synchronization state.
224 : *
225 : * Note that this updates only the latest persisted instant. The skew
226 : * is not adjusted automatically.
227 : *
228 : * @param tsp pointer to a timeutil_sync_state object.
229 : *
230 : * @param inst the new instant to be recorded. This becomes the base
231 : * instant if there is no base instant, otherwise the value must be
232 : * strictly after the base instant in both the reference and local
233 : * time scales.
234 : *
235 : * @retval 0 if installation succeeded in providing a new base
236 : * @retval 1 if installation provided a new latest instant
237 : * @retval -EINVAL if the new instant is not compatible with the base instant
238 : */
239 1 : int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
240 : const struct timeutil_sync_instant *inst);
241 :
242 : /**
243 : * @brief Update the state with a new skew and possibly base value.
244 : *
245 : * Set the skew from a value retrieved from persistent storage, or
246 : * calculated based on recent skew estimations including from
247 : * timeutil_sync_estimate_skew().
248 : *
249 : * Optionally update the base timestamp. If the base is replaced the
250 : * latest instant will be cleared until timeutil_sync_state_update() is
251 : * invoked.
252 : *
253 : * @param tsp pointer to a time synchronization state.
254 : *
255 : * @param skew the skew to be used. The value must be positive and
256 : * shouldn't be too far away from 1.
257 : *
258 : * @param base optional new base to be set. If provided this becomes
259 : * the base timestamp that will be used along with skew to convert
260 : * between reference and local timescale instants. Setting the base
261 : * clears the captured latest value.
262 : *
263 : * @return 0 if skew was updated
264 : * @return -EINVAL if skew was not valid
265 : */
266 1 : int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
267 : const struct timeutil_sync_instant *base);
268 :
269 : /**
270 : * @brief Estimate the skew based on current state.
271 : *
272 : * Using the base and latest syncpoints from the state determine the
273 : * skew of the local clock relative to the reference clock. See
274 : * timeutil_sync_state::skew.
275 : *
276 : * @param tsp pointer to a time synchronization state. The base and latest
277 : * syncpoints must be present and the latest syncpoint must be after
278 : * the base point in the local time scale.
279 : *
280 : * @return the estimated skew, or zero if skew could not be estimated.
281 : */
282 1 : float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp);
283 :
284 : /**
285 : * @brief Interpolate a reference timescale instant from a local
286 : * instant.
287 : *
288 : * @param tsp pointer to a time synchronization state. This must have a base
289 : * and a skew installed.
290 : *
291 : * @param local an instant measured in the local timescale. This may
292 : * be before or after the base instant.
293 : *
294 : * @param refp where the corresponding instant in the reference
295 : * timescale should be stored. A negative interpolated reference time
296 : * produces an error. If interpolation fails the referenced object is
297 : * not modified.
298 : *
299 : * @retval 0 if interpolated using a skew of 1
300 : * @retval 1 if interpolated using a skew not equal to 1
301 : * @retval -EINVAL
302 : * * the times synchronization state is not adequately initialized
303 : * * @p refp is null
304 : * @retval -ERANGE the interpolated reference time would be negative
305 : */
306 1 : int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
307 : uint64_t local, uint64_t *refp);
308 :
309 : /**
310 : * @brief Interpolate a local timescale instant from a reference
311 : * instant.
312 : *
313 : * @param tsp pointer to a time synchronization state. This must have a base
314 : * and a skew installed.
315 : *
316 : * @param ref an instant measured in the reference timescale. This
317 : * may be before or after the base instant.
318 : *
319 : * @param localp where the corresponding instant in the local
320 : * timescale should be stored. An interpolated value before local
321 : * time 0 is provided without error. If interpolation fails the
322 : * referenced object is not modified.
323 : *
324 : * @retval 0 if successful with a skew of 1
325 : * @retval 1 if successful with a skew not equal to 1
326 : * @retval -EINVAL
327 : * * the time synchronization state is not adequately initialized
328 : * * @p refp is null
329 : */
330 1 : int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
331 : uint64_t ref, int64_t *localp);
332 :
333 : /**
334 : * @brief Convert from a skew to an error in parts-per-billion.
335 : *
336 : * A skew of 1.0 has zero error. A skew less than 1 has a positive
337 : * error (clock is faster than it should be). A skew greater than one
338 : * has a negative error (clock is slower than it should be).
339 : *
340 : * Note that due to the limited precision of @c float compared with @c
341 : * double the smallest error that can be represented is about 120 ppb.
342 : * A "precise" time source may have error on the order of 2000 ppb.
343 : *
344 : * A skew greater than 3.14748 may underflow the 32-bit
345 : * representation; this represents a clock running at less than 1/3
346 : * its nominal rate.
347 : *
348 : * @return skew error represented as parts-per-billion, or INT32_MIN
349 : * if the skew cannot be represented in the return type.
350 : */
351 1 : int32_t timeutil_sync_skew_to_ppb(float skew);
352 :
353 : /**
354 : * @}
355 : */
356 :
357 : /**
358 : * @defgroup timeutil_timespec_apis Timespec Utility APIs
359 : * @ingroup timeutil_apis
360 : * @{
361 : */
362 :
363 : /**
364 : * @brief Check if a timespec is valid.
365 : *
366 : * Check if a timespec is valid (i.e. normalized) by ensuring that the `tv_nsec` field is in the
367 : * range `[0, NSEC_PER_SEC-1]`.
368 : *
369 : * @note @p ts must not be `NULL`.
370 : *
371 : * @param ts the timespec to check
372 : *
373 : * @return `true` if the timespec is valid, otherwise `false`.
374 : */
375 1 : static inline bool timespec_is_valid(const struct timespec *ts)
376 : {
377 : __ASSERT_NO_MSG(ts != NULL);
378 :
379 : return (ts->tv_nsec >= 0) && (ts->tv_nsec < (long)NSEC_PER_SEC);
380 : }
381 :
382 : /**
383 : * @brief Normalize a timespec so that the `tv_nsec` field is in valid range.
384 : *
385 : * Normalize a timespec by adjusting the `tv_sec` and `tv_nsec` fields so that the `tv_nsec` field
386 : * is in the range `[0, NSEC_PER_SEC-1]`. This is achieved by converting nanoseconds to seconds and
387 : * accumulating seconds in either the positive direction when `tv_nsec` > `NSEC_PER_SEC`, or in the
388 : * negative direction when `tv_nsec` < 0.
389 : *
390 : * In pseudocode, normalization can be done as follows:
391 : * ```python
392 : * if ts.tv_nsec >= NSEC_PER_SEC:
393 : * sec = ts.tv_nsec / NSEC_PER_SEC
394 : * ts.tv_sec += sec
395 : * ts.tv_nsec -= sec * NSEC_PER_SEC
396 : * elif ts.tv_nsec < 0:
397 : * # div_round_up(abs(ts->tv_nsec), NSEC_PER_SEC)
398 : * sec = (NSEC_PER_SEC - ts.tv_nsec - 1) / NSEC_PER_SEC
399 : * ts.tv_sec -= sec;
400 : * ts.tv_nsec += sec * NSEC_PER_SEC;
401 : * ```
402 : *
403 : * @note There are two cases where the normalization can result in integer overflow. These can
404 : * be extrapolated to not simply overflowing the `tv_sec` field by one second, but also by any
405 : * realizable multiple of `NSEC_PER_SEC`.
406 : *
407 : * 1. When `tv_nsec` is negative and `tv_sec` is already most negative.
408 : * 2. When `tv_nsec` is greater-or-equal to `NSEC_PER_SEC` and `tv_sec` is already most positive.
409 : *
410 : * If the operation would result in integer overflow, return value is `false`.
411 : *
412 : * @note @p ts must be non-`NULL`.
413 : *
414 : * @param ts the timespec to be normalized
415 : *
416 : * @return `true` if the operation completes successfully, otherwise `false`.
417 : */
418 1 : bool timespec_normalize(struct timespec *ts);
419 :
420 : /**
421 : * @brief Add one timespec to another
422 : *
423 : * This function sums the two timespecs pointed to by @p a and @p b and stores the result in the
424 : * timespce pointed to by @p a.
425 : *
426 : * If the operation would result in integer overflow, return value is `false`.
427 : *
428 : * @note @p a and @p b must be non-`NULL` and normalized.
429 : *
430 : * @param a the timespec which is added to
431 : * @param b the timespec to be added
432 : *
433 : * @return `true` if the operation was successful, otherwise `false`.
434 : */
435 1 : static inline bool timespec_add(struct timespec *a, const struct timespec *b)
436 : {
437 : __ASSERT_NO_MSG((a != NULL) && timespec_is_valid(a));
438 : __ASSERT_NO_MSG((b != NULL) && timespec_is_valid(b));
439 :
440 : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_add_overflow)
441 :
442 : return !__builtin_add_overflow(a->tv_sec, b->tv_sec, &a->tv_sec) &&
443 : !__builtin_add_overflow(a->tv_nsec, b->tv_nsec, &a->tv_nsec) &&
444 : timespec_normalize(a);
445 :
446 : #else
447 :
448 : if ((a->tv_sec < 0) && (b->tv_sec < 0) && (SYS_TIME_T_MIN - a->tv_sec > b->tv_sec)) {
449 : /* negative integer overflow would occur */
450 : return false;
451 : }
452 :
453 : if ((a->tv_sec > 0) && (b->tv_sec > 0) && (SYS_TIME_T_MAX - a->tv_sec < b->tv_sec)) {
454 : /* positive integer overflow would occur */
455 : return false;
456 : }
457 :
458 : a->tv_sec += b->tv_sec;
459 : a->tv_nsec += b->tv_nsec;
460 :
461 : return timespec_normalize(a);
462 :
463 : #endif
464 : }
465 :
466 : /**
467 : * @brief Negate a timespec object
468 : *
469 : * Negate the timespec object pointed to by @p ts and store the result in the same
470 : * memory location.
471 : *
472 : * If the operation would result in integer overflow, return value is `false`.
473 : *
474 : * @param ts The timespec object to negate.
475 : *
476 : * @return `true` of the operation is successful, otherwise `false`.
477 : */
478 1 : static inline bool timespec_negate(struct timespec *ts)
479 : {
480 : __ASSERT_NO_MSG((ts != NULL) && timespec_is_valid(ts));
481 :
482 : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_sub_overflow)
483 :
484 : return !__builtin_sub_overflow(0LL, ts->tv_sec, &ts->tv_sec) &&
485 : !__builtin_sub_overflow(0L, ts->tv_nsec, &ts->tv_nsec) && timespec_normalize(ts);
486 :
487 : #else
488 :
489 : if (ts->tv_sec == SYS_TIME_T_MIN) {
490 : /* -SYS_TIME_T_MIN > SYS_TIME_T_MAX, so positive integer overflow would occur */
491 : return false;
492 : }
493 :
494 : ts->tv_sec = -ts->tv_sec;
495 : ts->tv_nsec = -ts->tv_nsec;
496 :
497 : return timespec_normalize(ts);
498 :
499 : #endif
500 : }
501 :
502 : /**
503 : * @brief Subtract one timespec from another
504 : *
505 : * This function subtracts the timespec pointed to by @p b from the timespec pointed to by @p a and
506 : * stores the result in the timespce pointed to by @p a.
507 : *
508 : * If the operation would result in integer overflow, return value is `false`.
509 : *
510 : * @note @p a and @p b must be non-`NULL`.
511 : *
512 : * @param a the timespec which is subtracted from
513 : * @param b the timespec to be subtracted
514 : *
515 : * @return `true` if the operation is successful, otherwise `false`.
516 : */
517 1 : static inline bool timespec_sub(struct timespec *a, const struct timespec *b)
518 : {
519 : __ASSERT_NO_MSG(a != NULL);
520 : __ASSERT_NO_MSG(b != NULL);
521 :
522 : struct timespec neg = *b;
523 :
524 : return timespec_negate(&neg) && timespec_add(a, &neg);
525 : }
526 :
527 : /**
528 : * @brief Compare two timespec objects
529 : *
530 : * This function compares two timespec objects pointed to by @p a and @p b.
531 : *
532 : * @note @p a and @p b must be non-`NULL` and normalized.
533 : *
534 : * @param a the first timespec to compare
535 : * @param b the second timespec to compare
536 : *
537 : * @return -1, 0, or +1 if @a a is less than, equal to, or greater than @a b, respectively.
538 : */
539 1 : static inline int timespec_compare(const struct timespec *a, const struct timespec *b)
540 : {
541 : __ASSERT_NO_MSG((a != NULL) && timespec_is_valid(a));
542 : __ASSERT_NO_MSG((b != NULL) && timespec_is_valid(b));
543 :
544 : return (((a->tv_sec == b->tv_sec) && (a->tv_nsec < b->tv_nsec)) * -1) +
545 : (((a->tv_sec == b->tv_sec) && (a->tv_nsec > b->tv_nsec)) * 1) +
546 : ((a->tv_sec < b->tv_sec) * -1) + ((a->tv_sec > b->tv_sec));
547 : }
548 :
549 : /**
550 : * @brief Check if two timespec objects are equal
551 : *
552 : * This function checks if the two timespec objects pointed to by @p a and @p b are equal.
553 : *
554 : * @note @p a and @p b must be non-`NULL` are not required to be normalized.
555 : *
556 : * @param a the first timespec to compare
557 : * @param b the second timespec to compare
558 : *
559 : * @return true if the two timespec objects are equal, otherwise false.
560 : */
561 1 : static inline bool timespec_equal(const struct timespec *a, const struct timespec *b)
562 : {
563 : __ASSERT_NO_MSG(a != NULL);
564 : __ASSERT_NO_MSG(b != NULL);
565 :
566 : return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
567 : }
568 :
569 : /**
570 : * @}
571 : */
572 :
573 : /**
574 : * @ingroup timeutil_repr_apis
575 : * @{
576 : */
577 :
578 : /**
579 : * @brief Convert a kernel timeout to a timespec
580 : *
581 : * This function converts time durations expressed as Zephyr @ref k_timeout_t
582 : * objects to `struct timespec` objects.
583 : *
584 : * @note This function will assert if assertions are enabled and @p timeout is not relative,
585 : * (i.e. a timeout generated by `K_TIMEOUT_ABS_TICKS` or similar is used).
586 : *
587 : * @param timeout the kernel timeout to convert
588 : * @param[out] ts the timespec to store the result
589 : */
590 1 : static inline void timespec_from_timeout(k_timeout_t timeout, struct timespec *ts)
591 : {
592 : __ASSERT_NO_MSG(ts != NULL);
593 : __ASSERT_NO_MSG(Z_IS_TIMEOUT_RELATIVE(timeout) ||
594 : (IS_ENABLED(CONFIG_TIMEOUT_64BIT) &&
595 : K_TIMEOUT_EQ(timeout, (k_timeout_t){K_TICKS_FOREVER})));
596 :
597 : /* equivalent of K_FOREVER without including kernel.h */
598 : if (K_TIMEOUT_EQ(timeout, (k_timeout_t){K_TICKS_FOREVER})) {
599 : /* duration == K_TICKS_FOREVER ticks */
600 : *ts = SYS_TIMESPEC_FOREVER;
601 : /* equivalent of K_NO_WAIT without including kernel.h */
602 : } else if (K_TIMEOUT_EQ(timeout, (k_timeout_t){0})) {
603 : /* duration <= 0 ticks */
604 : *ts = SYS_TIMESPEC_NO_WAIT;
605 : } else {
606 : *ts = SYS_TICKS_TO_TIMESPEC(timeout.ticks);
607 : }
608 :
609 : __ASSERT_NO_MSG(timespec_is_valid(ts));
610 : }
611 :
612 : /**
613 : * @brief Convert a timespec to a kernel timeout
614 : *
615 : * This function converts a time duration, @p req, expressed as a `timespec` object, to a Zephyr
616 : * @ref k_timeout_t object.
617 : *
618 : * If @p req contains a negative duration or if both `tv_sec` and `tv_nsec` fields are zero, this
619 : * function will return @ref K_NO_WAIT.
620 : *
621 : * If @p req contains the maximum representable `timespec`, `{max(time_t), 999999999}`, then this
622 : * function will return @ref K_FOREVER.
623 : *
624 : * If @p req contains a value that is greater than the maximum equivalent tick duration, then this
625 : * function will return the maximum representable tick duration (i.e. @p req will be rounded-down).
626 : *
627 : * Otherwise, this function will return the `k_timeout_t` that is rounded-up to a tick boundary.
628 : *
629 : * If @p rem is not `NULL`, it will be set to the remainder of the conversion, i.e. the difference
630 : * between the requested duration and the converted duration as a `timespec` object, approximately
631 : * as shown below.
632 : *
633 : * ```python
634 : * rem = requested_duration - converted_duration
635 : * ```
636 : *
637 : * @param req the requested `timespec` to convert
638 : * @param[out] rem optional pointer to a `timespec` to store the remainder
639 : * @return the corresponding kernel timeout
640 : */
641 1 : static inline k_timeout_t timespec_to_timeout(const struct timespec *req, struct timespec *rem)
642 : {
643 : k_timeout_t timeout;
644 : struct timespec temp = SYS_TIMESPEC_NO_WAIT;
645 :
646 : __ASSERT_NO_MSG((req != NULL) && timespec_is_valid(req));
647 :
648 : if (timespec_compare(req, &temp) <= 0) {
649 : if (rem != NULL) {
650 : *rem = *req;
651 : }
652 : /* equivalent of K_NO_WAIT without including kernel.h */
653 : timeout.ticks = 0;
654 : return timeout;
655 : }
656 :
657 : temp = SYS_TIMESPEC_FOREVER;
658 :
659 : if (timespec_compare(req, &temp) == 0) {
660 : if (rem != NULL) {
661 : *rem = SYS_TIMESPEC_NO_WAIT;
662 : }
663 : /* equivalent of K_FOREVER without including kernel.h */
664 : timeout.ticks = K_TICKS_FOREVER;
665 : return timeout;
666 : }
667 :
668 : temp = SYS_TIMESPEC_MAX;
669 :
670 : if (timespec_compare(req, &temp) >= 0) {
671 : /* round down to align to max ticks */
672 : timeout.ticks = K_TICK_MAX;
673 : } else {
674 : /* round up to align to next tick boundary */
675 : timeout.ticks = CLAMP(k_ns_to_ticks_ceil64(req->tv_nsec) +
676 : k_sec_to_ticks_ceil64(req->tv_sec),
677 : K_TICK_MIN, K_TICK_MAX);
678 : }
679 :
680 : if (rem != NULL) {
681 : timespec_from_timeout(timeout, rem);
682 : timespec_sub(rem, req);
683 : timespec_negate(rem);
684 : }
685 :
686 : return timeout;
687 : }
688 :
689 : /**
690 : * @}
691 : */
692 :
693 : #ifdef __cplusplus
694 : }
695 : #endif
696 :
697 : #endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */
|