/* * Copyright (c) 2016, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * @brief * This file includes the platform-specific initializers. */ #include "openthread-posix-config.h" #include "platform-posix.h" #include #include #include #include #include #include #include #include #include #include "common/code_utils.hpp" #include "common/debug.hpp" #include "posix/platform/daemon.hpp" #include "posix/platform/firewall.hpp" #include "posix/platform/infra_if.hpp" #include "posix/platform/mainloop.hpp" #include "posix/platform/radio_url.hpp" #include "posix/platform/udp.hpp" otInstance *gInstance = nullptr; bool gDryRun = false; #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE static void processStateChange(otChangedFlags aFlags, void *aContext) { otInstance *instance = static_cast(aContext); OT_UNUSED_VARIABLE(instance); OT_UNUSED_VARIABLE(aFlags); #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifStateChange(instance, aFlags); #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE if (gBackboneNetifIndex != 0) { platformBackboneStateChange(instance, aFlags); } #endif } #endif static const char *get802154RadioUrl(otPlatformConfig *aPlatformConfig) { const char *radioUrl = nullptr; for (uint8_t i = 0; i < aPlatformConfig->mRadioUrlNum; i++) { ot::Posix::RadioUrl url(aPlatformConfig->mRadioUrls[i]); if (strcmp(url.GetProtocol(), "trel") == 0) { continue; } radioUrl = aPlatformConfig->mRadioUrls[i]; break; } VerifyOrDie(radioUrl != nullptr, OT_EXIT_INVALID_ARGUMENTS); return radioUrl; } #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE static const char *getTrelRadioUrl(otPlatformConfig *aPlatformConfig) { const char *radioUrl = nullptr; for (uint8_t i = 0; i < aPlatformConfig->mRadioUrlNum; i++) { ot::Posix::RadioUrl url(aPlatformConfig->mRadioUrls[i]); if (strcmp(url.GetProtocol(), "trel") == 0) { radioUrl = aPlatformConfig->mRadioUrls[i]; break; } } return radioUrl; } #endif void platformInit(otPlatformConfig *aPlatformConfig) { platformAlarmInit(aPlatformConfig->mSpeedUpFactor, aPlatformConfig->mRealTimeSignal); platformRadioInit(get802154RadioUrl(aPlatformConfig)); // For Dry-Run option, only init the radio. VerifyOrExit(!aPlatformConfig->mDryRun); #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelInit(getTrelRadioUrl(aPlatformConfig)); #endif platformRandomInit(); #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE platformBackboneInit(aPlatformConfig->mBackboneInterfaceName); #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE ot::Posix::InfraNetif::Get().Init(aPlatformConfig->mBackboneInterfaceName); #endif gNetifName[0] = '\0'; #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifInit(aPlatformConfig->mInterfaceName); #endif #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE ot::Posix::Udp::Get().Init(otSysGetThreadNetifName()); #else ot::Posix::Udp::Get().Init(aPlatformConfig->mInterfaceName); #endif #endif exit: return; } void platformSetUp(void) { VerifyOrExit(!gDryRun); #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE platformBackboneSetUp(); #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE ot::Posix::InfraNetif::Get().SetUp(); #endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifSetUp(); #endif #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE ot::Posix::Udp::Get().SetUp(); #endif #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE ot::Posix::Daemon::Get().SetUp(); #endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE SuccessOrDie(otSetStateChangedCallback(gInstance, processStateChange, gInstance)); #endif exit: return; } otInstance *otSysInit(otPlatformConfig *aPlatformConfig) { OT_ASSERT(gInstance == nullptr); platformInit(aPlatformConfig); gDryRun = aPlatformConfig->mDryRun; gInstance = otInstanceInitSingle(); OT_ASSERT(gInstance != nullptr); platformSetUp(); return gInstance; } void platformTearDown(void) { VerifyOrExit(!gDryRun); #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE ot::Posix::Daemon::Get().TearDown(); #endif #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE ot::Posix::Udp::Get().TearDown(); #endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifTearDown(); #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE ot::Posix::InfraNetif::Get().TearDown(); #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE platformBackboneTearDown(); #endif exit: return; } void platformDeinit(void) { #if OPENTHREAD_POSIX_VIRTUAL_TIME virtualTimeDeinit(); #endif platformRadioDeinit(); // For Dry-Run option, only the radio is initialized. VerifyOrExit(!gDryRun); #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE ot::Posix::Udp::Get().Deinit(); #endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifDeinit(); #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelDeinit(); #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE ot::Posix::InfraNetif::Get().Deinit(); #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE platformBackboneDeinit(); #endif exit: return; } void otSysDeinit(void) { OT_ASSERT(gInstance != nullptr); platformTearDown(); otInstanceFinalize(gInstance); gInstance = nullptr; platformDeinit(); } #if OPENTHREAD_POSIX_VIRTUAL_TIME /** * This function try selecting the given file descriptors in nonblocking mode. * * @param[in,out] aReadFdSet A pointer to the read file descriptors. * @param[in,out] aWriteFdSet A pointer to the write file descriptors. * @param[in,out] aErrorFdSet A pointer to the error file descriptors. * @param[in] aMaxFd The max file descriptor. * * @returns The value returned from select(). * */ static int trySelect(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int aMaxFd) { struct timeval timeout = {0, 0}; fd_set originReadFdSet = *aReadFdSet; fd_set originWriteFdSet = *aWriteFdSet; fd_set originErrorFdSet = *aErrorFdSet; int rval; rval = select(aMaxFd + 1, aReadFdSet, aWriteFdSet, aErrorFdSet, &timeout); if (rval == 0) { *aReadFdSet = originReadFdSet; *aWriteFdSet = originWriteFdSet; *aErrorFdSet = originErrorFdSet; } return rval; } #endif // OPENTHREAD_POSIX_VIRTUAL_TIME void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop) { ot::Posix::Mainloop::Manager::Get().Update(*aMainloop); platformAlarmUpdateTimeout(&aMainloop->mTimeout); #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mMaxFd); #endif #if OPENTHREAD_POSIX_VIRTUAL_TIME virtualTimeUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mMaxFd, &aMainloop->mTimeout); #else platformRadioUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mMaxFd, &aMainloop->mTimeout); #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mMaxFd, &aMainloop->mTimeout); #endif if (otTaskletsArePending(aInstance)) { aMainloop->mTimeout.tv_sec = 0; aMainloop->mTimeout.tv_usec = 0; } } int otSysMainloopPoll(otSysMainloopContext *aMainloop) { int rval; #if OPENTHREAD_POSIX_VIRTUAL_TIME if (timerisset(&aMainloop->mTimeout)) { // Make sure there are no data ready in UART rval = trySelect(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, aMainloop->mMaxFd); if (rval == 0) { bool noWrite = true; // If there are write requests, the device is supposed to wake soon for (int i = 0; i < aMainloop->mMaxFd + 1; ++i) { if (FD_ISSET(i, &aMainloop->mWriteFdSet)) { noWrite = false; break; } } if (noWrite) { virtualTimeSendSleepEvent(&aMainloop->mTimeout); } rval = select(aMainloop->mMaxFd + 1, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, nullptr); } } else #endif { rval = select(aMainloop->mMaxFd + 1, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mTimeout); } return rval; } void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMainloop) { ot::Posix::Mainloop::Manager::Get().Process(*aMainloop); #if OPENTHREAD_POSIX_VIRTUAL_TIME virtualTimeProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet); #else platformRadioProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet); #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet); #endif platformAlarmProcess(aInstance); #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE platformNetifProcess(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet); #endif } bool IsSystemDryRun(void) { return gDryRun; }