mirror of
https://github.com/espressif/openthread.git
synced 2026-09-12 20:20:04 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
Reference in New Issue
Block a user