From d640d4a7f996c1225423d9252bbf8c909f40c398 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 3 Oct 2018 23:21:12 -0700 Subject: [PATCH] [link-raw] move the LinkRaw implementation to core from api folder (#3114) --- etc/visual-studio/libopenthread.vcxproj | 1 + .../libopenthread.vcxproj.filters | 3 + etc/visual-studio/libopenthread_k.vcxproj | 2 +- .../libopenthread_k.vcxproj.filters | 2 +- src/core/Makefile.am | 4 +- src/core/api/link_raw_api.cpp | 516 ---------------- src/core/common/instance.hpp | 2 +- src/core/mac/link_raw.cpp | 574 ++++++++++++++++++ src/core/{api => mac}/link_raw.hpp | 0 9 files changed, 584 insertions(+), 520 deletions(-) create mode 100644 src/core/mac/link_raw.cpp rename src/core/{api => mac}/link_raw.hpp (100%) diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index e4603df29..4f0727bda 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -94,6 +94,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index fdbebd734..2af37200b 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -165,6 +165,9 @@ Source Files\crypto + + Source Files\api + Source Files\mac diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 0e54e2b07..82e2bc779 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -188,7 +188,6 @@ - @@ -214,6 +213,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 8d1b8a615..0e7efa9f3 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -686,7 +686,7 @@ Header Files\openthread - + Header Files\api diff --git a/src/core/Makefile.am b/src/core/Makefile.am index be1b7de5a..71a065b68 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -150,6 +150,7 @@ SOURCES_COMMON = \ crypto/mbedtls.cpp \ crypto/pbkdf2_cmac.cpp \ crypto/sha256.cpp \ + mac/link_raw.cpp \ mac/mac.cpp \ mac/mac_filter.cpp \ mac/mac_frame.cpp \ @@ -230,6 +231,7 @@ libopenthread_radio_a_SOURCES = \ common/string.cpp \ common/tasklet.cpp \ common/timer.cpp \ + mac/link_raw.cpp \ mac/mac_frame.cpp \ thread/link_quality.cpp \ utils/missing_strlcat.c \ @@ -250,7 +252,6 @@ HEADERS_COMMON = \ openthread-core-config-check.h \ openthread-core-config.h \ openthread-core-default-config.h \ - api/link_raw.hpp \ coap/coap.hpp \ coap/coap_header.hpp \ coap/coap_secure.hpp \ @@ -278,6 +279,7 @@ HEADERS_COMMON = \ crypto/mbedtls.hpp \ crypto/pbkdf2_cmac.h \ crypto/sha256.hpp \ + mac/link_raw.hpp \ mac/mac.hpp \ mac/mac_filter.hpp \ mac/mac_frame.hpp \ diff --git a/src/core/api/link_raw_api.cpp b/src/core/api/link_raw_api.cpp index 6ec0faa15..190aa9f29 100644 --- a/src/core/api/link_raw_api.cpp +++ b/src/core/api/link_raw_api.cpp @@ -49,489 +49,6 @@ using namespace ot; -LinkRaw::LinkRaw(Instance &aInstance) - : mInstance(aInstance) -#if OPENTHREAD_LINKRAW_TIMER_REQUIRED - , mTimer(aInstance, &LinkRaw::HandleTimer, this) - , mTimerReason(kTimerReasonNone) -#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER - , mTimerMicro(aInstance, &LinkRaw::HandleTimer, this) -#else - , mEnergyScanTimer(aInstance, &LinkRaw::HandleTimer, this) -#endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER -#endif // OPENTHREAD_LINKRAW_TIMER_REQUIRED -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - , mTransmitRetries(0) - , mCsmaBackoffs(0) -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - , mReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL) - , mReceiveDoneCallback(NULL) - , mTransmitDoneCallback(NULL) - , mEnergyScanDoneCallback(NULL) -{ - // Query the capabilities to check asserts - (void)GetCaps(); -} - -otError LinkRaw::SetEnabled(bool aEnabled) -{ - otError error = OT_ERROR_NONE; - - otLogInfoPlat(&mInstance, "LinkRaw Enabled=%d", aEnabled ? 1 : 0); - -#if OPENTHREAD_MTD || OPENTHREAD_FTD - VerifyOrExit(!static_cast(mInstance).GetThreadNetif().IsUp(), error = OT_ERROR_INVALID_STATE); -#endif // OPENTHREAD_MTD || OPENTHREAD_FTD - - if (aEnabled) - { - otPlatRadioEnable(&mInstance); - } - else - { - otPlatRadioDisable(&mInstance); - } - - mEnabled = aEnabled; - -#if OPENTHREAD_MTD || OPENTHREAD_FTD -exit: -#endif // OPENTHREAD_MTD || OPENTHREAD_FTD - return error; -} - -otError LinkRaw::SetPanId(uint16_t aPanId) -{ - otError error = OT_ERROR_NONE; - - VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); - - otPlatRadioSetPanId(&mInstance, aPanId); - mPanId = aPanId; - -exit: - return error; -} - -otError LinkRaw::SetChannel(uint8_t aChannel) -{ - otError error = OT_ERROR_NONE; - - VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); - - mReceiveChannel = aChannel; - -exit: - return error; -} - -otError LinkRaw::SetExtAddress(const otExtAddress &aExtAddress) -{ - otExtAddress addr; - otError error = OT_ERROR_NONE; - - VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); - - for (size_t i = 0; i < sizeof(addr); i++) - { - addr.m8[i] = aExtAddress.m8[7 - i]; - } - - otPlatRadioSetExtendedAddress(&mInstance, &addr); - mExtAddress = aExtAddress; - -exit: - return error; -} - -otError LinkRaw::SetShortAddress(uint16_t aShortAddress) -{ - otError error = OT_ERROR_NONE; - - VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); - - otPlatRadioSetShortAddress(&mInstance, aShortAddress); - mShortAddress = aShortAddress; - -exit: - return error; -} - -otRadioCaps LinkRaw::GetCaps(void) const -{ - otRadioCaps RadioCaps = otPlatRadioGetCaps(&mInstance); - - // The radio shouldn't support a capability if it is being compile - // time included into the raw link-layer code. - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - if ((RadioCaps & OT_RADIO_CAPS_ACK_TIMEOUT) == 0) - { - RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_ACK_TIMEOUT); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - if ((RadioCaps & OT_RADIO_CAPS_TRANSMIT_RETRIES) == 0) - { - RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_TRANSMIT_RETRIES); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - if ((RadioCaps & OT_RADIO_CAPS_CSMA_BACKOFF) == 0) - { - RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_CSMA_BACKOFF); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - if ((RadioCaps & OT_RADIO_CAPS_ENERGY_SCAN) == 0) - { - RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_ENERGY_SCAN); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - - return RadioCaps; -} - -otError LinkRaw::Receive(otLinkRawReceiveDone aCallback) -{ - otError error = OT_ERROR_INVALID_STATE; - - if (mEnabled) - { - mReceiveDoneCallback = aCallback; - error = otPlatRadioReceive(&mInstance, mReceiveChannel); - } - - return error; -} - -void LinkRaw::InvokeReceiveDone(otRadioFrame *aFrame, otError aError) -{ - if (mReceiveDoneCallback) - { - if (aError == OT_ERROR_NONE) - { - otLogInfoPlat(&mInstance, "LinkRaw Invoke Receive Done (%d bytes)", aFrame->mLength); - mReceiveDoneCallback(&mInstance, aFrame, aError); - } - else - { - otLogWarnPlat(&mInstance, "LinkRaw Invoke Receive Done (err=0x%x)", aError); - } - } -} - -otError LinkRaw::Transmit(otRadioFrame *aFrame, otLinkRawTransmitDone aCallback) -{ - otError error = OT_ERROR_INVALID_STATE; - - if (mEnabled) - { - mTransmitDoneCallback = aCallback; - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - mTransmitRetries = 0; - mCsmaBackoffs = 0; -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled) - { - // Start the transmission backoff logic - StartCsmaBackoff(); - error = OT_ERROR_NONE; - } - else - { -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - error = otPlatRadioTransmit(&mInstance, aFrame); -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - } - - return error; -} - -void LinkRaw::InvokeTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) -{ - otLogDebgPlat(&mInstance, "LinkRaw Transmit Done (err=0x%x)", aError); - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - mTimer.Stop(); -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - - if (aError == OT_ERROR_CHANNEL_ACCESS_FAILURE) - { - mCsmaBackoffs++; - - if (mCsmaBackoffs < aFrame->mInfo.mTxInfo.mMaxCsmaBackoffs) - { -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - StartCsmaBackoff(); -#else - // Start the transmit now - otError error = otPlatRadioTransmit(&mInstance, aFrame); - - if (error != OT_ERROR_NONE) - { - InvokeTransmitDone(aFrame, NULL, error); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - ExitNow(); - } - } - else - { - mCsmaBackoffs = 0; - } - - if (aError == OT_ERROR_NO_ACK) - { - if (mTransmitRetries < aFrame->mInfo.mTxInfo.mMaxFrameRetries) - { - mTransmitRetries++; -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - StartCsmaBackoff(); -#else - // Start the transmit now - otError error = otPlatRadioTransmit(&mInstance, aFrame); - - if (error != OT_ERROR_NONE) - { - InvokeTransmitDone(aFrame, NULL, error); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - ExitNow(); - } - } - -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT - - // Transition back to receive state on previous channel - otPlatRadioReceive(&mInstance, mReceiveChannel); - - if (mTransmitDoneCallback) - { - if (aError == OT_ERROR_NONE) - { - otLogInfoPlat(&mInstance, "LinkRaw Invoke Transmit Done"); - } - else - { - otLogWarnPlat(&mInstance, "LinkRaw Invoke Transmit Failed (err=0x%x)", aError); - } - - mTransmitDoneCallback(&mInstance, aFrame, aAckFrame, aError); - mTransmitDoneCallback = NULL; - } - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT -exit: - return; -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT -} - -otError LinkRaw::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration, otLinkRawEnergyScanDone aCallback) -{ - otError error = OT_ERROR_INVALID_STATE; - - if (mEnabled) - { - mEnergyScanDoneCallback = aCallback; - - if (otPlatRadioGetCaps(&mInstance) & OT_RADIO_CAPS_ENERGY_SCAN) - { - // Do the HW offloaded energy scan - error = otPlatRadioEnergyScan(&mInstance, aScanChannel, aScanDuration); - } -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - else - { - // Start listening on the scan channel - otPlatRadioReceive(&mInstance, aScanChannel); - - // Reset the RSSI value and start scanning - mEnergyScanRssi = kInvalidRssiValue; - mTimerReason = kTimerReasonEnergyScanComplete; -#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER - mTimerMicro.Start(0); -#else - mEnergyScanTimer.Start(0); -#endif - mTimer.Start(aScanDuration); - } -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - } - - return error; -} - -void LinkRaw::InvokeEnergyScanDone(int8_t aEnergyScanMaxRssi) -{ - if (IsEnabled() && mEnergyScanDoneCallback) - { - mEnergyScanDoneCallback(&mInstance, aEnergyScanMaxRssi); - mEnergyScanDoneCallback = NULL; - } -} - -void LinkRaw::TransmitStarted(otRadioFrame *aFrame) -{ -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - - // If we are implementing the ACK timeout logic, start a timer here (if ACK request) - // to fire if we don't get a transmit done callback in time. - if (static_cast(aFrame)->GetAckRequest() && - !(otPlatRadioGetCaps(&mInstance) & OT_RADIO_CAPS_ACK_TIMEOUT)) - { - otLogDebgPlat(&mInstance, "LinkRaw Starting AckTimeout Timer"); - mTimerReason = kTimerReasonAckTimeout; - mTimer.Start(Mac::kAckTimeout); - } - -#else - OT_UNUSED_VARIABLE(aFrame); -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT -} - -#if OPENTHREAD_LINKRAW_TIMER_REQUIRED - -void LinkRaw::HandleTimer(Timer &aTimer) -{ - LinkRaw &linkRaw = aTimer.GetOwner(); - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - // Energy scan uses a different timer for adding delay between RSSI samples. - if (&aTimer != &linkRaw.mTimer && linkRaw.mTimerReason == kTimerReasonEnergyScanComplete) - { - linkRaw.HandleEnergyScanTimer(); - } - else -#endif - { - linkRaw.HandleTimer(); - } -} - -void LinkRaw::HandleTimer(void) -{ - TimerReason timerReason = mTimerReason; - mTimerReason = kTimerReasonNone; - - switch (timerReason) - { -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - - case kTimerReasonAckTimeout: - { - // Transition back to receive state on previous channel - otPlatRadioReceive(&mInstance, mReceiveChannel); - - // Invoke completion callback for transmit - InvokeTransmitDone(otPlatRadioGetTransmitBuffer(&mInstance), NULL, OT_ERROR_NO_ACK); - break; - } - -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - - case kTimerReasonCsmaBackoffComplete: - { - otRadioFrame *aFrame = otPlatRadioGetTransmitBuffer(&mInstance); - - // Start the transmit now - otError error = otPlatRadioTransmit(&mInstance, aFrame); - - if (error != OT_ERROR_NONE) - { - InvokeTransmitDone(aFrame, NULL, error); - } - - break; - } - -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - - case kTimerReasonEnergyScanComplete: - { - // Invoke completion callback for the energy scan - InvokeEnergyScanDone(mEnergyScanRssi); - break; - } - -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - - default: - assert(false); - } -} - -#endif // OPENTHREAD_LINKRAW_TIMER_REQUIRED - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - -void LinkRaw::StartCsmaBackoff(void) -{ - uint32_t backoffExponent = Mac::kMinBE + mTransmitRetries + mCsmaBackoffs; - uint32_t backoff; - - if (backoffExponent > Mac::kMaxBE) - { - backoffExponent = Mac::kMaxBE; - } - - backoff = Random::GetUint32InRange(0, 1U << backoffExponent); - backoff *= (static_cast(Mac::kUnitBackoffPeriod) * OT_RADIO_SYMBOL_TIME); - - otLogDebgPlat(&mInstance, "LinkRaw Starting RetransmitTimeout Timer (%d ms)", backoff); - mTimerReason = kTimerReasonCsmaBackoffComplete; - -#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER - mTimerMicro.Start(backoff); -#else // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER - mTimer.Start(backoff / 1000UL); -#endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER -} - -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF - -#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - -void LinkRaw::HandleEnergyScanTimer(void) -{ - // Only process if we are still energy scanning - if (mTimer.IsRunning() && mTimerReason == kTimerReasonEnergyScanComplete) - { - int8_t rssi = otPlatRadioGetRssi(&mInstance); - - // Only apply the RSSI if it was a valid value - if (rssi != kInvalidRssiValue) - { - if ((mEnergyScanRssi == kInvalidRssiValue) || (rssi > mEnergyScanRssi)) - { - mEnergyScanRssi = rssi; - } - } - -#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER - mTimerMicro.Start(kEnergyScanRssiSampleInterval); -#else - mEnergyScanTimer.Start(kEnergyScanRssiSampleInterval); -#endif - } -} - -#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN - otError otLinkRawSetEnable(otInstance *aInstance, bool aEnabled) { return static_cast(aInstance)->GetLinkRaw().SetEnabled(aEnabled); @@ -719,30 +236,6 @@ exit: } #if OPENTHREAD_RADIO -void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) -{ - static_cast(aInstance)->GetLinkRaw().InvokeReceiveDone(aFrame, aError); -} - -void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) -{ - static_cast(aInstance)->GetLinkRaw().InvokeTransmitDone(aFrame, aAckFrame, aError); -} - -void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame) -{ - static_cast(aInstance)->GetLinkRaw().TransmitStarted(aFrame); -} - -void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi) -{ - VerifyOrExit(otInstanceIsInitialized(aInstance)); - - static_cast(aInstance)->GetLinkRaw().InvokeEnergyScanDone(aEnergyScanMaxRssi); - -exit: - return; -} otDeviceRole otThreadGetDeviceRole(otInstance *aInstance) { @@ -877,15 +370,6 @@ extern "C" void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame * } #endif // OPENTHREAD_ENABLE_DIAG -#if OPENTHREAD_CONFIG_HEADER_IE_SUPPORT -extern "C" void otPlatRadioFrameUpdated(otInstance *aInstance, otRadioFrame *aFrame) -{ - // Note: For now this functionality is not supported in Radio Only mode. - (void)aInstance; - (void)aFrame; -} -#endif - #endif // OPENTHREAD_RADIO #endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index a0fe62a1f..939958a6e 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -43,8 +43,8 @@ #include #if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API -#include "api/link_raw.hpp" #include "common/message.hpp" +#include "mac/link_raw.hpp" #endif #if OPENTHREAD_FTD || OPENTHREAD_MTD #include "coap/coap.hpp" diff --git a/src/core/mac/link_raw.cpp b/src/core/mac/link_raw.cpp new file mode 100644 index 000000000..064a9a525 --- /dev/null +++ b/src/core/mac/link_raw.cpp @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2016-2018, 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 + * This file implements the OpenThread Link Raw API. + */ + +#include "openthread-core-config.h" + +#include +#include +#include + +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/logging.hpp" +#include "common/owner-locator.hpp" +#include "common/random.hpp" +#include "mac/mac.hpp" +#include "utils/parse_cmdline.hpp" + +#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API + +namespace ot { + +LinkRaw::LinkRaw(Instance &aInstance) + : mInstance(aInstance) +#if OPENTHREAD_LINKRAW_TIMER_REQUIRED + , mTimer(aInstance, &LinkRaw::HandleTimer, this) + , mTimerReason(kTimerReasonNone) +#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + , mTimerMicro(aInstance, &LinkRaw::HandleTimer, this) +#else + , mEnergyScanTimer(aInstance, &LinkRaw::HandleTimer, this) +#endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER +#endif // OPENTHREAD_LINKRAW_TIMER_REQUIRED +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + , mTransmitRetries(0) + , mCsmaBackoffs(0) +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + , mReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL) + , mReceiveDoneCallback(NULL) + , mTransmitDoneCallback(NULL) + , mEnergyScanDoneCallback(NULL) +{ + // Query the capabilities to check asserts + (void)GetCaps(); +} + +otError LinkRaw::SetEnabled(bool aEnabled) +{ + otError error = OT_ERROR_NONE; + + otLogInfoPlat(&mInstance, "LinkRaw Enabled=%d", aEnabled ? 1 : 0); + +#if OPENTHREAD_MTD || OPENTHREAD_FTD + VerifyOrExit(!static_cast(mInstance).GetThreadNetif().IsUp(), error = OT_ERROR_INVALID_STATE); +#endif // OPENTHREAD_MTD || OPENTHREAD_FTD + + if (aEnabled) + { + otPlatRadioEnable(&mInstance); + } + else + { + otPlatRadioDisable(&mInstance); + } + + mEnabled = aEnabled; + +#if OPENTHREAD_MTD || OPENTHREAD_FTD +exit: +#endif // OPENTHREAD_MTD || OPENTHREAD_FTD + return error; +} + +otError LinkRaw::SetPanId(uint16_t aPanId) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); + + otPlatRadioSetPanId(&mInstance, aPanId); + mPanId = aPanId; + +exit: + return error; +} + +otError LinkRaw::SetChannel(uint8_t aChannel) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); + + mReceiveChannel = aChannel; + +exit: + return error; +} + +otError LinkRaw::SetExtAddress(const otExtAddress &aExtAddress) +{ + otExtAddress addr; + otError error = OT_ERROR_NONE; + + VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); + + for (size_t i = 0; i < sizeof(addr); i++) + { + addr.m8[i] = aExtAddress.m8[7 - i]; + } + + otPlatRadioSetExtendedAddress(&mInstance, &addr); + mExtAddress = aExtAddress; + +exit: + return error; +} + +otError LinkRaw::SetShortAddress(uint16_t aShortAddress) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(IsEnabled(), error = OT_ERROR_INVALID_STATE); + + otPlatRadioSetShortAddress(&mInstance, aShortAddress); + mShortAddress = aShortAddress; + +exit: + return error; +} + +otRadioCaps LinkRaw::GetCaps(void) const +{ + otRadioCaps RadioCaps = otPlatRadioGetCaps(&mInstance); + + // The radio shouldn't support a capability if it is being compile + // time included into the raw link-layer code. + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + if ((RadioCaps & OT_RADIO_CAPS_ACK_TIMEOUT) == 0) + { + RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_ACK_TIMEOUT); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + if ((RadioCaps & OT_RADIO_CAPS_TRANSMIT_RETRIES) == 0) + { + RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_TRANSMIT_RETRIES); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + if ((RadioCaps & OT_RADIO_CAPS_CSMA_BACKOFF) == 0) + { + RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_CSMA_BACKOFF); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + if ((RadioCaps & OT_RADIO_CAPS_ENERGY_SCAN) == 0) + { + RadioCaps = static_cast(RadioCaps | OT_RADIO_CAPS_ENERGY_SCAN); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + + return RadioCaps; +} + +otError LinkRaw::Receive(otLinkRawReceiveDone aCallback) +{ + otError error = OT_ERROR_INVALID_STATE; + + if (mEnabled) + { + mReceiveDoneCallback = aCallback; + error = otPlatRadioReceive(&mInstance, mReceiveChannel); + } + + return error; +} + +void LinkRaw::InvokeReceiveDone(otRadioFrame *aFrame, otError aError) +{ + if (mReceiveDoneCallback) + { + if (aError == OT_ERROR_NONE) + { + otLogInfoPlat(&mInstance, "LinkRaw Invoke Receive Done (%d bytes)", aFrame->mLength); + mReceiveDoneCallback(&mInstance, aFrame, aError); + } + else + { + otLogWarnPlat(&mInstance, "LinkRaw Invoke Receive Done (err=0x%x)", aError); + } + } +} + +otError LinkRaw::Transmit(otRadioFrame *aFrame, otLinkRawTransmitDone aCallback) +{ + otError error = OT_ERROR_INVALID_STATE; + + if (mEnabled) + { + mTransmitDoneCallback = aCallback; + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + mTransmitRetries = 0; + mCsmaBackoffs = 0; +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled) + { + // Start the transmission backoff logic + StartCsmaBackoff(); + error = OT_ERROR_NONE; + } + else + { +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + error = otPlatRadioTransmit(&mInstance, aFrame); +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + } + + return error; +} + +void LinkRaw::InvokeTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) +{ + otLogDebgPlat(&mInstance, "LinkRaw Transmit Done (err=0x%x)", aError); + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + mTimer.Stop(); +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + + if (aError == OT_ERROR_CHANNEL_ACCESS_FAILURE) + { + mCsmaBackoffs++; + + if (mCsmaBackoffs < aFrame->mInfo.mTxInfo.mMaxCsmaBackoffs) + { +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + StartCsmaBackoff(); +#else + // Start the transmit now + otError error = otPlatRadioTransmit(&mInstance, aFrame); + + if (error != OT_ERROR_NONE) + { + InvokeTransmitDone(aFrame, NULL, error); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + ExitNow(); + } + } + else + { + mCsmaBackoffs = 0; + } + + if (aError == OT_ERROR_NO_ACK) + { + if (mTransmitRetries < aFrame->mInfo.mTxInfo.mMaxFrameRetries) + { + mTransmitRetries++; +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + StartCsmaBackoff(); +#else + // Start the transmit now + otError error = otPlatRadioTransmit(&mInstance, aFrame); + + if (error != OT_ERROR_NONE) + { + InvokeTransmitDone(aFrame, NULL, error); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + ExitNow(); + } + } + +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT + + // Transition back to receive state on previous channel + otPlatRadioReceive(&mInstance, mReceiveChannel); + + if (mTransmitDoneCallback) + { + if (aError == OT_ERROR_NONE) + { + otLogInfoPlat(&mInstance, "LinkRaw Invoke Transmit Done"); + } + else + { + otLogWarnPlat(&mInstance, "LinkRaw Invoke Transmit Failed (err=0x%x)", aError); + } + + mTransmitDoneCallback(&mInstance, aFrame, aAckFrame, aError); + mTransmitDoneCallback = NULL; + } + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT +exit: + return; +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_RETRANSMIT +} + +otError LinkRaw::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration, otLinkRawEnergyScanDone aCallback) +{ + otError error = OT_ERROR_INVALID_STATE; + + if (mEnabled) + { + mEnergyScanDoneCallback = aCallback; + + if (otPlatRadioGetCaps(&mInstance) & OT_RADIO_CAPS_ENERGY_SCAN) + { + // Do the HW offloaded energy scan + error = otPlatRadioEnergyScan(&mInstance, aScanChannel, aScanDuration); + } +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + else + { + // Start listening on the scan channel + otPlatRadioReceive(&mInstance, aScanChannel); + + // Reset the RSSI value and start scanning + mEnergyScanRssi = kInvalidRssiValue; + mTimerReason = kTimerReasonEnergyScanComplete; +#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + mTimerMicro.Start(0); +#else + mEnergyScanTimer.Start(0); +#endif + mTimer.Start(aScanDuration); + } +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + } + + return error; +} + +void LinkRaw::InvokeEnergyScanDone(int8_t aEnergyScanMaxRssi) +{ + if (IsEnabled() && mEnergyScanDoneCallback) + { + mEnergyScanDoneCallback(&mInstance, aEnergyScanMaxRssi); + mEnergyScanDoneCallback = NULL; + } +} + +void LinkRaw::TransmitStarted(otRadioFrame *aFrame) +{ +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + + // If we are implementing the ACK timeout logic, start a timer here (if ACK request) + // to fire if we don't get a transmit done callback in time. + if (static_cast(aFrame)->GetAckRequest() && + !(otPlatRadioGetCaps(&mInstance) & OT_RADIO_CAPS_ACK_TIMEOUT)) + { + otLogDebgPlat(&mInstance, "LinkRaw Starting AckTimeout Timer"); + mTimerReason = kTimerReasonAckTimeout; + mTimer.Start(Mac::kAckTimeout); + } + +#else + OT_UNUSED_VARIABLE(aFrame); +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT +} + +#if OPENTHREAD_LINKRAW_TIMER_REQUIRED + +void LinkRaw::HandleTimer(Timer &aTimer) +{ + LinkRaw &linkRaw = aTimer.GetOwner(); + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + // Energy scan uses a different timer for adding delay between RSSI samples. + if (&aTimer != &linkRaw.mTimer && linkRaw.mTimerReason == kTimerReasonEnergyScanComplete) + { + linkRaw.HandleEnergyScanTimer(); + } + else +#endif + { + linkRaw.HandleTimer(); + } +} + +void LinkRaw::HandleTimer(void) +{ + TimerReason timerReason = mTimerReason; + mTimerReason = kTimerReasonNone; + + switch (timerReason) + { +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + + case kTimerReasonAckTimeout: + { + // Transition back to receive state on previous channel + otPlatRadioReceive(&mInstance, mReceiveChannel); + + // Invoke completion callback for transmit + InvokeTransmitDone(otPlatRadioGetTransmitBuffer(&mInstance), NULL, OT_ERROR_NO_ACK); + break; + } + +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ACK_TIMEOUT + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + + case kTimerReasonCsmaBackoffComplete: + { + otRadioFrame *aFrame = otPlatRadioGetTransmitBuffer(&mInstance); + + // Start the transmit now + otError error = otPlatRadioTransmit(&mInstance, aFrame); + + if (error != OT_ERROR_NONE) + { + InvokeTransmitDone(aFrame, NULL, error); + } + + break; + } + +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + + case kTimerReasonEnergyScanComplete: + { + // Invoke completion callback for the energy scan + InvokeEnergyScanDone(mEnergyScanRssi); + break; + } + +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + + default: + assert(false); + } +} + +#endif // OPENTHREAD_LINKRAW_TIMER_REQUIRED + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + +void LinkRaw::StartCsmaBackoff(void) +{ + uint32_t backoffExponent = Mac::kMinBE + mTransmitRetries + mCsmaBackoffs; + uint32_t backoff; + + if (backoffExponent > Mac::kMaxBE) + { + backoffExponent = Mac::kMaxBE; + } + + backoff = Random::GetUint32InRange(0, 1U << backoffExponent); + backoff *= (static_cast(Mac::kUnitBackoffPeriod) * OT_RADIO_SYMBOL_TIME); + + otLogDebgPlat(&mInstance, "LinkRaw Starting RetransmitTimeout Timer (%d ms)", backoff); + mTimerReason = kTimerReasonCsmaBackoffComplete; + +#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + mTimerMicro.Start(backoff); +#else // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + mTimer.Start(backoff / 1000UL); +#endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER +} + +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_CSMA_BACKOFF + +#if OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + +void LinkRaw::HandleEnergyScanTimer(void) +{ + // Only process if we are still energy scanning + if (mTimer.IsRunning() && mTimerReason == kTimerReasonEnergyScanComplete) + { + int8_t rssi = otPlatRadioGetRssi(&mInstance); + + // Only apply the RSSI if it was a valid value + if (rssi != kInvalidRssiValue) + { + if ((mEnergyScanRssi == kInvalidRssiValue) || (rssi > mEnergyScanRssi)) + { + mEnergyScanRssi = rssi; + } + } + +#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER + mTimerMicro.Start(kEnergyScanRssiSampleInterval); +#else + mEnergyScanTimer.Start(kEnergyScanRssiSampleInterval); +#endif + } +} + +#endif // OPENTHREAD_CONFIG_ENABLE_SOFTWARE_ENERGY_SCAN + +#if OPENTHREAD_RADIO +extern "C" void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + static_cast(aInstance)->GetLinkRaw().InvokeReceiveDone(aFrame, aError); +} + +extern "C" void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) +{ + static_cast(aInstance)->GetLinkRaw().InvokeTransmitDone(aFrame, aAckFrame, aError); +} + +extern "C" void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame) +{ + static_cast(aInstance)->GetLinkRaw().TransmitStarted(aFrame); +} + +extern "C" void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi) +{ + VerifyOrExit(otInstanceIsInitialized(aInstance)); + + static_cast(aInstance)->GetLinkRaw().InvokeEnergyScanDone(aEnergyScanMaxRssi); + +exit: + return; +} + +#if OPENTHREAD_CONFIG_HEADER_IE_SUPPORT +extern "C" void otPlatRadioFrameUpdated(otInstance *aInstance, otRadioFrame *aFrame) +{ + // Note: For now this functionality is not supported in Radio Only mode. + (void)aInstance; + (void)aFrame; +} +#endif + +#endif // OPENTHREAD_RADIO + +} // namespace ot + +#endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API diff --git a/src/core/api/link_raw.hpp b/src/core/mac/link_raw.hpp similarity index 100% rename from src/core/api/link_raw.hpp rename to src/core/mac/link_raw.hpp