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