xref: /aosp_15_r20/external/openthread/examples/platforms/simulation/alarm.c (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
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