1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "platform-simulation.h"
30
31 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
32
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include "utils/code_utils.h"
38
39 #ifdef __linux__
40 #include <signal.h>
41 #include <time.h>
42
43 #ifndef OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL
44 #define OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL SIGRTMIN
45 #endif
46
47 timer_t sMicroTimer;
48 #endif // __linux__
49
50 #include <openthread/logging.h>
51 #include <openthread/platform/alarm-micro.h>
52 #include <openthread/platform/alarm-milli.h>
53 #include <openthread/platform/diag.h>
54 #include <openthread/platform/time.h>
55
56 #include "lib/platform/exit_code.h"
57
58 #define DEFAULT_TIMEOUT_IN_SEC 10 // seconds
59
60 #ifdef CLOCK_MONOTONIC_RAW
61 #define OT_SIMULATION_CLOCK_ID CLOCK_MONOTONIC_RAW
62 #else
63 #define OT_SIMULATION_CLOCK_ID CLOCK_MONOTONIC
64 #endif
65
66 static bool sIsMsRunning = false;
67 static uint32_t sMsAlarm = 0;
68
69 static bool sIsUsRunning = false;
70 static uint32_t sUsAlarm = 0;
71
72 static uint32_t sSpeedUpFactor = 1;
73
74 #ifdef __linux__
microTimerHandler(int aSignal,siginfo_t * aSignalInfo,void * aUserContext)75 static void microTimerHandler(int aSignal, siginfo_t *aSignalInfo, void *aUserContext)
76 {
77 assert(aSignal == OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL);
78 assert(aSignalInfo->si_value.sival_ptr == &sMicroTimer);
79 (void)aSignal;
80 (void)aSignalInfo;
81 (void)aUserContext;
82 }
83 #endif
84
isExpired(uint32_t aTime,uint32_t aNow)85 static bool isExpired(uint32_t aTime, uint32_t aNow)
86 {
87 // Determine whether or not `aTime` is before or same as `aNow`.
88
89 uint32_t diff = aNow - aTime;
90
91 return (diff & (1U << 31)) == 0;
92 }
93
calculateDuration(uint32_t aTime,uint32_t aNow)94 static uint32_t calculateDuration(uint32_t aTime, uint32_t aNow)
95 {
96 // Return the time duration from `aNow` to `aTime` if `aTimer` is
97 // after `aNow`, otherwise return zero.
98
99 return isExpired(aTime, aNow) ? 0 : aTime - aNow;
100 }
101
platformAlarmInit(uint32_t aSpeedUpFactor)102 void platformAlarmInit(uint32_t aSpeedUpFactor)
103 {
104 sSpeedUpFactor = aSpeedUpFactor;
105
106 #ifdef __linux__
107 {
108 struct sigaction sa;
109
110 sa.sa_flags = SA_SIGINFO;
111 sa.sa_sigaction = microTimerHandler;
112 sigemptyset(&sa.sa_mask);
113
114 if (sigaction(OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL, &sa, NULL) == -1)
115 {
116 perror("sigaction");
117 exit(EXIT_FAILURE);
118 }
119
120 struct sigevent sev;
121
122 sev.sigev_notify = SIGEV_SIGNAL;
123 sev.sigev_signo = OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL;
124 sev.sigev_value.sival_ptr = &sMicroTimer;
125
126 if (-1 == timer_create(CLOCK_MONOTONIC, &sev, &sMicroTimer))
127 {
128 perror("timer_create");
129 exit(EXIT_FAILURE);
130 }
131 }
132 #endif
133 }
134
135 #if defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC)
platformGetNow(void)136 uint64_t platformGetNow(void)
137 {
138 struct timespec now;
139 int err;
140
141 err = clock_gettime(OT_SIMULATION_CLOCK_ID, &now);
142
143 VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO);
144
145 return (uint64_t)now.tv_sec * sSpeedUpFactor * OT_US_PER_S + (uint64_t)now.tv_nsec * sSpeedUpFactor / OT_NS_PER_US;
146 }
147 #else
platformGetNow(void)148 uint64_t platformGetNow(void)
149 {
150 struct timeval tv;
151 int err;
152
153 err = gettimeofday(&tv, NULL);
154
155 assert(err == 0);
156
157 return (uint64_t)tv.tv_sec * sSpeedUpFactor * OT_US_PER_S + (uint64_t)tv.tv_usec * sSpeedUpFactor;
158 }
159 #endif // defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC)
160
otPlatAlarmMilliGetNow(void)161 uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformGetNow() / OT_US_PER_MS); }
162
otPlatAlarmMilliStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)163 void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
164 {
165 OT_UNUSED_VARIABLE(aInstance);
166
167 sMsAlarm = aT0 + aDt;
168 sIsMsRunning = true;
169 }
170
otPlatAlarmMilliStop(otInstance * aInstance)171 void otPlatAlarmMilliStop(otInstance *aInstance)
172 {
173 OT_UNUSED_VARIABLE(aInstance);
174
175 sIsMsRunning = false;
176 }
177
otPlatAlarmMicroGetNow(void)178 uint32_t otPlatAlarmMicroGetNow(void) { return (uint32_t)platformGetNow(); }
179
otPlatAlarmMicroStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)180 void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
181 {
182 OT_UNUSED_VARIABLE(aInstance);
183
184 sUsAlarm = aT0 + aDt;
185 sIsUsRunning = true;
186
187 #ifdef __linux__
188 {
189 struct itimerspec its;
190 uint32_t diff = sUsAlarm - otPlatAlarmMicroGetNow();
191
192 its.it_value.tv_sec = diff / OT_US_PER_S;
193 its.it_value.tv_nsec = (diff % OT_US_PER_S) * OT_NS_PER_US;
194
195 its.it_interval.tv_sec = 0;
196 its.it_interval.tv_nsec = 0;
197
198 if (-1 == timer_settime(sMicroTimer, 0, &its, NULL))
199 {
200 perror("otPlatAlarmMicroStartAt timer_settime()");
201 exit(EXIT_FAILURE);
202 }
203 }
204 #endif // __linux__
205 }
206
otPlatAlarmMicroStop(otInstance * aInstance)207 void otPlatAlarmMicroStop(otInstance *aInstance)
208 {
209 OT_UNUSED_VARIABLE(aInstance);
210
211 sIsUsRunning = false;
212
213 #ifdef __linux__
214 {
215 struct itimerspec its = {{0, 0}, {0, 0}};
216
217 if (-1 == timer_settime(sMicroTimer, 0, &its, NULL))
218 {
219 perror("otPlatAlarmMicroStop timer_settime()");
220 exit(EXIT_FAILURE);
221 }
222 }
223 #endif // __linux__
224 }
225
platformAlarmUpdateTimeout(struct timeval * aTimeout)226 void platformAlarmUpdateTimeout(struct timeval *aTimeout)
227 {
228 uint64_t remaining = DEFAULT_TIMEOUT_IN_SEC * OT_US_PER_S; // in usec.
229
230 assert(aTimeout != NULL);
231
232 if (sIsMsRunning)
233 {
234 uint32_t msRemaining = calculateDuration(sMsAlarm, otPlatAlarmMilliGetNow());
235
236 remaining = ((uint64_t)msRemaining) * OT_US_PER_MS;
237 }
238
239 if (sIsUsRunning)
240 {
241 uint32_t usRemaining = calculateDuration(sUsAlarm, otPlatAlarmMicroGetNow());
242
243 if (usRemaining < remaining)
244 {
245 remaining = usRemaining;
246 }
247 }
248
249 if (remaining == 0)
250 {
251 aTimeout->tv_sec = 0;
252 aTimeout->tv_usec = 0;
253 }
254 else
255 {
256 remaining /= sSpeedUpFactor;
257
258 if (remaining == 0)
259 {
260 remaining = 1;
261 }
262
263 aTimeout->tv_sec = (time_t)(remaining / OT_US_PER_S);
264 aTimeout->tv_usec = remaining % OT_US_PER_S;
265 }
266 }
267
platformAlarmProcess(otInstance * aInstance)268 void platformAlarmProcess(otInstance *aInstance)
269 {
270 if (sIsMsRunning && isExpired(sMsAlarm, otPlatAlarmMilliGetNow()))
271 {
272 sIsMsRunning = false;
273
274 #if OPENTHREAD_CONFIG_DIAG_ENABLE
275 if (otPlatDiagModeGet())
276 {
277 otPlatDiagAlarmFired(aInstance);
278 }
279 else
280 #endif
281 {
282 otPlatAlarmMilliFired(aInstance);
283 }
284 }
285
286 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
287 if (sIsUsRunning && isExpired(sUsAlarm, otPlatAlarmMicroGetNow()))
288 {
289 sIsUsRunning = false;
290
291 otPlatAlarmMicroFired(aInstance);
292 }
293 #endif
294 }
295
otPlatTimeGet(void)296 uint64_t otPlatTimeGet(void) { return platformGetNow(); }
297
otPlatTimeGetXtalAccuracy(void)298 uint16_t otPlatTimeGetXtalAccuracy(void) { return 0; }
299
300 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
301