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 /**
30 * @file
31 * @brief
32 * This file includes the platform-specific initializers.
33 */
34
35 #include "openthread-posix-config.h"
36 #include "platform-posix.h"
37
38 #include <assert.h>
39 #include <inttypes.h>
40
41 #include <openthread-core-config.h>
42 #include <openthread/border_router.h>
43 #include <openthread/cli.h>
44 #include <openthread/heap.h>
45 #include <openthread/tasklet.h>
46 #include <openthread/trel.h>
47 #include <openthread/platform/alarm-milli.h>
48 #include <openthread/platform/infra_if.h>
49 #include <openthread/platform/logging.h>
50 #include <openthread/platform/otns.h>
51 #include <openthread/platform/radio.h>
52
53 #include "common/code_utils.hpp"
54 #include "common/debug.hpp"
55 #include "posix/platform/daemon.hpp"
56 #include "posix/platform/firewall.hpp"
57 #include "posix/platform/infra_if.hpp"
58 #include "posix/platform/mainloop.hpp"
59 #include "posix/platform/mdns_socket.hpp"
60 #include "posix/platform/radio_url.hpp"
61 #include "posix/platform/spinel_driver_getter.hpp"
62 #include "posix/platform/udp.hpp"
63
64 otInstance *gInstance = nullptr;
65 bool gDryRun = false;
66
67 CoprocessorType sCoprocessorType = OT_COPROCESSOR_UNKNOWN;
68
processStateChange(otChangedFlags aFlags,void * aContext)69 static void processStateChange(otChangedFlags aFlags, void *aContext)
70 {
71 otInstance *instance = static_cast<otInstance *>(aContext);
72
73 OT_UNUSED_VARIABLE(instance);
74 OT_UNUSED_VARIABLE(aFlags);
75
76 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
77 platformNetifStateChange(instance, aFlags);
78 #endif
79
80 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
81 ot::Posix::InfraNetif::Get().HandleBackboneStateChange(instance, aFlags);
82 #endif
83
84 platformRadioHandleStateChange(instance, aFlags);
85 }
86
get802154RadioUrl(const otPlatformCoprocessorUrls & aUrls)87 static const char *get802154RadioUrl(const otPlatformCoprocessorUrls &aUrls)
88 {
89 const char *radioUrl = nullptr;
90
91 for (uint8_t i = 0; i < aUrls.mNum; i++)
92 {
93 ot::Posix::RadioUrl url(aUrls.mUrls[i]);
94
95 if (strcmp(url.GetProtocol(), "trel") == 0)
96 {
97 continue;
98 }
99
100 radioUrl = aUrls.mUrls[i];
101 break;
102 }
103
104 VerifyOrDie(radioUrl != nullptr, OT_EXIT_INVALID_ARGUMENTS);
105 return radioUrl;
106 }
107
108 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
getTrelRadioUrl(otPlatformConfig * aPlatformConfig)109 static const char *getTrelRadioUrl(otPlatformConfig *aPlatformConfig)
110 {
111 const char *radioUrl = nullptr;
112
113 for (uint8_t i = 0; i < aPlatformConfig->mCoprocessorUrls.mNum; i++)
114 {
115 ot::Posix::RadioUrl url(aPlatformConfig->mCoprocessorUrls.mUrls[i]);
116
117 if (strcmp(url.GetProtocol(), "trel") == 0)
118 {
119 radioUrl = aPlatformConfig->mCoprocessorUrls.mUrls[i];
120 break;
121 }
122 }
123
124 return radioUrl;
125 }
126 #endif
127
128 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
otSysSetInfraNetif(const char * aInfraNetifName,int aIcmp6Socket)129 void otSysSetInfraNetif(const char *aInfraNetifName, int aIcmp6Socket)
130 {
131 ot::Posix::InfraNetif::Get().SetInfraNetif(aInfraNetifName, aIcmp6Socket);
132 }
133 #endif
134
platformInitRcpMode(otPlatformConfig * aPlatformConfig)135 void platformInitRcpMode(otPlatformConfig *aPlatformConfig)
136 {
137 platformRadioInit(get802154RadioUrl(aPlatformConfig->mCoprocessorUrls));
138
139 // For Dry-Run option, only init the co-processor.
140 VerifyOrExit(!aPlatformConfig->mDryRun);
141
142 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE && !OPENTHREAD_POSIX_CONFIG_TREL_SELECT_INFRA_IF
143 platformTrelInit(getTrelRadioUrl(aPlatformConfig));
144 #endif
145 platformRandomInit();
146
147 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
148 ot::Posix::InfraNetif::Get().Init();
149 #endif
150
151 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
152 ot::Posix::MdnsSocket::Get().Init();
153 #endif
154
155 gNetifName[0] = '\0';
156
157 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
158 platformNetifInit(aPlatformConfig);
159 #endif
160 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
161 platformResolverInit();
162 #endif
163
164 #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
165 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
166 ot::Posix::Udp::Get().Init(otSysGetThreadNetifName());
167 #else
168 ot::Posix::Udp::Get().Init(aPlatformConfig->mInterfaceName);
169 #endif
170 #endif
171 exit:
172 return;
173 }
174
platformInitNcpMode(otPlatformConfig * aPlatformConfig)175 void platformInitNcpMode(otPlatformConfig *aPlatformConfig)
176 {
177 // Do nothing now.
178 OT_UNUSED_VARIABLE(aPlatformConfig);
179 }
180
platformInit(otPlatformConfig * aPlatformConfig)181 void platformInit(otPlatformConfig *aPlatformConfig)
182 {
183 #if OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE
184 platformBacktraceInit();
185 #endif
186
187 platformAlarmInit(aPlatformConfig->mSpeedUpFactor, aPlatformConfig->mRealTimeSignal);
188
189 if (sCoprocessorType == OT_COPROCESSOR_UNKNOWN)
190 {
191 sCoprocessorType = platformSpinelManagerInit(get802154RadioUrl(aPlatformConfig->mCoprocessorUrls));
192 }
193
194 switch (sCoprocessorType)
195 {
196 case OT_COPROCESSOR_RCP:
197 platformInitRcpMode(aPlatformConfig);
198 break;
199
200 case OT_COPROCESSOR_NCP:
201 platformInitNcpMode(aPlatformConfig);
202 break;
203
204 default:
205 otPlatLog(OT_LOG_LEVEL_CRIT, OT_LOG_REGION_PLATFORM, "Unknown type of the co-processor!\n");
206 exit(OT_EXIT_FAILURE);
207 break;
208 }
209
210 aPlatformConfig->mCoprocessorType = sCoprocessorType;
211 }
212
platformSetUp(otPlatformConfig * aPlatformConfig)213 void platformSetUp(otPlatformConfig *aPlatformConfig)
214 {
215 OT_UNUSED_VARIABLE(aPlatformConfig);
216
217 VerifyOrExit(!gDryRun);
218
219 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
220 if (aPlatformConfig->mBackboneInterfaceName != nullptr && strlen(aPlatformConfig->mBackboneInterfaceName) > 0)
221 {
222 int icmp6Sock = -1;
223
224 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
225 icmp6Sock = ot::Posix::InfraNetif::CreateIcmp6Socket(aPlatformConfig->mBackboneInterfaceName);
226 #endif
227
228 otSysSetInfraNetif(aPlatformConfig->mBackboneInterfaceName, icmp6Sock);
229 }
230 #endif
231
232 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
233 ot::Posix::InfraNetif::Get().SetUp();
234 #endif
235
236 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
237 platformNetifSetUp();
238 #endif
239
240 #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
241 ot::Posix::Udp::Get().SetUp();
242 #endif
243
244 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
245 ot::Posix::MdnsSocket::Get().SetUp();
246 #endif
247
248 #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
249 ot::Posix::Daemon::Get().SetUp();
250 #endif
251
252 SuccessOrDie(otSetStateChangedCallback(gInstance, processStateChange, gInstance));
253
254 exit:
255 return;
256 }
257
otSysInitCoprocessor(otPlatformCoprocessorUrls * aUrls)258 CoprocessorType otSysInitCoprocessor(otPlatformCoprocessorUrls *aUrls)
259 {
260 sCoprocessorType = platformSpinelManagerInit(get802154RadioUrl(*aUrls));
261 return sCoprocessorType;
262 }
263
otSysGetSpinelDriver(void)264 otSpinelDriver *otSysGetSpinelDriver(void) { return &ot::Posix::GetSpinelDriver(); }
265
otSysInit(otPlatformConfig * aPlatformConfig)266 otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
267 {
268 OT_ASSERT(gInstance == nullptr);
269
270 platformInit(aPlatformConfig);
271
272 gDryRun = aPlatformConfig->mDryRun;
273 if (sCoprocessorType == OT_COPROCESSOR_RCP)
274 {
275 gInstance = otInstanceInitSingle();
276 OT_ASSERT(gInstance != nullptr);
277
278 platformSetUp(aPlatformConfig);
279 }
280
281 return gInstance;
282 }
283
platformTearDown(void)284 void platformTearDown(void)
285 {
286 VerifyOrExit(!gDryRun);
287
288 #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
289 ot::Posix::Daemon::Get().TearDown();
290 #endif
291
292 #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
293 ot::Posix::Udp::Get().TearDown();
294 #endif
295
296 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
297 platformNetifTearDown();
298 #endif
299
300 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
301 ot::Posix::InfraNetif::Get().TearDown();
302 #endif
303
304 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
305 ot::Posix::MdnsSocket::Get().TearDown();
306 #endif
307
308 exit:
309 return;
310 }
311
platformDeinitRcpMode(void)312 void platformDeinitRcpMode(void)
313 {
314 #if OPENTHREAD_POSIX_VIRTUAL_TIME
315 virtualTimeDeinit();
316 #endif
317 platformRadioDeinit();
318 platformSpinelManagerDeinit();
319 sCoprocessorType = OT_COPROCESSOR_UNKNOWN;
320
321 // For Dry-Run option, only the radio is initialized.
322 VerifyOrExit(!gDryRun);
323
324 #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
325 ot::Posix::Udp::Get().Deinit();
326 #endif
327 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
328 platformNetifDeinit();
329 #endif
330 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE && !OPENTHREAD_POSIX_CONFIG_TREL_SELECT_INFRA_IF
331 otPlatTrelDisable(/* aInstance */ nullptr);
332 platformTrelDeinit();
333 #endif
334
335 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
336 ot::Posix::InfraNetif::Get().Deinit();
337 #endif
338
339 #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
340 ot::Posix::MdnsSocket::Get().Deinit();
341 #endif
342
343 exit:
344 return;
345 }
346
platformDeinitNcpMode(void)347 void platformDeinitNcpMode(void)
348 {
349 platformSpinelManagerDeinit();
350 sCoprocessorType = OT_COPROCESSOR_UNKNOWN;
351 }
352
otSysDeinit(void)353 void otSysDeinit(void)
354 {
355 if (sCoprocessorType == OT_COPROCESSOR_RCP)
356 {
357 OT_ASSERT(gInstance != nullptr);
358 platformTearDown();
359 otInstanceFinalize(gInstance);
360 gInstance = nullptr;
361 platformDeinitRcpMode();
362 }
363 else if (sCoprocessorType == OT_COPROCESSOR_NCP)
364 {
365 platformDeinitNcpMode();
366 }
367 }
368
369 #if OPENTHREAD_POSIX_VIRTUAL_TIME
370 /**
371 * Try selecting the given file descriptors in nonblocking mode.
372 *
373 * @param[in,out] aContext A reference to the mainloop context.
374 *
375 * @returns The value returned from select().
376 *
377 */
trySelect(otSysMainloopContext & aContext)378 static int trySelect(otSysMainloopContext &aContext)
379 {
380 struct timeval timeout = {0, 0};
381 fd_set originReadFdSet = aContext.mReadFdSet;
382 fd_set originWriteFdSet = aContext.mWriteFdSet;
383 fd_set originErrorFdSet = aContext.mErrorFdSet;
384 int rval;
385
386 rval = select(aContext.mMaxFd + 1, &aContext.mReadFdSet, &aContext.mWriteFdSet, &aContext.mErrorFdSet, &timeout);
387
388 if (rval == 0)
389 {
390 aContext.mReadFdSet = originReadFdSet;
391 aContext.mWriteFdSet = originWriteFdSet;
392 aContext.mErrorFdSet = originErrorFdSet;
393 }
394
395 return rval;
396 }
397 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
398
otSysMainloopUpdate(otInstance * aInstance,otSysMainloopContext * aMainloop)399 void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop)
400 {
401 ot::Posix::Mainloop::Manager::Get().Update(*aMainloop);
402
403 platformAlarmUpdateTimeout(&aMainloop->mTimeout);
404 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
405 platformNetifUpdateFdSet(aMainloop);
406 #endif
407 #if OPENTHREAD_POSIX_VIRTUAL_TIME
408 virtualTimeUpdateFdSet(aMainloop);
409 #else
410 platformSpinelManagerUpdateFdSet(aMainloop);
411 platformRadioUpdateFdSet(aMainloop);
412 #endif
413 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
414 platformTrelUpdateFdSet(aMainloop);
415 #endif
416 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
417 platformResolverUpdateFdSet(aMainloop);
418 #endif
419
420 if (otTaskletsArePending(aInstance))
421 {
422 aMainloop->mTimeout.tv_sec = 0;
423 aMainloop->mTimeout.tv_usec = 0;
424 }
425 }
426
otSysMainloopPoll(otSysMainloopContext * aMainloop)427 int otSysMainloopPoll(otSysMainloopContext *aMainloop)
428 {
429 int rval;
430
431 #if OPENTHREAD_POSIX_VIRTUAL_TIME
432 if (timerisset(&aMainloop->mTimeout))
433 {
434 // Make sure there are no data ready in UART
435 rval = trySelect(*aMainloop);
436
437 if (rval == 0)
438 {
439 bool noWrite = true;
440
441 // If there are write requests, the device is supposed to wake soon
442 for (int i = 0; i < aMainloop->mMaxFd + 1; ++i)
443 {
444 if (FD_ISSET(i, &aMainloop->mWriteFdSet))
445 {
446 noWrite = false;
447 break;
448 }
449 }
450
451 if (noWrite)
452 {
453 virtualTimeSendSleepEvent(&aMainloop->mTimeout);
454 }
455
456 rval = select(aMainloop->mMaxFd + 1, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet,
457 &aMainloop->mErrorFdSet, nullptr);
458 }
459 }
460 else
461 #endif
462 {
463 rval = select(aMainloop->mMaxFd + 1, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet,
464 &aMainloop->mTimeout);
465 }
466
467 return rval;
468 }
469
otSysMainloopProcess(otInstance * aInstance,const otSysMainloopContext * aMainloop)470 void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMainloop)
471 {
472 ot::Posix::Mainloop::Manager::Get().Process(*aMainloop);
473
474 #if OPENTHREAD_POSIX_VIRTUAL_TIME
475 virtualTimeProcess(aInstance, aMainloop);
476 #else
477 platformSpinelManagerProcess(aInstance, aMainloop);
478 platformRadioProcess(aInstance, aMainloop);
479 #endif
480 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
481 platformTrelProcess(aInstance, aMainloop);
482 #endif
483 platformAlarmProcess(aInstance);
484 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
485 platformNetifProcess(aMainloop);
486 #endif
487 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
488 platformResolverProcess(aMainloop);
489 #endif
490 }
491
IsSystemDryRun(void)492 bool IsSystemDryRun(void) { return gDryRun; }
493
494 #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE && OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
otSysCliInitUsingDaemon(otInstance * aInstance)495 void otSysCliInitUsingDaemon(otInstance *aInstance)
496 {
497 otCliInit(
498 aInstance,
499 [](void *aContext, const char *aFormat, va_list aArguments) -> int {
500 return static_cast<ot::Posix::Daemon *>(aContext)->OutputFormatV(aFormat, aArguments);
501 },
502 &ot::Posix::Daemon::Get());
503 }
504 #endif
505