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_floor32((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 : static inline bool timespec_normalize(struct timespec *ts)
419 : {
420 : __ASSERT_NO_MSG(ts != NULL);
421 :
422 : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_add_overflow)
423 :
424 : int64_t sec = 0;
425 : int sign = (ts->tv_nsec >= 0) - (ts->tv_nsec < 0);
426 :
427 : /* only one of the following should be non-zero */
428 : sec += (ts->tv_nsec >= (long)NSEC_PER_SEC) * (ts->tv_nsec / (long)NSEC_PER_SEC);
429 : sec += ((sizeof(ts->tv_nsec) != sizeof(int64_t)) && (ts->tv_nsec != LONG_MIN) &&
430 : (ts->tv_nsec < 0)) *
431 : DIV_ROUND_UP((unsigned long)-ts->tv_nsec, (long)NSEC_PER_SEC);
432 : sec += ((sizeof(ts->tv_nsec) == sizeof(int64_t)) && (ts->tv_nsec != INT64_MIN) &&
433 : (ts->tv_nsec < 0)) *
434 : DIV_ROUND_UP((uint64_t)-ts->tv_nsec, NSEC_PER_SEC);
435 : sec += ((sizeof(ts->tv_nsec) != sizeof(int64_t)) && (ts->tv_nsec == LONG_MIN)) *
436 : ((LONG_MAX / NSEC_PER_SEC) + 1);
437 : sec += ((sizeof(ts->tv_nsec) == sizeof(int64_t)) && (ts->tv_nsec == INT64_MIN)) *
438 : ((INT64_MAX / NSEC_PER_SEC) + 1);
439 :
440 : ts->tv_nsec -= sec * sign * NSEC_PER_SEC;
441 :
442 : bool overflow = __builtin_add_overflow(ts->tv_sec, sign * sec, &ts->tv_sec);
443 :
444 : if (!overflow) {
445 : __ASSERT_NO_MSG(timespec_is_valid(ts));
446 : }
447 :
448 : return !overflow;
449 :
450 : #else
451 :
452 : long sec;
453 :
454 : if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
455 : sec = ts->tv_nsec / (long)NSEC_PER_SEC;
456 : } else if (ts->tv_nsec < 0) {
457 : sec = DIV_ROUND_UP((unsigned long)-ts->tv_nsec, NSEC_PER_SEC);
458 : } else {
459 : sec = 0;
460 : }
461 :
462 : if ((ts->tv_nsec < 0) && (ts->tv_sec < 0) && (ts->tv_sec - SYS_TIME_T_MIN < sec)) {
463 : /*
464 : * When `tv_nsec` is negative and `tv_sec` is already most negative,
465 : * further subtraction would cause integer overflow.
466 : */
467 : return false;
468 : }
469 :
470 : if ((ts->tv_nsec >= (long)NSEC_PER_SEC) && (ts->tv_sec > 0) &&
471 : (SYS_TIME_T_MAX - ts->tv_sec < sec)) {
472 : /*
473 : * When `tv_nsec` is >= `NSEC_PER_SEC` and `tv_sec` is already most
474 : * positive, further addition would cause integer overflow.
475 : */
476 : return false;
477 : }
478 :
479 : if (ts->tv_nsec >= (long)NSEC_PER_SEC) {
480 : ts->tv_sec += sec;
481 : ts->tv_nsec -= sec * (long)NSEC_PER_SEC;
482 : } else if (ts->tv_nsec < 0) {
483 : ts->tv_sec -= sec;
484 : ts->tv_nsec += sec * (long)NSEC_PER_SEC;
485 : } else {
486 : /* no change: SonarQube was complaining */
487 : }
488 :
489 : __ASSERT_NO_MSG(timespec_is_valid(ts));
490 :
491 : return true;
492 : #endif
493 : }
494 :
495 : /**
496 : * @brief Add one timespec to another
497 : *
498 : * This function sums the two timespecs pointed to by @p a and @p b and stores the result in the
499 : * timespce pointed to by @p a.
500 : *
501 : * If the operation would result in integer overflow, return value is `false`.
502 : *
503 : * @note @p a and @p b must be non-`NULL` and normalized.
504 : *
505 : * @param a the timespec which is added to
506 : * @param b the timespec to be added
507 : *
508 : * @return `true` if the operation was successful, otherwise `false`.
509 : */
510 1 : static inline bool timespec_add(struct timespec *a, const struct timespec *b)
511 : {
512 : __ASSERT_NO_MSG((a != NULL) && timespec_is_valid(a));
513 : __ASSERT_NO_MSG((b != NULL) && timespec_is_valid(b));
514 :
515 : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_add_overflow)
516 :
517 : return !__builtin_add_overflow(a->tv_sec, b->tv_sec, &a->tv_sec) &&
518 : !__builtin_add_overflow(a->tv_nsec, b->tv_nsec, &a->tv_nsec) &&
519 : timespec_normalize(a);
520 :
521 : #else
522 :
523 : if ((a->tv_sec < 0) && (b->tv_sec < 0) && (SYS_TIME_T_MIN - a->tv_sec > b->tv_sec)) {
524 : /* negative integer overflow would occur */
525 : return false;
526 : }
527 :
528 : if ((a->tv_sec > 0) && (b->tv_sec > 0) && (SYS_TIME_T_MAX - a->tv_sec < b->tv_sec)) {
529 : /* positive integer overflow would occur */
530 : return false;
531 : }
532 :
533 : a->tv_sec += b->tv_sec;
534 : a->tv_nsec += b->tv_nsec;
535 :
536 : return timespec_normalize(a);
537 :
538 : #endif
539 : }
540 :
541 : /**
542 : * @brief Negate a timespec object
543 : *
544 : * Negate the timespec object pointed to by @p ts and store the result in the same
545 : * memory location.
546 : *
547 : * If the operation would result in integer overflow, return value is `false`.
548 : *
549 : * @param ts The timespec object to negate.
550 : *
551 : * @return `true` of the operation is successful, otherwise `false`.
552 : */
553 1 : static inline bool timespec_negate(struct timespec *ts)
554 : {
555 : __ASSERT_NO_MSG((ts != NULL) && timespec_is_valid(ts));
556 :
557 : #if defined(CONFIG_SPEED_OPTIMIZATIONS) && HAS_BUILTIN(__builtin_sub_overflow)
558 :
559 : return !__builtin_sub_overflow(0LL, ts->tv_sec, &ts->tv_sec) &&
560 : !__builtin_sub_overflow(0L, ts->tv_nsec, &ts->tv_nsec) && timespec_normalize(ts);
561 :
562 : #else
563 :
564 : if (ts->tv_sec == SYS_TIME_T_MIN) {
565 : /* -SYS_TIME_T_MIN > SYS_TIME_T_MAX, so positive integer overflow would occur */
566 : return false;
567 : }
568 :
569 : ts->tv_sec = -ts->tv_sec;
570 : ts->tv_nsec = -ts->tv_nsec;
571 :
572 : return timespec_normalize(ts);
573 :
574 : #endif
575 : }
576 :
577 : /**
578 : * @brief Subtract one timespec from another
579 : *
580 : * This function subtracts the timespec pointed to by @p b from the timespec pointed to by @p a and
581 : * stores the result in the timespce pointed to by @p a.
582 : *
583 : * If the operation would result in integer overflow, return value is `false`.
584 : *
585 : * @note @p a and @p b must be non-`NULL`.
586 : *
587 : * @param a the timespec which is subtracted from
588 : * @param b the timespec to be subtracted
589 : *
590 : * @return `true` if the operation is successful, otherwise `false`.
591 : */
592 1 : static inline bool timespec_sub(struct timespec *a, const struct timespec *b)
593 : {
594 : __ASSERT_NO_MSG(a != NULL);
595 : __ASSERT_NO_MSG(b != NULL);
596 :
597 : struct timespec neg = *b;
598 :
599 : return timespec_negate(&neg) && timespec_add(a, &neg);
600 : }
601 :
602 : /**
603 : * @brief Compare two timespec objects
604 : *
605 : * This function compares two timespec objects pointed to by @p a and @p b.
606 : *
607 : * @note @p a and @p b must be non-`NULL` and normalized.
608 : *
609 : * @param a the first timespec to compare
610 : * @param b the second timespec to compare
611 : *
612 : * @return -1, 0, or +1 if @a a is less than, equal to, or greater than @a b, respectively.
613 : */
614 1 : static inline int timespec_compare(const struct timespec *a, const struct timespec *b)
615 : {
616 : __ASSERT_NO_MSG((a != NULL) && timespec_is_valid(a));
617 : __ASSERT_NO_MSG((b != NULL) && timespec_is_valid(b));
618 :
619 : return (((a->tv_sec == b->tv_sec) && (a->tv_nsec < b->tv_nsec)) * -1) +
620 : (((a->tv_sec == b->tv_sec) && (a->tv_nsec > b->tv_nsec)) * 1) +
621 : ((a->tv_sec < b->tv_sec) * -1) + ((a->tv_sec > b->tv_sec));
622 : }
623 :
624 : /**
625 : * @brief Check if two timespec objects are equal
626 : *
627 : * This function checks if the two timespec objects pointed to by @p a and @p b are equal.
628 : *
629 : * @note @p a and @p b must be non-`NULL` are not required to be normalized.
630 : *
631 : * @param a the first timespec to compare
632 : * @param b the second timespec to compare
633 : *
634 : * @return true if the two timespec objects are equal, otherwise false.
635 : */
636 1 : static inline bool timespec_equal(const struct timespec *a, const struct timespec *b)
637 : {
638 : __ASSERT_NO_MSG(a != NULL);
639 : __ASSERT_NO_MSG(b != NULL);
640 :
641 : return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
642 : }
643 :
644 : /**
645 : * @}
646 : */
647 :
648 : /**
649 : * @ingroup timeutil_repr_apis
650 : * @{
651 : */
652 :
653 : /**
654 : * @brief Convert a kernel timeout to a timespec
655 : *
656 : * This function converts time durations expressed as Zephyr @ref k_timeout_t
657 : * objects to `struct timespec` objects.
658 : *
659 : * @note This function will assert if assertions are enabled and @p timeout is not relative,
660 : * (i.e. a timeout generated by `K_TIMEOUT_ABS_TICKS` or similar is used).
661 : *
662 : * @param timeout the kernel timeout to convert
663 : * @param[out] ts the timespec to store the result
664 : */
665 1 : static inline void timespec_from_timeout(k_timeout_t timeout, struct timespec *ts)
666 : {
667 : __ASSERT_NO_MSG(ts != NULL);
668 : __ASSERT_NO_MSG(Z_IS_TIMEOUT_RELATIVE(timeout) ||
669 : (IS_ENABLED(CONFIG_TIMEOUT_64BIT) &&
670 : K_TIMEOUT_EQ(timeout, (k_timeout_t){K_TICKS_FOREVER})));
671 :
672 : /* equivalent of K_FOREVER without including kernel.h */
673 : if (K_TIMEOUT_EQ(timeout, (k_timeout_t){K_TICKS_FOREVER})) {
674 : /* duration == K_TICKS_FOREVER ticks */
675 : *ts = SYS_TIMESPEC_FOREVER;
676 : /* equivalent of K_NO_WAIT without including kernel.h */
677 : } else if (K_TIMEOUT_EQ(timeout, (k_timeout_t){0})) {
678 : /* duration <= 0 ticks */
679 : *ts = SYS_TIMESPEC_NO_WAIT;
680 : } else {
681 : *ts = SYS_TICKS_TO_TIMESPEC(timeout.ticks);
682 : }
683 :
684 : __ASSERT_NO_MSG(timespec_is_valid(ts));
685 : }
686 :
687 : /**
688 : * @brief Convert a timespec to a kernel timeout
689 : *
690 : * This function converts a time duration, @p req, expressed as a `timespec` object, to a Zephyr
691 : * @ref k_timeout_t object.
692 : *
693 : * If @p req contains a negative duration or if both `tv_sec` and `tv_nsec` fields are zero, this
694 : * function will return @ref K_NO_WAIT.
695 : *
696 : * If @p req contains the maximum representable `timespec`, `{max(time_t), 999999999}`, then this
697 : * function will return @ref K_FOREVER.
698 : *
699 : * If @p req contains a value that is greater than the maximum equivalent tick duration, then this
700 : * function will return the maximum representable tick duration (i.e. @p req will be rounded-down).
701 : *
702 : * Otherwise, this function will return the `k_timeout_t` that is rounded-up to a tick boundary.
703 : *
704 : * If @p rem is not `NULL`, it will be set to the remainder of the conversion, i.e. the difference
705 : * between the requested duration and the converted duration as a `timespec` object, approximately
706 : * as shown below.
707 : *
708 : * ```python
709 : * rem = requested_duration - converted_duration
710 : * ```
711 : *
712 : * @param req the requested `timespec` to convert
713 : * @param[out] rem optional pointer to a `timespec` to store the remainder
714 : * @return the corresponding kernel timeout
715 : */
716 1 : static inline k_timeout_t timespec_to_timeout(const struct timespec *req, struct timespec *rem)
717 : {
718 : k_timeout_t timeout;
719 :
720 : __ASSERT_NO_MSG((req != NULL) && timespec_is_valid(req));
721 :
722 : if (timespec_compare(req, &SYS_TIMESPEC_NO_WAIT) <= 0) {
723 : if (rem != NULL) {
724 : *rem = *req;
725 : }
726 : /* equivalent of K_NO_WAIT without including kernel.h */
727 : timeout.ticks = 0;
728 : return timeout;
729 : }
730 :
731 : if (timespec_compare(req, &SYS_TIMESPEC_FOREVER) == 0) {
732 : if (rem != NULL) {
733 : *rem = SYS_TIMESPEC_NO_WAIT;
734 : }
735 : /* equivalent of K_FOREVER without including kernel.h */
736 : timeout.ticks = K_TICKS_FOREVER;
737 : return timeout;
738 : }
739 :
740 : if (timespec_compare(req, &SYS_TIMESPEC_MAX) >= 0) {
741 : /* round down to align to max ticks */
742 : timeout.ticks = K_TICK_MAX;
743 : } else {
744 : /* round up to align to next tick boundary */
745 : timeout.ticks = CLAMP(k_ns_to_ticks_ceil64(req->tv_nsec) +
746 : k_sec_to_ticks_ceil64(req->tv_sec),
747 : K_TICK_MIN, K_TICK_MAX);
748 : }
749 :
750 : if (rem != NULL) {
751 : timespec_from_timeout(timeout, rem);
752 : timespec_sub(rem, req);
753 : timespec_negate(rem);
754 : }
755 :
756 : return timeout;
757 : }
758 :
759 : /**
760 : * @}
761 : */
762 :
763 : #ifdef __cplusplus
764 : }
765 : #endif
766 :
767 : #endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */
|