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) { return otPlatTimeGet() * sSpeedUpFactor; }
91
platformAlarmInit(uint32_t aSpeedUpFactor,int aRealTimeSignal)92 void platformAlarmInit(uint32_t aSpeedUpFactor, int aRealTimeSignal)
93 {
94 sSpeedUpFactor = aSpeedUpFactor;
95
96 if (aRealTimeSignal == 0)
97 {
98 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
99 otLogWarnPlat("Real time signal not enabled, microsecond timers may be inaccurate!");
100 #endif
101 }
102 #ifdef __linux__
103 else if (aRealTimeSignal >= SIGRTMIN && aRealTimeSignal <= SIGRTMAX)
104 {
105 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE && !OPENTHREAD_POSIX_VIRTUAL_TIME
106 struct sigaction sa;
107 struct sigevent sev;
108
109 sa.sa_flags = SA_SIGINFO;
110 sa.sa_sigaction = microTimerHandler;
111 sigemptyset(&sa.sa_mask);
112
113 VerifyOrDie(sigaction(aRealTimeSignal, &sa, nullptr) != -1, OT_EXIT_ERROR_ERRNO);
114
115 sev.sigev_notify = SIGEV_SIGNAL;
116 sev.sigev_signo = aRealTimeSignal;
117 sev.sigev_value.sival_ptr = &sMicroTimer;
118
119 VerifyOrDie(timer_create(CLOCK_MONOTONIC, &sev, &sMicroTimer) != -1, OT_EXIT_ERROR_ERRNO);
120
121 sRealTimeSignal = aRealTimeSignal;
122 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE && !OPENTHREAD_POSIX_VIRTUAL_TIME
123 }
124 #endif // __linux__
125 else
126 {
127 DieNow(OT_EXIT_INVALID_ARGUMENTS);
128 }
129 }
130
otPlatAlarmMilliGetNow(void)131 uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformAlarmGetNow() / US_PER_MS); }
132
otPlatAlarmMilliStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)133 void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
134 {
135 OT_UNUSED_VARIABLE(aInstance);
136
137 sMsAlarm = aT0 + aDt;
138 sIsMsRunning = true;
139 }
140
otPlatAlarmMilliStop(otInstance * aInstance)141 void otPlatAlarmMilliStop(otInstance *aInstance)
142 {
143 OT_UNUSED_VARIABLE(aInstance);
144
145 sIsMsRunning = false;
146 }
147
148 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
otPlatAlarmMicroGetNow(void)149 uint32_t otPlatAlarmMicroGetNow(void) { return static_cast<uint32_t>(platformAlarmGetNow()); }
150
otPlatAlarmMicroStartAt(otInstance * aInstance,uint32_t aT0,uint32_t aDt)151 void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
152 {
153 OT_UNUSED_VARIABLE(aInstance);
154
155 sUsAlarm = aT0 + aDt;
156 sIsUsRunning = true;
157
158 #ifdef __linux__
159 if (sRealTimeSignal != 0)
160 {
161 struct itimerspec its;
162 uint32_t diff = sUsAlarm - otPlatAlarmMicroGetNow();
163
164 its.it_value.tv_sec = diff / US_PER_S;
165 its.it_value.tv_nsec = (diff % US_PER_S) * NS_PER_US;
166
167 its.it_interval.tv_sec = 0;
168 its.it_interval.tv_nsec = 0;
169
170 if (-1 == timer_settime(sMicroTimer, 0, &its, nullptr))
171 {
172 otLogWarnPlat("Failed to update microsecond timer: %s", strerror(errno));
173 }
174 }
175 #endif // __linux__
176 }
177
otPlatAlarmMicroStop(otInstance * aInstance)178 void otPlatAlarmMicroStop(otInstance *aInstance)
179 {
180 OT_UNUSED_VARIABLE(aInstance);
181
182 sIsUsRunning = false;
183
184 #ifdef __linux__
185 if (sRealTimeSignal != 0)
186 {
187 struct itimerspec its = {{0, 0}, {0, 0}};
188
189 if (-1 == timer_settime(sMicroTimer, 0, &its, nullptr))
190 {
191 otLogWarnPlat("Failed to stop microsecond timer: %s", strerror(errno));
192 }
193 }
194 #endif // __linux__
195 }
196 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
197
platformAlarmUpdateTimeout(struct timeval * aTimeout)198 void platformAlarmUpdateTimeout(struct timeval *aTimeout)
199 {
200 int64_t remaining = INT32_MAX;
201 uint64_t now = platformAlarmGetNow();
202
203 assert(aTimeout != nullptr);
204
205 if (sIsMsRunning)
206 {
207 remaining = (int32_t)(sMsAlarm - (uint32_t)(now / US_PER_MS));
208 VerifyOrExit(remaining > 0);
209 remaining *= US_PER_MS;
210 remaining -= (now % US_PER_MS);
211 }
212
213 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
214 if (sIsUsRunning)
215 {
216 int32_t usRemaining = (int32_t)(sUsAlarm - (uint32_t)now);
217
218 if (usRemaining < remaining)
219 {
220 remaining = usRemaining;
221 }
222 }
223 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
224
225 exit:
226 if (remaining <= 0)
227 {
228 aTimeout->tv_sec = 0;
229 aTimeout->tv_usec = 0;
230 }
231 else
232 {
233 remaining /= sSpeedUpFactor;
234
235 if (remaining == 0)
236 {
237 remaining = 1;
238 }
239
240 if (remaining < static_cast<int64_t>(aTimeout->tv_sec) * US_PER_S + static_cast<int64_t>(aTimeout->tv_usec))
241 {
242 aTimeout->tv_sec = static_cast<time_t>(remaining / US_PER_S);
243 aTimeout->tv_usec = static_cast<suseconds_t>(remaining % US_PER_S);
244 }
245 }
246 }
247
platformAlarmProcess(otInstance * aInstance)248 void platformAlarmProcess(otInstance *aInstance)
249 {
250 int32_t remaining;
251
252 if (sIsMsRunning)
253 {
254 remaining = (int32_t)(sMsAlarm - otPlatAlarmMilliGetNow());
255
256 if (remaining <= 0)
257 {
258 sIsMsRunning = false;
259
260 #if OPENTHREAD_CONFIG_DIAG_ENABLE
261
262 if (otPlatDiagModeGet())
263 {
264 otPlatDiagAlarmFired(aInstance);
265 }
266 else
267 #endif
268 {
269 otPlatAlarmMilliFired(aInstance);
270 }
271 }
272 }
273
274 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
275
276 if (sIsUsRunning)
277 {
278 remaining = (int32_t)(sUsAlarm - otPlatAlarmMicroGetNow());
279
280 if (remaining <= 0)
281 {
282 sIsUsRunning = false;
283
284 otPlatAlarmMicroFired(aInstance);
285 }
286 }
287
288 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
289 }
290