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
55 #include "lib/platform/exit_code.h"
56
57 #define MS_PER_S 1000
58 #define NS_PER_US 1000
59 #define US_PER_MS 1000
60 #define US_PER_S 1000000
61
62 #define DEFAULT_TIMEOUT_IN_SEC 10 // seconds
63
64 #ifdef CLOCK_MONOTONIC_RAW
65 #define OT_SIMULATION_CLOCK_ID CLOCK_MONOTONIC_RAW
66 #else
67 #define OT_SIMULATION_CLOCK_ID CLOCK_MONOTONIC
68 #endif
69
70 static bool sIsMsRunning = false;
71 static uint32_t sMsAlarm = 0;
72
73 static bool sIsUsRunning = false;
74 static uint32_t sUsAlarm = 0;
75
76 static uint32_t sSpeedUpFactor = 1;
77
78 #ifdef __linux__
microTimerHandler(int aSignal,siginfo_t * aSignalInfo,void * aUserContext)79 static void microTimerHandler(int aSignal, siginfo_t *aSignalInfo, void *aUserContext)
80 {
81 assert(aSignal == OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL);
82 assert(aSignalInfo->si_value.sival_ptr == &sMicroTimer);
83 (void)aSignal;
84 (void)aSignalInfo;
85 (void)aUserContext;
86 }
87 #endif
88
isExpired(uint32_t aTime,uint32_t aNow)89 static bool isExpired(uint32_t aTime, uint32_t aNow)
90 {
91 // Determine whether or not `aTime` is before or same as `aNow`.
92
93 uint32_t diff = aNow - aTime;
94
95 return (diff & (1U << 31)) == 0;
96 }
97
calculateDuration(uint32_t aTime,uint32_t aNow)98 static uint32_t calculateDuration(uint32_t aTime, uint32_t aNow)
99 {
100 // Return the time duration from `aNow` to `aTime` if `aTimer` is
101 // after `aNow`, otherwise return zero.
102
103 return isExpired(aTime, aNow) ? 0 : aTime - aNow;
104 }
105
platformAlarmInit(uint32_t aSpeedUpFactor)106 void platformAlarmInit(uint32_t aSpeedUpFactor)
107 {
108 sSpeedUpFactor = aSpeedUpFactor;
109
110 #ifdef __linux__
111 {
112 struct sigaction sa;
113
114 sa.sa_flags = SA_SIGINFO;
115 sa.sa_sigaction = microTimerHandler;
116 sigemptyset(&sa.sa_mask);
117
118 if (sigaction(OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL, &sa, NULL) == -1)
119 {
120 perror("sigaction");
121 exit(EXIT_FAILURE);
122 }
123
124 struct sigevent sev;
125
126 sev.sigev_notify = SIGEV_SIGNAL;
127 sev.sigev_signo = OPENTHREAD_CONFIG_MICRO_TIMER_SIGNAL;
128 sev.sigev_value.sival_ptr = &sMicroTimer;
129
130 if (-1 == timer_create(CLOCK_MONOTONIC, &sev, &sMicroTimer))
131 {
132 perror("timer_create");
133 exit(EXIT_FAILURE);
134 }
135 }
136 #endif
137 }
138
139 #if defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC)
platformGetNow(void)140 uint64_t platformGetNow(void)
141 {
142 struct timespec now;
143 int err;
144
145 err = clock_gettime(OT_SIMULATION_CLOCK_ID, &now);
146
147 VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO);
148
149 return (uint64_t)now.tv_sec * sSpeedUpFactor * US_PER_S + (uint64_t)now.tv_nsec * sSpeedUpFactor / NS_PER_US;
150 }
151 #else
platformGetNow(void)152 uint64_t platformGetNow(void)
153 {
154 struct timeval tv;
155 int err;
156
157 err = gettimeofday(&tv, NULL);
158
159 assert(err == 0);
160
161 return (uint64_t)tv.tv_sec * sSpeedUpFactor * US_PER_S + (uint64_t)tv.tv_usec * sSpeedUpFactor;
162 }
163 #endif // defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC)
164
otPlatAlarmMilliGetNow(void)165 uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformGetNow() / US_PER_MS); }
166
otPlatAlarmMilliStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)167 void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
168 {
169 OT_UNUSED_VARIABLE(aInstance);
170
171 sMsAlarm = aT0 + aDt;
172 sIsMsRunning = true;
173 }
174
otPlatAlarmMilliStop(otInstance * aInstance)175 void otPlatAlarmMilliStop(otInstance *aInstance)
176 {
177 OT_UNUSED_VARIABLE(aInstance);
178
179 sIsMsRunning = false;
180 }
181
otPlatAlarmMicroGetNow(void)182 uint32_t otPlatAlarmMicroGetNow(void) { return (uint32_t)platformGetNow(); }
183
otPlatAlarmMicroStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)184 void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
185 {
186 OT_UNUSED_VARIABLE(aInstance);
187
188 sUsAlarm = aT0 + aDt;
189 sIsUsRunning = true;
190
191 #ifdef __linux__
192 {
193 struct itimerspec its;
194 uint32_t diff = sUsAlarm - otPlatAlarmMicroGetNow();
195
196 its.it_value.tv_sec = diff / US_PER_S;
197 its.it_value.tv_nsec = (diff % US_PER_S) * NS_PER_US;
198
199 its.it_interval.tv_sec = 0;
200 its.it_interval.tv_nsec = 0;
201
202 if (-1 == timer_settime(sMicroTimer, 0, &its, NULL))
203 {
204 perror("otPlatAlarmMicroStartAt timer_settime()");
205 exit(EXIT_FAILURE);
206 }
207 }
208 #endif // __linux__
209 }
210
otPlatAlarmMicroStop(otInstance * aInstance)211 void otPlatAlarmMicroStop(otInstance *aInstance)
212 {
213 OT_UNUSED_VARIABLE(aInstance);
214
215 sIsUsRunning = false;
216
217 #ifdef __linux__
218 {
219 struct itimerspec its = {{0, 0}, {0, 0}};
220
221 if (-1 == timer_settime(sMicroTimer, 0, &its, NULL))
222 {
223 perror("otPlatAlarmMicroStop timer_settime()");
224 exit(EXIT_FAILURE);
225 }
226 }
227 #endif // __linux__
228 }
229
platformAlarmUpdateTimeout(struct timeval * aTimeout)230 void platformAlarmUpdateTimeout(struct timeval *aTimeout)
231 {
232 uint64_t remaining = DEFAULT_TIMEOUT_IN_SEC * US_PER_S; // in usec.
233
234 assert(aTimeout != NULL);
235
236 if (sIsMsRunning)
237 {
238 uint32_t msRemaining = calculateDuration(sMsAlarm, otPlatAlarmMilliGetNow());
239
240 remaining = ((uint64_t)msRemaining) * US_PER_MS;
241 }
242
243 if (sIsUsRunning)
244 {
245 uint32_t usRemaining = calculateDuration(sUsAlarm, otPlatAlarmMicroGetNow());
246
247 if (usRemaining < remaining)
248 {
249 remaining = usRemaining;
250 }
251 }
252
253 if (remaining == 0)
254 {
255 aTimeout->tv_sec = 0;
256 aTimeout->tv_usec = 0;
257 }
258 else
259 {
260 remaining /= sSpeedUpFactor;
261
262 if (remaining == 0)
263 {
264 remaining = 1;
265 }
266
267 aTimeout->tv_sec = (time_t)(remaining / US_PER_S);
268 aTimeout->tv_usec = remaining % US_PER_S;
269 }
270 }
271
platformAlarmProcess(otInstance * aInstance)272 void platformAlarmProcess(otInstance *aInstance)
273 {
274 if (sIsMsRunning && isExpired(sMsAlarm, otPlatAlarmMilliGetNow()))
275 {
276 sIsMsRunning = false;
277
278 #if OPENTHREAD_CONFIG_DIAG_ENABLE
279 if (otPlatDiagModeGet())
280 {
281 otPlatDiagAlarmFired(aInstance);
282 }
283 else
284 #endif
285 {
286 otPlatAlarmMilliFired(aInstance);
287 }
288 }
289
290 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
291 if (sIsUsRunning && isExpired(sUsAlarm, otPlatAlarmMicroGetNow()))
292 {
293 sIsUsRunning = false;
294
295 otPlatAlarmMicroFired(aInstance);
296 }
297 #endif
298 }
299
otPlatTimeGet(void)300 uint64_t otPlatTimeGet(void) { return platformGetNow(); }
301
otPlatTimeGetXtalAccuracy(void)302 uint16_t otPlatTimeGetXtalAccuracy(void) { return 0; }
303
304 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
305