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