1 /* coap_time.c -- Clock Handling
2  *
3  * Copyright (C) 2015,2023 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  *
7  * This file is part of the CoAP library libcoap. Please see
8  * README for terms of use.
9  */
10 
11 /**
12  * @file coap_time.c
13  * @brief Clock handling functions
14  */
15 
16 #include "coap3/coap_internal.h"
17 
18 #ifdef HAVE_TIME_H
19 #include <time.h>
20 #ifdef HAVE_SYS_TIME_H
21 #include <sys/time.h>
22 #endif
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>  /* _POSIX_TIMERS */
25 #endif
26 #ifdef HAVE_WINSOCK2_H
27 #include <winsock2.h>
28 #include <stdint.h>
29 #endif
30 
31 static coap_tick_t coap_clock_offset = 0;
32 
33 #if _POSIX_TIMERS && !defined(__APPLE__)
34 /* _POSIX_TIMERS is > 0 when clock_gettime() is available */
35 
36 /* Use real-time clock for correct timestamps in coap_log(). */
37 #define COAP_CLOCK CLOCK_REALTIME
38 #endif
39 
40 #if defined(HAVE_WINSOCK2_H) && !defined(__MINGW32__)
41 static int
gettimeofday(struct timeval *tp, TIME_ZONE_INFORMATION *tzp)42 gettimeofday(struct timeval *tp, TIME_ZONE_INFORMATION *tzp) {
43   (void)tzp;
44   static const uint64_t s_tUnixEpoch = 116444736000000000Ui64;
45 
46   FILETIME file_time;
47   ULARGE_INTEGER time;
48   uint64_t tUsSinceUnicEpoch;
49 
50   GetSystemTimeAsFileTime(&file_time);
51   time.LowPart = file_time.dwLowDateTime;
52   time.HighPart = file_time.dwHighDateTime;
53   tUsSinceUnicEpoch = (time.QuadPart - s_tUnixEpoch) / 10;
54 
55   tp->tv_sec = (long)(tUsSinceUnicEpoch / 1000000);
56   tp->tv_usec = (long)(tUsSinceUnicEpoch % 1000000);
57   return 0;
58 }
59 #endif /* HAVE_WINSOCK2_H && !__MINGW32__ */
60 
61 void
coap_clock_init(void)62 coap_clock_init(void) {
63 #ifdef COAP_CLOCK
64   struct timespec tv;
65   clock_gettime(COAP_CLOCK, &tv);
66 #else /* _POSIX_TIMERS */
67   struct timeval tv;
68   gettimeofday(&tv, NULL);
69 #endif /* not _POSIX_TIMERS */
70 
71   coap_clock_offset = tv.tv_sec;
72 }
73 
74 /* creates a Qx.frac from fval */
75 #define Q(frac,fval) ((1 << (frac)) * (fval))
76 
77 /* number of frac bits for sub-seconds */
78 #define FRAC 10
79 
80 /* rounds val up and right shifts by frac positions */
81 #define SHR_FP(val,frac) (((coap_tick_t)((val) + (1 << ((frac) - 1)))) >> (frac))
82 
83 void
coap_ticks(coap_tick_t *t)84 coap_ticks(coap_tick_t *t) {
85   coap_tick_t tmp;
86 
87 #ifdef COAP_CLOCK
88   struct timespec tv;
89   clock_gettime(COAP_CLOCK, &tv);
90   /* Possible errors are (see clock_gettime(2)):
91    *  EFAULT tp points outside the accessible address space.
92    *  EINVAL The clk_id specified is not supported on this system.
93    * Both cases should not be possible here.
94    */
95 
96   tmp = SHR_FP(tv.tv_nsec * Q(FRAC, (COAP_TICKS_PER_SECOND/1000000000.0)), FRAC);
97 #else /* _POSIX_TIMERS */
98   /* Fall back to gettimeofday() */
99 
100   struct timeval tv;
101   gettimeofday(&tv, NULL);
102   /* Possible errors are (see gettimeofday(2)):
103    *  EFAULT One of tv or tz pointed outside the accessible address space.
104    *  EINVAL Timezone (or something else) is invalid.
105    * Both cases should not be possible here.
106    */
107 
108   tmp = SHR_FP(tv.tv_usec * Q(FRAC, (COAP_TICKS_PER_SECOND/1000000.0)), FRAC);
109 #endif /* not _POSIX_TIMERS */
110 
111   /* Finally, convert temporary FP representation to multiple of
112    * COAP_TICKS_PER_SECOND */
113   *t = tmp + (tv.tv_sec - coap_clock_offset) * COAP_TICKS_PER_SECOND;
114 }
115 
116 coap_time_t
coap_ticks_to_rt(coap_tick_t t)117 coap_ticks_to_rt(coap_tick_t t) {
118   return coap_clock_offset + (t / COAP_TICKS_PER_SECOND);
119 }
120 
121 uint64_t
coap_ticks_to_rt_us(coap_tick_t t)122 coap_ticks_to_rt_us(coap_tick_t t) {
123   return (uint64_t)coap_clock_offset * 1000000 + (uint64_t)t * 1000000 / COAP_TICKS_PER_SECOND;
124 }
125 
126 coap_tick_t
coap_ticks_from_rt_us(uint64_t t)127 coap_ticks_from_rt_us(uint64_t t) {
128   return (coap_tick_t)((t - (uint64_t)coap_clock_offset * 1000000) * COAP_TICKS_PER_SECOND / 1000000);
129 }
130 
131 #undef Q
132 #undef FRAC
133 #undef SHR_FP
134 
135 #else /* HAVE_TIME_H */
136 
137 #ifdef __clang__
138 /* Make compilers happy that do not like empty modules. As this function is
139  * never used, we ignore -Wunused-function at the end of compiling this file
140  */
141 #pragma GCC diagnostic ignored "-Wunused-function"
142 #endif
143 COAP_STATIC_INLINE void
dummy(void)144 dummy(void) {
145 }
146 
147 #endif /* not HAVE_TIME_H */
148