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 "openthread-posix-config.h"
30 #include "platform-posix.h"
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <time.h>
36
37 #include <openthread/platform/alarm-micro.h>
38 #include <openthread/platform/alarm-milli.h>
39 #include <openthread/platform/diag.h>
40
41 #include "common/code_utils.hpp"
42
43 static bool sIsMsRunning = false;
44 static uint32_t sMsAlarm = 0;
45
46 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
47 static bool sIsUsRunning = false;
48 static uint32_t sUsAlarm = 0;
49 #endif
50
51 static uint32_t sSpeedUpFactor = 1;
52
53 #ifdef __linux__
54
55 #include <signal.h>
56 #include <time.h>
57
58 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE && !OPENTHREAD_POSIX_VIRTUAL_TIME
59 static timer_t sMicroTimer;
60 static int sRealTimeSignal = 0;
61
microTimerHandler(int aSignal,siginfo_t * aSignalInfo,void * aUserContext)62 static void microTimerHandler(int aSignal, siginfo_t *aSignalInfo, void *aUserContext)
63 {
64 assert(aSignal == sRealTimeSignal);
65 assert(aSignalInfo->si_value.sival_ptr == &sMicroTimer);
66 OT_UNUSED_VARIABLE(aSignal);
67 OT_UNUSED_VARIABLE(aSignalInfo);
68 OT_UNUSED_VARIABLE(aUserContext);
69 }
70 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE && !OPENTHREAD_POSIX_VIRTUAL_TIME
71 #endif // __linux__
72
73 #ifdef CLOCK_MONOTONIC_RAW
74 #define OT_POSIX_CLOCK_ID CLOCK_MONOTONIC_RAW
75 #else
76 #define OT_POSIX_CLOCK_ID CLOCK_MONOTONIC
77 #endif
78
79 #if !OPENTHREAD_POSIX_VIRTUAL_TIME
otPlatTimeGet(void)80 uint64_t otPlatTimeGet(void)
81 {
82 struct timespec now;
83
84 VerifyOrDie(clock_gettime(OT_POSIX_CLOCK_ID, &now) == 0, OT_EXIT_FAILURE);
85
86 return static_cast<uint64_t>(now.tv_sec) * US_PER_S + static_cast<uint64_t>(now.tv_nsec) / NS_PER_US;
87 }
88 #endif // !OPENTHREAD_POSIX_VIRTUAL_TIME
89
platformAlarmGetNow(void)90 static uint64_t platformAlarmGetNow(void)
91 {
92 return otPlatTimeGet() * sSpeedUpFactor;
93 }
94
platformAlarmInit(uint32_t aSpeedUpFactor,int aRealTimeSignal)95 void platformAlarmInit(uint32_t aSpeedUpFactor, int aRealTimeSignal)
96 {
97 sSpeedUpFactor = aSpeedUpFactor;
98
99 if (aRealTimeSignal == 0)
100 {
101 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
102 otLogWarnPlat("Real time signal not enabled, microsecond timers may be inaccurate!");
103 #endif
104 }
105 #ifdef __linux__
106 else if (aRealTimeSignal >= SIGRTMIN && aRealTimeSignal <= SIGRTMAX)
107 {
108 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE && !OPENTHREAD_POSIX_VIRTUAL_TIME
109 struct sigaction sa;
110 struct sigevent sev;
111
112 sa.sa_flags = SA_SIGINFO;
113 sa.sa_sigaction = microTimerHandler;
114 sigemptyset(&sa.sa_mask);
115
116 VerifyOrDie(sigaction(aRealTimeSignal, &sa, nullptr) != -1, OT_EXIT_ERROR_ERRNO);
117
118 sev.sigev_notify = SIGEV_SIGNAL;
119 sev.sigev_signo = aRealTimeSignal;
120 sev.sigev_value.sival_ptr = &sMicroTimer;
121
122 VerifyOrDie(timer_create(CLOCK_MONOTONIC, &sev, &sMicroTimer) != -1, OT_EXIT_ERROR_ERRNO);
123
124 sRealTimeSignal = aRealTimeSignal;
125 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE && !OPENTHREAD_POSIX_VIRTUAL_TIME
126 }
127 #endif // __linux__
128 else
129 {
130 DieNow(OT_EXIT_INVALID_ARGUMENTS);
131 }
132 }
133
otPlatAlarmMilliGetNow(void)134 uint32_t otPlatAlarmMilliGetNow(void)
135 {
136 return (uint32_t)(platformAlarmGetNow() / US_PER_MS);
137 }
138
otPlatAlarmMilliStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)139 void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
140 {
141 OT_UNUSED_VARIABLE(aInstance);
142
143 sMsAlarm = aT0 + aDt;
144 sIsMsRunning = true;
145 }
146
otPlatAlarmMilliStop(otInstance * aInstance)147 void otPlatAlarmMilliStop(otInstance *aInstance)
148 {
149 OT_UNUSED_VARIABLE(aInstance);
150
151 sIsMsRunning = false;
152 }
153
154 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
otPlatAlarmMicroGetNow(void)155 uint32_t otPlatAlarmMicroGetNow(void)
156 {
157 return static_cast<uint32_t>(platformAlarmGetNow());
158 }
159
otPlatAlarmMicroStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)160 void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
161 {
162 OT_UNUSED_VARIABLE(aInstance);
163
164 sUsAlarm = aT0 + aDt;
165 sIsUsRunning = true;
166
167 #ifdef __linux__
168 if (sRealTimeSignal != 0)
169 {
170 struct itimerspec its;
171 uint32_t diff = sUsAlarm - otPlatAlarmMicroGetNow();
172
173 its.it_value.tv_sec = diff / US_PER_S;
174 its.it_value.tv_nsec = (diff % US_PER_S) * NS_PER_US;
175
176 its.it_interval.tv_sec = 0;
177 its.it_interval.tv_nsec = 0;
178
179 if (-1 == timer_settime(sMicroTimer, 0, &its, nullptr))
180 {
181 otLogWarnPlat("Failed to update microsecond timer: %s", strerror(errno));
182 }
183 }
184 #endif // __linux__
185 }
186
otPlatAlarmMicroStop(otInstance * aInstance)187 void otPlatAlarmMicroStop(otInstance *aInstance)
188 {
189 OT_UNUSED_VARIABLE(aInstance);
190
191 sIsUsRunning = false;
192
193 #ifdef __linux__
194 if (sRealTimeSignal != 0)
195 {
196 struct itimerspec its = {{0, 0}, {0, 0}};
197
198 if (-1 == timer_settime(sMicroTimer, 0, &its, nullptr))
199 {
200 otLogWarnPlat("Failed to stop microsecond timer: %s", strerror(errno));
201 }
202 }
203 #endif // __linux__
204 }
205 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
206
platformAlarmUpdateTimeout(struct timeval * aTimeout)207 void platformAlarmUpdateTimeout(struct timeval *aTimeout)
208 {
209 int64_t remaining = INT32_MAX;
210 uint64_t now = platformAlarmGetNow();
211
212 assert(aTimeout != nullptr);
213
214 if (sIsMsRunning)
215 {
216 remaining = (int32_t)(sMsAlarm - (uint32_t)(now / US_PER_MS));
217 VerifyOrExit(remaining > 0);
218 remaining *= US_PER_MS;
219 remaining -= (now % US_PER_MS);
220 }
221
222 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
223 if (sIsUsRunning)
224 {
225 int32_t usRemaining = (int32_t)(sUsAlarm - (uint32_t)now);
226
227 if (usRemaining < remaining)
228 {
229 remaining = usRemaining;
230 }
231 }
232 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
233
234 exit:
235 if (remaining <= 0)
236 {
237 aTimeout->tv_sec = 0;
238 aTimeout->tv_usec = 0;
239 }
240 else
241 {
242 remaining /= sSpeedUpFactor;
243
244 if (remaining == 0)
245 {
246 remaining = 1;
247 }
248
249 if (remaining < static_cast<int64_t>(aTimeout->tv_sec) * US_PER_S + static_cast<int64_t>(aTimeout->tv_usec))
250 {
251 aTimeout->tv_sec = static_cast<time_t>(remaining / US_PER_S);
252 aTimeout->tv_usec = static_cast<suseconds_t>(remaining % US_PER_S);
253 }
254 }
255 }
256
platformAlarmProcess(otInstance * aInstance)257 void platformAlarmProcess(otInstance *aInstance)
258 {
259 int32_t remaining;
260
261 if (sIsMsRunning)
262 {
263 remaining = (int32_t)(sMsAlarm - otPlatAlarmMilliGetNow());
264
265 if (remaining <= 0)
266 {
267 sIsMsRunning = false;
268
269 #if OPENTHREAD_CONFIG_DIAG_ENABLE
270
271 if (otPlatDiagModeGet())
272 {
273 otPlatDiagAlarmFired(aInstance);
274 }
275 else
276 #endif
277 {
278 otPlatAlarmMilliFired(aInstance);
279 }
280 }
281 }
282
283 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
284
285 if (sIsUsRunning)
286 {
287 remaining = (int32_t)(sUsAlarm - otPlatAlarmMicroGetNow());
288
289 if (remaining <= 0)
290 {
291 sIsUsRunning = false;
292
293 otPlatAlarmMicroFired(aInstance);
294 }
295 }
296
297 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
298 }
299