[mac] wake-up frame sequence transmission (#10771)

If the Wake-up Coordinator role is enabled:
1. Add an API to attempt to attach a Wake-up End Device.  For now,
   this new API starts a wake-up frame sequence to the WED, but all
   the remaining MLE changes will be provided in the upcoming PRs.
2. Add "wakeup wake" shell command.

Note:
This commit enables testing the wake-up feature without the need to
make any changes in the radio layer by implementing the wake-up frame
scheduling in the core.

In real products with the Wake-up Coordinator capability, this should
likely be offloaded to the radio layer to assure a reliable wake-up
despite of the high rate of the wake-up frames.
This commit is contained in:
Damian Królik
2024-11-19 08:23:37 -08:00
committed by GitHub
parent 9277bce08e
commit 10d8b68f23
19 changed files with 628 additions and 4 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (463)
#define OPENTHREAD_API_VERSION (464)
/**
* @addtogroup api-instance
+40
View File
@@ -222,6 +222,16 @@ typedef struct otThreadParentResponseInfo
*/
typedef void (*otDetachGracefullyCallback)(void *aContext);
/**
* Informs the application about the result of waking a Wake-up End Device.
*
* @param[in] aError OT_ERROR_NONE Indicates that the Wake-up End Device has been added as a neighbor.
* OT_ERROR_FAILED Indicates that the Wake-up End Device has not received a wake-up frame, or it
* has failed the MLE procedure.
* @param[in] aContext A pointer to application-specific context.
*/
typedef void (*otWakeupCallback)(otError aError, void *aContext);
/**
* Starts Thread protocol operation.
*
@@ -1115,6 +1125,36 @@ void otThreadSetStoreFrameCounterAhead(otInstance *aInstance, uint32_t aStoreFra
*/
uint32_t otThreadGetStoreFrameCounterAhead(otInstance *aInstance);
/**
* Attempts to wake a Wake-up End Device.
*
* Requires `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` to be enabled.
*
* The wake-up starts with transmitting a wake-up frame sequence to the Wake-up End Device.
* During the wake-up sequence, and for a short time after the last wake-up frame is sent, the Wake-up Coordinator keeps
* its receiver on to be able to receive an initial mesh link establishment message from the WED.
*
* @warning The functionality implemented by this function is still in the design phase.
* Consequently, the prototype and semantics of this function are subject to change.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aWedAddress The extended address of the Wake-up End Device.
* @param[in] aWakeupIntervalUs An interval between consecutive wake-up frames (in microseconds).
* @param[in] aWakeupDurationMs Duration of the wake-up sequence (in milliseconds).
* @param[in] aCallback A pointer to function that is called when the wake-up succeeds or fails.
* @param[in] aContext A pointer to callback application-specific context.
*
* @retval OT_ERROR_NONE Successfully started the wake-up.
* @retval OT_ERROR_INVALID_STATE Another attachment request is still in progress.
* @retval OT_ERROR_INVALID_ARGS The wake-up interval or duration are invalid.
*/
otError otThreadWakeup(otInstance *aInstance,
const otExtAddress *aWedAddress,
uint16_t aWakeupIntervalUs,
uint16_t aWakeupDurationMs,
otWakeupCallback aCallback,
void *aCallbackContext);
/**
* @}
*/
+11
View File
@@ -4482,3 +4482,14 @@ Enable/disable listening for wake-up frames.
> wakeup listen enable
Done
```
### wakeup wake \<extaddr\> \<wakeup-interval\> \<wakeup-duration\>
Wakes a Wake-up End Device.
`OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` is required.
```bash
> wakeup wake 1ece0a6c4653a7c1 7500 1090
Done
```
+37 -1
View File
@@ -8319,6 +8319,33 @@ template <> otError Interpreter::Process<Cmd("wakeup")>(Arg aArgs[])
error = ProcessEnableDisable(aArgs + 1, otLinkIsWakeupListenEnabled, otLinkSetWakeUpListenEnabled);
}
#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
/**
* @cli wakeup wake
* @code
* wakeup wake 1ece0a6c4653a7c1 7500 1090
* Done
* @endcode
* @cparam wakeup wake @ca{extaddr} @ca{wakeup-interval} @ca{wakeup-duration}
* @par
* Wakes a Wake-up End Device identified by its MAC extended address, using the provided wake-up interval (in the
* units of microseconds), and wake-up duration (in the units of milliseconds).
*/
else if (aArgs[0] == "wake")
{
otExtAddress extAddress;
uint16_t wakeupIntervalUs;
uint16_t wakeupDurationMs;
SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddress.m8));
SuccessOrExit(error = aArgs[2].ParseAsUint16(wakeupIntervalUs));
SuccessOrExit(error = aArgs[3].ParseAsUint16(wakeupDurationMs));
SuccessOrExit(error = otThreadWakeup(GetInstancePtr(), &extAddress, wakeupIntervalUs, wakeupDurationMs,
HandleWakeupResult, this));
error = OT_ERROR_PENDING;
}
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
@@ -8329,6 +8356,15 @@ exit:
}
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
void Interpreter::HandleWakeupResult(otError aError, void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleWakeupResult(aError);
}
void Interpreter::HandleWakeupResult(otError aError) { OutputResult(aError); }
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
@@ -8642,7 +8678,7 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
CmdEntry("wakeup"),
#endif
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
};
#undef CmdEntry
+5
View File
@@ -329,6 +329,11 @@ private:
static void HandleIp6Receive(otMessage *aMessage, void *aContext);
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
static void HandleWakeupResult(otError aError, void *aContext);
void HandleWakeupResult(otError aError);
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
#if OPENTHREAD_CONFIG_DIAG_ENABLE
+2
View File
@@ -509,6 +509,8 @@ openthread_core_files = [
"mac/sub_mac_callbacks.cpp",
"mac/sub_mac_csl_receiver.cpp",
"mac/sub_mac_wed.cpp",
"mac/wakeup_tx_scheduler.cpp",
"mac/wakeup_tx_scheduler.hpp",
"meshcop/announce_begin_client.cpp",
"meshcop/announce_begin_client.hpp",
"meshcop/border_agent.cpp",
+1
View File
@@ -147,6 +147,7 @@ set(COMMON_SOURCES
mac/sub_mac_callbacks.cpp
mac/sub_mac_csl_receiver.cpp
mac/sub_mac_wed.cpp
mac/wakeup_tx_scheduler.cpp
meshcop/announce_begin_client.cpp
meshcop/border_agent.cpp
meshcop/commissioner.cpp
+13
View File
@@ -516,6 +516,19 @@ uint32_t otThreadGetStoreFrameCounterAhead(otInstance *aInstance)
}
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
otError otThreadWakeup(otInstance *aInstance,
const otExtAddress *aWedAddress,
uint16_t aWakeupIntervalUs,
uint16_t aWakeupDurationMs,
otWakeupCallback aCallback,
void *aCallbackContext)
{
return AsCoreType(aInstance).Get<Mle::Mle>().Wakeup(AsCoreType(aWedAddress), aWakeupIntervalUs, aWakeupDurationMs,
aCallback, aCallbackContext);
}
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
#if OPENTHREAD_CONFIG_UPTIME_ENABLE
+26
View File
@@ -53,6 +53,32 @@
#define OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_INTERVAL
*
* The Connection Retry Interval is included in the Connection IE of each wake-up frame sent by the Wake-up Coordinator
* to a Wake-up End Device.
*
* This value defines how frequently the Wake-up End Device should retry sending the initial MLE message to the Wake-up
* Parent after receiving a wake-up frame, in the units of Wake-up Intervals (7.5ms by default).
*/
#ifndef OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_INTERVAL
#define OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_INTERVAL 1
#endif
/**
* @def OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_COUNT
*
* The Connection Retry Count is included in the Connection IE of each wake-up frame sent by the Wake-up Coordinator to
* a Wake-up End Device.
*
* This value defines how many times the Wake-up End Device should retry sending the initial MLE message to the Wake-up
* Parent after receiving a wake-up frame.
*/
#ifndef OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_COUNT
#define OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_COUNT 12
#endif
/**
* @def OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
*
+5
View File
@@ -80,6 +80,7 @@
#include "common/settings.hpp"
#include "crypto/mbedtls.hpp"
#include "mac/mac.hpp"
#include "mac/wakeup_tx_scheduler.hpp"
#include "meshcop/border_agent.hpp"
#include "meshcop/commissioner.hpp"
#include "meshcop/dataset_manager.hpp"
@@ -788,6 +789,10 @@ template <> inline ChildTable &Instance::Get(void) { return mMleRouter.mChildTab
template <> inline RouterTable &Instance::Get(void) { return mMleRouter.mRouterTable; }
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
template <> inline WakeupTxScheduler &Instance::Get(void) { return mMleRouter.mWakeupTxScheduler; }
#endif
template <> inline Ip6::Netif &Instance::Get(void) { return mThreadNetif; }
template <> inline ThreadNetif &Instance::Get(void) { return mThreadNetif; }
+52 -1
View File
@@ -206,6 +206,9 @@ bool Mac::IsInTransmitState(void) const
#endif
case kOperationTransmitBeacon:
case kOperationTransmitPoll:
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
case kOperationTransmitWakeup:
#endif
retval = true;
break;
@@ -521,6 +524,17 @@ exit:
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
void Mac::RequestWakeupFrameTransmission(void)
{
VerifyOrExit(IsEnabled());
StartOperation(kOperationTransmitWakeup);
exit:
return;
}
#endif
Error Mac::RequestDataPollTransmission(void)
{
Error error = kErrorNone;
@@ -641,6 +655,12 @@ void Mac::PerformNextOperation(void)
{
mOperation = kOperationWaitingForData;
}
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
else if (IsPending(kOperationTransmitWakeup))
{
mOperation = kOperationTransmitWakeup;
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
else if (IsPending(kOperationTransmitDataCsl) && TimerMilli::GetNow() >= mCslTxFireTime)
{
@@ -712,6 +732,9 @@ void Mac::PerformNextOperation(void)
#endif
#endif
case kOperationTransmitPoll:
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
case kOperationTransmitWakeup:
#endif
BeginTransmit();
break;
@@ -901,8 +924,17 @@ void Mac::ProcessTransmitSecurity(TxFrame &aFrame)
case Frame::kKeyIdMode2:
{
const uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff};
uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff};
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
if (aFrame.IsWakeupFrame())
{
// Just set the key source here, further security processing will happen in SubMac
BigEndian::WriteUint32(keyManager.GetCurrentKeySequence(), keySource);
aFrame.SetKeySource(keySource);
ExitNow();
}
#endif
aFrame.SetAesKey(mMode2KeyMaterial);
mKeyIdMode2FrameCounter++;
@@ -1023,6 +1055,15 @@ void Mac::BeginTransmit(void)
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
case kOperationTransmitWakeup:
frame = Get<WakeupTxScheduler>().PrepareWakeupFrame(txFrames);
VerifyOrExit(frame != nullptr);
frame->SetChannel(mWakeupChannel);
frame->SetRxChannelAfterTxDone(mRadioChannel);
break;
#endif
default:
OT_ASSERT(false);
}
@@ -1465,6 +1506,13 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
break;
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
case kOperationTransmitWakeup:
FinishOperation();
PerformNextOperation();
break;
#endif
default:
OT_ASSERT(false);
}
@@ -2237,6 +2285,9 @@ const char *Mac::OperationToString(Operation aOperation)
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
"TransmitDataCsl", // (8) kOperationTransmitDataCsl
#endif
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
"TransmitWakeup", // kOperationTransmitWakeup
#endif
};
+10
View File
@@ -213,6 +213,13 @@ public:
void RequestCslFrameTransmission(uint32_t aDelay);
#endif
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
/**
* Requests `Mac` to start a wake-up frame transmission.
*/
void RequestWakeupFrameTransmission(void);
#endif
/**
@@ -776,6 +783,9 @@ private:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
kOperationTransmitDataCsl,
#endif
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
kOperationTransmitWakeup,
#endif
};
+11 -1
View File
@@ -373,7 +373,17 @@ void SubMac::ProcessTransmitSecurity(void)
}
VerifyOrExit(ShouldHandleTransmitSecurity());
VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
if (mTransmitFrame.GetType() == Frame::kTypeMultipurpose)
{
VerifyOrExit(keyIdMode == Frame::kKeyIdMode2);
}
else
#endif
{
VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
}
mTransmitFrame.SetAesKey(GetCurrentMacKey());
+170
View File
@@ -0,0 +1,170 @@
/*
* Copyright (c) 2024, 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.
*/
#include "wakeup_tx_scheduler.hpp"
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
#include "common/code_utils.hpp"
#include "common/log.hpp"
#include "common/num_utils.hpp"
#include "common/time.hpp"
#include "core/instance/instance.hpp"
#include "radio/radio.hpp"
namespace ot {
RegisterLogModule("WakeupTxSched");
WakeupTxScheduler::WakeupTxScheduler(Instance &aInstance)
: InstanceLocator(aInstance)
, mTxTimeUs(0)
, mTxEndTimeUs(0)
, mTimer(aInstance)
, mIsRunning(false)
{
UpdateFrameRequestAhead();
}
Error WakeupTxScheduler::WakeUp(const Mac::ExtAddress &aWedAddress, uint16_t aIntervalUs, uint16_t aDurationMs)
{
Error error = kErrorNone;
VerifyOrExit(!mIsRunning, error = kErrorInvalidState);
mWedAddress = aWedAddress;
mTxTimeUs = TimerMicro::GetNow() + mTxRequestAheadTimeUs;
mTxEndTimeUs = mTxTimeUs + aDurationMs * Time::kOneMsecInUsec + aIntervalUs;
mIntervalUs = aIntervalUs;
mIsRunning = true;
LogInfo("Started wake-up sequence to %s", aWedAddress.ToString().AsCString());
ScheduleTimer();
exit:
return error;
}
void WakeupTxScheduler::RequestWakeupFrameTransmission(void) { Get<Mac::Mac>().RequestWakeupFrameTransmission(); }
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames)
{
Mac::TxFrame *frame = nullptr;
Mac::Address target;
Mac::Address source;
uint32_t radioTxUs;
uint32_t rendezvousTimeUs;
Mac::ConnectionIe *connectionIe;
VerifyOrExit(mIsRunning);
target.SetExtended(mWedAddress);
source.SetExtended(Get<Mac::Mac>().GetExtAddress());
radioTxUs = static_cast<uint32_t>(Get<Radio>().GetNow()) + (mTxTimeUs - TimerMicro::GetNow());
#if OPENTHREAD_CONFIG_MULTI_RADIO
frame = &aTxFrames.GetTxFrame(Mac::kRadioTypeIeee802154);
#else
frame = &aTxFrames.GetTxFrame();
#endif
VerifyOrExit(frame->GenerateWakeupFrame(Get<Mac::Mac>().GetPanId(), target, source) == kErrorNone, frame = nullptr);
frame->SetTxDelayBaseTime(0);
frame->SetTxDelay(radioTxUs);
frame->SetCsmaCaEnabled(false);
frame->SetMaxCsmaBackoffs(0);
frame->SetMaxFrameRetries(0);
// Rendezvous Time is the time between the end of a wake-up frame and the start of the first payload frame.
// For the n-th wake-up frame, set the Rendezvous Time so that the expected reception of a Parent Request happens in
// the "free space" between the "n+1"-th and "n+2"-th wake-up frame.
rendezvousTimeUs = mIntervalUs;
rendezvousTimeUs += (mIntervalUs - (kWakeupFrameLength + kParentRequestLength) * kOctetDuration) / 2;
frame->GetRendezvousTimeIe()->SetRendezvousTime(rendezvousTimeUs / kUsPerTenSymbols);
connectionIe = frame->GetConnectionIe();
connectionIe->SetRetryInterval(kConnectionRetryInterval);
connectionIe->SetRetryCount(kConnectionRetryCount);
// Advance to the time of the next wake-up frame.
mTxTimeUs = Max(mTxTimeUs + mIntervalUs, TimerMicro::GetNow() + mTxRequestAheadTimeUs);
// Schedule the next timer right away before waiting for the transmission completion
// to keep up with the high rate of wake-up frames in the RCP architecture.
ScheduleTimer();
exit:
return frame;
}
#else // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &) { return nullptr; }
#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
void WakeupTxScheduler::ScheduleTimer(void)
{
if (mTxTimeUs >= mTxEndTimeUs)
{
mIsRunning = false;
LogInfo("Stopped wake-up sequence");
ExitNow();
}
mTimer.FireAt(mTxTimeUs - mTxRequestAheadTimeUs);
exit:
return;
}
void WakeupTxScheduler::Stop(void)
{
mIsRunning = false;
mTimer.Stop();
}
void WakeupTxScheduler::UpdateFrameRequestAhead(void)
{
// A rough estimate of the size of data that has to be exchanged with the radio to schedule a wake-up frame TX.
// This is used to make sure that a wake-up frame is received by the radio early enough to be transmitted on time.
constexpr uint32_t kWakeupFrameWeight = 100;
uint32_t busSpeedHz = otPlatRadioGetBusSpeed(&GetInstance());
uint32_t busLatency = otPlatRadioGetBusLatency(&GetInstance());
uint32_t busTxTimeUs = ((busSpeedHz == 0) ? 0 : (kWakeupFrameWeight * 8 * 1000000 + busSpeedHz - 1) / busSpeedHz);
mTxRequestAheadTimeUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs + busLatency;
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2024, 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.
*/
#ifndef WAKEUP_TX_SCHEDULER_HPP_
#define WAKEUP_TX_SCHEDULER_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/timer.hpp"
#include "mac/mac.hpp"
namespace ot {
class Child;
/**
* Implements wake-up sequence TX scheduling functionality.
*/
class WakeupTxScheduler : public InstanceLocator, private NonCopyable
{
friend class Mac::Mac;
public:
/**
* Initializes the wake-up sequence TX scheduler object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*/
explicit WakeupTxScheduler(Instance &aInstance);
/**
* Initiates the wake-up sequence to a Wake-up End Device.
*
* @param[in] aWedAddress The extended address of the Wake-up End Device.
* @param[in] aIntervalUs An interval between consecutive wake-up frames (in microseconds).
* @param[in] aDurationMs Duration of the wake-up sequence (in milliseconds).
*
* @retval kErrorNone Successfully started the wake-up sequence.
* @retval kErrorInvalidState This or another device is currently being woken-up.
*/
Error WakeUp(const Mac::ExtAddress &aWedAddress, uint16_t aIntervalUs, uint16_t aDurationMs);
/**
* Returns the connection window used by this device.
*
* The connection window is amount of time that this device waits for an initial link establishment message after
* sending the last wake-up frame.
*
* @returns Connection window in the units of microseconds.
*/
uint32_t GetConnectionWindowUs(void) const
{
return mIntervalUs * kConnectionRetryInterval * kConnectionRetryCount;
}
/**
* Returns the end of the wake-up sequence time.
*
* @returns End of the wake-up sequence time.
*/
TimeMicro GetTxEndTime(void) const { return mTxEndTimeUs; }
/**
* Stops the ongoing wake-up sequence.
*/
void Stop(void);
/**
* Updates the value of `mTxRequestAheadTimeUs`, based on bus speed, bus latency and
* `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US`.
*/
void UpdateFrameRequestAhead(void);
private:
constexpr static uint8_t kConnectionRetryInterval = OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_INTERVAL;
constexpr static uint8_t kConnectionRetryCount = OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_CONNECTION_RETRY_COUNT;
constexpr static uint32_t kWakeupFrameLength = 54; // Includes SHR
constexpr static uint32_t kParentRequestLength = 78; // Includes SHR
// Called by the MAC layer when a wake-up frame transmission is about to be started.
Mac::TxFrame *PrepareWakeupFrame(Mac::TxFrames &aTxFrames);
// Called at the beginning of a wake-up sequence and right after a wake-up frame has been prepared for transmission.
void ScheduleTimer(void);
void RequestWakeupFrameTransmission(void);
using WakeupTimer = TimerMicroIn<WakeupTxScheduler, &WakeupTxScheduler::RequestWakeupFrameTransmission>;
Mac::ExtAddress mWedAddress;
TimeMicro mTxTimeUs; // Point in time when the next TX occurs.
TimeMicro mTxEndTimeUs; // Point in time when the wake-up sequence is over.
uint16_t mTxRequestAheadTimeUs; // How much ahead the TX MAC operation needs to be requested.
uint16_t mIntervalUs; // Interval between consecutive wake-up frames.
WakeupTimer mTimer;
bool mIsRunning;
};
} // namespace ot
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
#endif // WAKEUP_TX_SCHEDULER_HPP_
+3
View File
@@ -62,6 +62,9 @@ void Radio::Callbacks::HandleBusLatencyChanged(void)
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
Get<CslTxScheduler>().UpdateFrameRequestAhead();
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
Get<WakeupTxScheduler>().UpdateFrameRequestAhead();
#endif
}
#if OPENTHREAD_CONFIG_DIAG_ENABLE
+53
View File
@@ -86,6 +86,11 @@ Mle::Mle(Instance &aInstance)
#endif
, mAttachTimer(aInstance)
, mMessageTransmissionTimer(aInstance)
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
, mWakeupTxScheduler(aInstance)
, mWedAttachState(kWedDetached)
, mWedAttachTimer(aInstance)
#endif
{
mParent.Init(aInstance);
mParentCandidate.Init(aInstance);
@@ -4351,6 +4356,54 @@ uint64_t Mle::CalcParentCslMetric(const Mac::CslAccuracy &aCslAccuracy) const
}
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
void Mle::HandleWedAttachTimer(void)
{
switch (mWedAttachState)
{
case kWedAttaching:
// Connection timeout
if (!IsRxOnWhenIdle())
{
Get<MeshForwarder>().SetRxOnWhenIdle(false);
}
LogInfo("Connection window closed");
mWedAttachState = kWedDetached;
mWakeupCallback.InvokeAndClearIfSet(kErrorFailed);
break;
default:
break;
}
}
Error Mle::Wakeup(const Mac::ExtAddress &aWedAddress,
uint16_t aIntervalUs,
uint16_t aDurationMs,
WakeupCallback aCallback,
void *aCallbackContext)
{
Error error;
VerifyOrExit((aIntervalUs > 0) && (aDurationMs > 0), error = kErrorInvalidArgs);
VerifyOrExit(aIntervalUs < aDurationMs * Time::kOneMsecInUsec, error = kErrorInvalidArgs);
VerifyOrExit(mWedAttachState == kWedDetached, error = kErrorInvalidState);
SuccessOrExit(error = mWakeupTxScheduler.WakeUp(aWedAddress, aIntervalUs, aDurationMs));
mWedAttachState = kWedAttaching;
mWakeupCallback.Set(aCallback, aCallbackContext);
Get<MeshForwarder>().SetRxOnWhenIdle(true);
mWedAttachTimer.FireAt(mWakeupTxScheduler.GetTxEndTime() + mWakeupTxScheduler.GetConnectionWindowUs());
LogInfo("Connection window open");
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
Error Mle::DetachGracefully(DetachCallback aCallback, void *aContext)
{
Error error = kErrorNone;
+47
View File
@@ -45,6 +45,7 @@
#include "common/timer.hpp"
#include "crypto/aes_ccm.hpp"
#include "mac/mac.hpp"
#include "mac/wakeup_tx_scheduler.hpp"
#include "meshcop/dataset.hpp"
#include "meshcop/joiner_router.hpp"
#include "meshcop/meshcop.hpp"
@@ -120,6 +121,8 @@ class Mle : public InstanceLocator, private NonCopyable
public:
typedef otDetachGracefullyCallback DetachCallback; ///< Callback to signal end of graceful detach.
typedef otWakeupCallback WakeupCallback; ///< Callback to communicate the result of waking a Wake-up End Device
/**
* Initializes the MLE object.
*
@@ -726,6 +729,27 @@ public:
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
/**
* Attempts to wake a Wake-up End Device.
*
* @param[in] aWedAddress The extended address of the Wake-up End Device.
* @param[in] aIntervalUs An interval between consecutive wake-up frames (in microseconds).
* @param[in] aDurationMs Duration of the wake-up sequence (in milliseconds).
* @param[in] aCallback A pointer to function that is called when the wake-up succeeds or fails.
* @param[in] aContext A pointer to callback application-specific context.
*
* @retval kErrorNone Successfully started the wake-up.
* @retval kErrorInvalidState Another wake-up request is still in progress.
* @retval kErrorInvalidArgs The wake-up interval or duration are invalid.
*/
Error Wakeup(const Mac::ExtAddress &aWedAddress,
uint16_t aIntervalUs,
uint16_t aDurationMs,
WakeupCallback aCallback,
void *aCallbackContext);
#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
private:
//------------------------------------------------------------------------------------------------------------------
// Constants
@@ -924,6 +948,15 @@ private:
#endif
};
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
enum WedAttachState : uint8_t{
kWedDetached,
kWedAttaching,
kWedAttached,
kWedDetaching,
};
#endif
//------------------------------------------------------------------------------------------------------------------
// Nested types
@@ -1374,6 +1407,10 @@ private:
static void Log(MessageAction, MessageType, const Ip6::Address &, uint16_t) {}
#endif
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
void HandleWedAttachTimer(void);
#endif
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
static const char *AttachModeToString(AttachMode aMode);
static const char *AttachStateToString(AttachState aState);
@@ -1398,6 +1435,9 @@ private:
using AttachTimer = TimerMilliIn<Mle, &Mle::HandleAttachTimer>;
using MsgTxTimer = TimerMilliIn<Mle, &Mle::HandleMessageTransmissionTimer>;
using MleSocket = Ip6::Udp::SocketIn<Mle, &Mle::HandleUdpReceive>;
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
using WedAttachTimer = TimerMicroIn<Mle, &Mle::HandleWedAttachTimer>;
#endif
static const otMeshLocalPrefix kMeshLocalPrefixInit;
@@ -1464,6 +1504,13 @@ private:
Ip6::Netif::UnicastAddress mMeshLocalRloc;
Ip6::Netif::MulticastAddress mLinkLocalAllThreadNodes;
Ip6::Netif::MulticastAddress mRealmLocalAllThreadNodes;
#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
WakeupTxScheduler mWakeupTxScheduler;
WedAttachState mWedAttachState;
WedAttachTimer mWedAttachTimer;
Callback<WakeupCallback> mWakeupCallback;
#endif
};
} // namespace Mle
@@ -163,4 +163,13 @@
*/
#define OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS 2
#endif
#ifndef OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
#endif
#ifndef OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US
#define OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US 5000
#endif
#endif // OPENTHREAD_CORE_POSIX_CONFIG_H_