• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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