[low-power] add csl feature for Thread 1.2 (#4557)

This commit implements the CSL feature in Thread 1.2.

- Add macro definitions for low power to control the compiling of
  source code.

- Add data and methods for running CSL in Mac and SubMac. This mainly
  includes setting CSL parameters, starting/stopping CSL, and the
  timer handling process.

- Add otPlatTimeGetAPI and implementation.

- Add CSL transmission implementation. CSL transmission is a new kind
  of transmission, the related definition and implementaion for the
  whole transmitting process is added.

- Add calling of start/stop CSL in certain cases.

- Implement CSL synchronization maintainence. If a CSL cordinator
  didn't get a frame containing CSL IE for CSLTimeout, the CSL
  receiver is regarded as de-synchronized.

- Add Cli interface for using CSL.

- Implement enhanced Ack with IE. The original code can only generate
  auto ack for Imm-Ack. As CSL requires CSL IE included in enhanced
  ack. This PR implements it.

- Add basic functional test for CSL transmission. More tests
  corresponding to test plan would be added later.
This commit is contained in:
Li Cao
2020-08-18 10:55:33 -07:00
committed by GitHub
parent 5b7f3b9acf
commit 7db8c6815c
41 changed files with 2012 additions and 50 deletions
+1
View File
@@ -241,6 +241,7 @@ LOCAL_SRC_FILES := \
src/core/thread/announce_begin_server.cpp \
src/core/thread/announce_sender.cpp \
src/core/thread/child_table.cpp \
src/core/thread/csl_tx_scheduler.cpp \
src/core/thread/discover_scanner.cpp \
src/core/thread/dua_manager.cpp \
src/core/thread/energy_scan_server.cpp \
-2
View File
@@ -310,11 +310,9 @@ uint64_t otPlatTimeGet(void)
return platformGetNow();
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
uint16_t otPlatTimeGetXtalAccuracy(void)
{
return 0;
}
#endif
#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
@@ -145,6 +145,16 @@
*/
#define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 1
/**
* @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
*
* The CSL sample window in units of 10 symbols.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
*
+93 -5
View File
@@ -31,6 +31,7 @@
#include <errno.h>
#include <openthread/dataset.h>
#include <openthread/link.h>
#include <openthread/random_noncrypto.h>
#include <openthread/platform/alarm-micro.h>
#include <openthread/platform/alarm-milli.h>
@@ -113,6 +114,17 @@ static int8_t sCcaEdThresh = -74;
static bool sSrcMatchEnabled = false;
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
static uint8_t sAckIeData[OT_ACK_IE_MAX_SIZE];
static uint8_t sAckIeDataLength = 0;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static const uint8_t sCslIeHeader[OT_IE_HEADER_IE_SIZE] = {0x04, 0x0d};
static uint32_t sCslSampleTime;
static uint32_t sCslPeriod;
#endif
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
static bool sRadioCoexEnabled = true;
#endif
@@ -138,7 +150,7 @@ static void ReverseExtAddress(otExtAddress *aReversed, const otExtAddress *aOrig
}
}
static bool HasFramePending(const otRadioFrame *aFrame)
static bool hasFramePending(const otRadioFrame *aFrame)
{
bool rval = false;
otMacAddress src;
@@ -348,6 +360,17 @@ void platformRadioInit(void)
#endif
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static uint16_t getCslPhase(void)
{
uint32_t curTime = otPlatAlarmMicroGetNow();
uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS;
uint32_t diff = ((sCslSampleTime % cslPeriodInUs) - (curTime % cslPeriodInUs) + cslPeriodInUs) % cslPeriodInUs;
return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS);
}
#endif
bool otPlatRadioIsEnabled(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
@@ -630,6 +653,13 @@ void radioSendMessage(otInstance *aInstance)
}
#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0)
{
otMacFrameSetCslIe(&sTransmitFrame, (uint16_t)sCslPeriod, getCslPhase());
}
#endif
sTransmitMessage.mChannel = sTransmitFrame.mChannel;
otEXPECT(radioProcessTransmitSecurity(&sTransmitFrame) == OT_ERROR_NONE);
@@ -803,7 +833,7 @@ void radioSendAck(void)
#else
otMacFrameIsDataRequest(&sReceiveFrame)
#endif
&& HasFramePending(&sReceiveFrame))
&& hasFramePending(&sReceiveFrame))
{
sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = true;
}
@@ -812,9 +842,18 @@ void radioSendAck(void)
// Use enh-ack for 802.15.4-2015 frames
if (otMacFrameIsVersion2015(&sReceiveFrame))
{
otEXPECT(otMacFrameGenerateEnhAck(&sReceiveFrame, sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending, NULL, 0,
&sAckFrame) == OT_ERROR_NONE);
otEXPECT(radioProcessTransmitSecurity(&sAckFrame) == OT_ERROR_NONE);
otEXPECT(otMacFrameGenerateEnhAck(&sReceiveFrame, sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending,
sAckIeData, sAckIeDataLength, &sAckFrame) == OT_ERROR_NONE);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0)
{
otMacFrameSetCslIe(&sAckFrame, (uint16_t)sCslPeriod, getCslPhase());
}
#endif
if (otMacFrameIsSecurityEnabled(&sAckFrame))
{
otEXPECT(radioProcessTransmitSecurity(&sAckFrame) == OT_ERROR_NONE);
}
}
else
#endif
@@ -1016,6 +1055,55 @@ exit:
}
#endif
uint64_t otPlatRadioGetNow(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return otPlatTimeGet();
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static otError updateIeData(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
uint8_t offset = 0;
if (sCslPeriod > 0)
{
memcpy(sAckIeData, sCslIeHeader, OT_IE_HEADER_IE_SIZE);
offset += OT_IE_HEADER_IE_SIZE + OT_CSL_IE_SIZE; // reserve space for CSL IE
}
sAckIeDataLength = offset;
return error;
}
otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const otExtAddress *aExtAddr)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aExtAddr);
otError error = OT_ERROR_NONE;
sCslPeriod = aCslPeriod;
error = updateIeData(aInstance);
return error;
}
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
{
OT_UNUSED_VARIABLE(aInstance);
sCslSampleTime = aCslSampleTime;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void otPlatRadioSetMacKey(otInstance * aInstance,
uint8_t aKeyIdMode,
uint8_t aKeyId,
+67
View File
@@ -52,6 +52,7 @@ extern "C" {
* @{
*
*/
#define OT_US_PER_TEN_SYMBOLS 160 ///< The microseconds per 10 symbols.
/**
* This structure represents link-specific information for messages received from the Thread radio.
@@ -1004,6 +1005,72 @@ bool otLinkIsPromiscuous(otInstance *aInstance);
*/
otError otLinkSetPromiscuous(otInstance *aInstance, bool aPromiscuous);
/**
* This function gets the CSL channel.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The CSL channel.
*
*/
uint8_t otLinkCslGetChannel(otInstance *aInstance);
/**
* This function sets the CSL channel.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aChannel The CSL sample channel.
*
* @retval OT_ERROR_NONE Successfully set the CSL parameters.
* @retval OT_ERROR_INVALID_ARGS Invalid @p aChannel.
*
*/
otError otLinkCslSetChannel(otInstance *aInstance, uint8_t aChannel);
/**
* This function gets the CSL period.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The CSL period in units of 10 symbols.
*
*/
uint16_t otLinkCslGetPeriod(otInstance *aInstance);
/**
* This function sets the CSL period.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aPeriod The CSL period in units of 10 symbols.
*
* @retval OT_ERROR_NONE Successfully set the CSL period.
* @retval OT_ERROR_INVALID_ARGS Invalid CSL period.
*
*/
otError otLinkCslSetPeriod(otInstance *aInstance, uint16_t aPeriod);
/**
* This function gets the CSL timeout.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The CSL timeout in seconds.
*
*/
uint32_t otLinkCslGetTimeout(otInstance *aInstance);
/**
* This function sets the CSL timeout.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aTimeout The CSL timeout in seconds.
*
* @retval OT_ERROR_NONE Successfully set the CSL timeout.
* @retval OT_ERROR_INVALID_ARGS Invalid CSL timeout.
*
*/
otError otLinkCslSetTimeout(otInstance *aInstance, uint32_t aTimeout);
/**
* This function returns the current CCA (Clear Channel Assessment) failure rate.
*
+40
View File
@@ -143,6 +143,17 @@ typedef uint16_t otShortAddress;
#define OT_EXT_ADDRESS_SIZE 8 ///< Size of an IEEE 802.15.4 Extended Address (bytes)
/**
* This enumeration defines constants about size of header IE in ACK.
*
*/
enum
{
OT_IE_HEADER_IE_SIZE = 2, ///< Size of IE header in bytes.
OT_CSL_IE_SIZE = 4, ///< Size of CSL IE content in bytes.
OT_ACK_IE_MAX_SIZE = 16, ///< Max length for header IE in ACK.
};
/**
* @struct otExtAddress
*
@@ -213,10 +224,13 @@ typedef struct otRadioFrame
{
const otMacKey *mAesKey; ///< The key used for AES-CCM frame security.
otRadioIeInfo * mIeInfo; ///< The pointer to the Header IE(s) related information.
uint16_t mPeriod; ///< The transmit time period.
uint16_t mPhase; ///< The transmit time phase.
uint8_t mMaxCsmaBackoffs; ///< Maximum number of backoffs attempts before declaring CCA failure.
uint8_t mMaxFrameRetries; ///< Maximum number of retries allowed after a transmission failure.
bool mIsARetx : 1; ///< True if this frame is a retransmission (ignored by radio driver).
bool mCsmaCaEnabled : 1; ///< Set to true to enable CSMA-CA for this packet, false otherwise.
bool mCslPresent : 1; ///< Set to true if CSL header ie is present.
bool mIsSecurityProcessed : 1; ///< True if SubMac should skip the AES processing of this frame.
} mTxInfo;
@@ -866,6 +880,32 @@ bool otPlatRadioIsCoexEnabled(otInstance *aInstance);
*/
otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics);
/**
* Enable or disable CSL receiver.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aCslPeriod CSL period, 0 for disabling CSL.
* @param[in] aExtAddr The extended source address of CSL receiver's parent device (when the platforms generate
* enhanced ack, platforms may need to know acks to which address should include CSL IE).
*
* @retval OT_ERROR_NOT_SUPPORTED Radio driver doesn't support CSL.
* @retval OT_ERROR_FAILED Other platform specific errors.
* @retval OT_ERROR_NONE Successfully enabled or disabled CSL.
*
*/
otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const otExtAddress *aExtAddr);
/**
* Update CSL sample time in radio driver.
*
* Sample time is stored in radio driver as a copy to calculate phase when sending ACK with CSL IE.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aCslSampleTime The latest sample time.
*
*/
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime);
/**
* @}
*
+1
View File
@@ -86,6 +86,7 @@ build_all_features()
local options_1_2=(
"-DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1"
"-DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1"
"-DOPENTHREAD_CONFIG_DUA_ENABLE=1"
)
+40
View File
@@ -33,6 +33,7 @@ Done
- [commissioner](README_COMMISSIONER.md)
- [contextreusedelay](#contextreusedelay)
- [counters](#counters)
- [csl](#csl)
- [dataset](README_DATASET.md)
- [delaytimermin](#delaytimermin)
- [diag](#diag)
@@ -499,6 +500,45 @@ Done
Done
```
### csl
Get the CSL configuration.
```bash
> csl
Channel: 11
Period: 1000 (in units of 10 symbols), 160ms
Timeout: 1000s
Done
```
### csl channel \<channel\>
Set CSL channel.
```bash
> csl channel 20
Done
```
### csl period \<period\>
Set CSL period in units of 10 symbols. Disable CSL by setting this parameter to `0`.
```bash
> csl period 3000
Done
```
### csl timeout \<timeout\>
Set the CSL timeout in seconds.
```bash
> csl timeout 10
Done
```
### networktime
Get the Thread network time and the time sync parameters.
+41
View File
@@ -132,6 +132,9 @@ const struct Command Interpreter::sCommands[] = {
{"contextreusedelay", &Interpreter::ProcessContextIdReuseDelay},
#endif
{"counters", &Interpreter::ProcessCounters},
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
{"csl", &Interpreter::ProcessCsl},
#endif
{"dataset", &Interpreter::ProcessDataset},
#if OPENTHREAD_FTD
{"delaytimermin", &Interpreter::ProcessDelayTimerMin},
@@ -1283,6 +1286,44 @@ exit:
AppendResult(error);
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void Interpreter::ProcessCsl(uint8_t aArgsLength, char *argv[])
{
otError error = OT_ERROR_INVALID_ARGS;
if (aArgsLength == 0)
{
OutputFormat("Channel: %u\r\n", otLinkCslGetChannel(mInstance));
OutputFormat("Period: %u(in units of 10 symbols), %ums\r\n", otLinkCslGetPeriod(mInstance),
otLinkCslGetPeriod(mInstance) * kUsPerTenSymbols / 1000);
OutputFormat("Timeout: %us\r\n", otLinkCslGetTimeout(mInstance));
error = OT_ERROR_NONE;
}
else if (aArgsLength == 2)
{
long value;
SuccessOrExit(error = ParseLong(argv[1], value));
if (strcmp(argv[0], "channel") == 0)
{
SuccessOrExit(error = otLinkCslSetChannel(mInstance, static_cast<uint8_t>(value)));
}
else if (strcmp(argv[0], "period") == 0)
{
SuccessOrExit(error = otLinkCslSetPeriod(mInstance, static_cast<uint16_t>(value)));
}
else if (strcmp(argv[0], "timeout") == 0)
{
SuccessOrExit(error = otLinkCslSetTimeout(mInstance, static_cast<uint32_t>(value)));
}
}
exit:
AppendResult(error);
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_FTD
void Interpreter::ProcessDelayTimerMin(uint8_t aArgsLength, char *aArgs[])
{
+1
View File
@@ -307,6 +307,7 @@ private:
void ProcessContextIdReuseDelay(uint8_t aArgsLength, char *aArgs[]);
#endif
void ProcessCounters(uint8_t aArgsLength, char *aArgs[]);
void ProcessCsl(uint8_t aArgsLength, char *argv[]);
#if OPENTHREAD_FTD
void ProcessDelayTimerMin(uint8_t aArgsLength, char *aArgs[]);
#endif
+1
View File
@@ -173,6 +173,7 @@ set(COMMON_SOURCES
thread/announce_begin_server.cpp
thread/announce_sender.cpp
thread/child_table.cpp
thread/csl_tx_scheduler.cpp
thread/discover_scanner.cpp
thread/dua_manager.cpp
thread/energy_scan_server.cpp
+2
View File
@@ -215,6 +215,7 @@ SOURCES_COMMON = \
thread/announce_begin_server.cpp \
thread/announce_sender.cpp \
thread/child_table.cpp \
thread/csl_tx_scheduler.cpp \
thread/discover_scanner.cpp \
thread/dua_manager.cpp \
thread/energy_scan_server.cpp \
@@ -431,6 +432,7 @@ HEADERS_COMMON = \
thread/announce_sender.hpp \
thread/child_mask.hpp \
thread/child_table.hpp \
thread/csl_tx_scheduler.hpp \
thread/discover_scanner.hpp \
thread/dua_manager.hpp \
thread/energy_scan_server.hpp \
+55
View File
@@ -481,3 +481,58 @@ uint16_t otLinkGetCcaFailureRate(otInstance *aInstance)
return instance.Get<Mac::Mac>().GetCcaFailureRate();
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
uint8_t otLinkCslGetChannel(otInstance *aInstance)
{
return static_cast<Instance *>(aInstance)->Get<Mac::Mac>().GetCslChannel();
}
otError otLinkCslSetChannel(otInstance *aInstance, uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
Instance &instance = *static_cast<Instance *>(aInstance);
VerifyOrExit((Radio::kChannelMin <= aChannel) && (aChannel <= Radio::kChannelMax), error = OT_ERROR_INVALID_ARGS);
instance.Get<Mac::Mac>().SetCslChannel(aChannel);
exit:
return error;
}
uint16_t otLinkCslGetPeriod(otInstance *aInstance)
{
return static_cast<Instance *>(aInstance)->Get<Mac::Mac>().GetCslPeriod();
}
otError otLinkCslSetPeriod(otInstance *aInstance, uint16_t aPeriod)
{
otError error = OT_ERROR_NONE;
Instance &instance = *static_cast<Instance *>(aInstance);
VerifyOrExit((aPeriod == 0 || kMinCslPeriod <= aPeriod), error = OT_ERROR_INVALID_ARGS);
instance.Get<Mac::Mac>().SetCslPeriod(aPeriod);
exit:
return error;
}
uint32_t otLinkCslGetTimeout(otInstance *aInstance)
{
return static_cast<Instance *>(aInstance)->Get<Mac::Mac>().GetCslTimeout();
}
otError otLinkCslSetTimeout(otInstance *aInstance, uint32_t aTimeout)
{
otError error = OT_ERROR_NONE;
Instance &instance = *static_cast<Instance *>(aInstance);
VerifyOrExit(kMaxCslTimeout >= aTimeout, error = OT_ERROR_INVALID_ARGS);
instance.Get<Mac::Mac>().SetCslTimeout(aTimeout);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+7
View File
@@ -524,6 +524,13 @@ template <> inline DataPollHandler &Instance::Get(void)
return mThreadNetif.mMeshForwarder.mIndirectSender.mDataPollHandler;
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
template <> inline CslTxScheduler &Instance::Get(void)
{
return mThreadNetif.mMeshForwarder.mIndirectSender.mCslTxScheduler;
}
#endif
template <> inline AddressResolver &Instance::Get(void)
{
return mThreadNetif.mAddressResolver;
+44
View File
@@ -326,4 +326,48 @@
(OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_MTD
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* @def OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD
*
* This setting configures the minimum CSL period that could be used, in units of milliseconds.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD
#define OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD 10
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT
*
* This setting configures the maximum CSL timeout that could be used, in units of seconds.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT
#define OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT 10000
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_TIMEOUT
*
* The default CSL timeout in seconds.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_TIMEOUT
#define OPENTHREAD_CONFIG_CSL_TIMEOUT 100
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
*
* The CSL sample window in 10 symbols.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5
#endif
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#endif // CONFIG_MAC_H_
+3
View File
@@ -89,6 +89,9 @@ public:
class ChildInfo
{
friend class DataPollHandler;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
friend class CslTxScheduler;
#endif
private:
bool IsDataPollPending(void) const { return mDataPollPending; }
+258 -21
View File
@@ -46,6 +46,7 @@
#include "crypto/sha256.hpp"
#include "mac/mac_frame.hpp"
#include "radio/radio.hpp"
#include "thread/child_table.hpp"
#include "thread/link_quality.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
@@ -111,6 +112,9 @@ Mac::Mac(Instance &aInstance)
, mMaxFrameRetriesDirect(kDefaultMaxFrameRetriesDirect)
#if OPENTHREAD_FTD
, mMaxFrameRetriesIndirect(kDefaultMaxFrameRetriesIndirect)
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
, mCslTxFireTime(TimeMilli::kMaxDuration)
#endif
, mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union
, mScanHandlerContext(nullptr)
@@ -207,6 +211,9 @@ bool Mac::IsInTransmitState(void) const
case kOperationTransmitDataDirect:
#if OPENTHREAD_FTD
case kOperationTransmitDataIndirect:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kOperationTransmitDataCsl:
#endif
#endif
case kOperationTransmitBeacon:
case kOperationTransmitPoll:
@@ -561,10 +568,24 @@ void Mac::RequestIndirectFrameTransmission(void)
StartOperation(kOperationTransmitDataIndirect);
exit:
return;
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
void Mac::RequestCslFrameTransmission(uint32_t aDelay)
{
VerifyOrExit(mEnabled, OT_NOOP);
mCslTxFireTime = mTimer.GetNow() + aDelay;
StartOperation(kOperationTransmitDataCsl);
exit:
return;
}
#endif
#endif // OPENTHREAD_FTD
otError Mac::RequestOutOfBandFrameTransmission(otRadioFrame *aOobFrame)
{
@@ -608,23 +629,39 @@ void Mac::UpdateIdleMode(void)
VerifyOrExit(mOperation == kOperationIdle, OT_NOOP);
if (!mRxOnWhenIdle)
{
#if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
if (mShouldDelaySleep)
{
mTimer.Start(kSleepDelay);
mShouldDelaySleep = false;
mDelayingSleep = true;
otLogDebgMac("Idle mode: Sleep delayed");
}
if (mShouldDelaySleep)
{
mTimer.Start(kSleepDelay);
mShouldDelaySleep = false;
mDelayingSleep = true;
otLogDebgMac("Idle mode: Sleep delayed");
}
if (mDelayingSleep)
if (mDelayingSleep)
{
shouldSleep = false;
}
#endif
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
else if (mPendingTransmitDataCsl)
{
shouldSleep = false;
mTimer.FireAt(mCslTxFireTime);
}
#endif
if (shouldSleep)
{
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (IsCslEnabled())
{
IgnoreError(mSubMac.CslSample());
ExitNow();
}
#endif
IgnoreError(mSubMac.Sleep());
otLogDebgMac("Idle mode: Radio sleeping");
}
@@ -680,6 +717,12 @@ void Mac::StartOperation(Operation aOperation)
case kOperationTransmitDataIndirect:
mPendingTransmitDataIndirect = true;
break;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kOperationTransmitDataCsl:
mPendingTransmitDataCsl = true;
break;
#endif
#endif
case kOperationTransmitPoll:
@@ -720,6 +763,9 @@ void Mac::PerformNextOperation(void)
mPendingTransmitDataDirect = false;
#if OPENTHREAD_FTD
mPendingTransmitDataIndirect = false;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mPendingTransmitDataCsl = false;
#endif
#endif
mPendingTransmitPoll = false;
mTimer.Stop();
@@ -738,6 +784,13 @@ void Mac::PerformNextOperation(void)
mPendingWaitingForData = false;
mOperation = kOperationWaitingForData;
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
else if (mPendingTransmitDataCsl && mTimer.GetNow() >= mCslTxFireTime)
{
mPendingTransmitDataCsl = false;
mOperation = kOperationTransmitDataCsl;
}
#endif
else if (mPendingTransmitOobFrame)
{
mPendingTransmitOobFrame = false;
@@ -764,7 +817,7 @@ void Mac::PerformNextOperation(void)
mPendingTransmitDataIndirect = false;
mOperation = kOperationTransmitDataIndirect;
}
#endif
#endif // OPENTHREAD_FTD
else if (mPendingTransmitPoll && (!mPendingTransmitDataDirect || mShouldTxPollBeforeData))
{
mPendingTransmitPoll = false;
@@ -806,6 +859,9 @@ void Mac::PerformNextOperation(void)
case kOperationTransmitDataDirect:
#if OPENTHREAD_FTD
case kOperationTransmitDataIndirect:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kOperationTransmitDataCsl:
#endif
#endif
case kOperationTransmitPoll:
case kOperationTransmitOutOfBandFrame:
@@ -837,7 +893,7 @@ otError Mac::PrepareDataRequest(TxFrame &aFrame)
VerifyOrExit(!dst.IsNone(), error = OT_ERROR_ABORT);
fcf = Frame::kFcfFrameMacCmd | Frame::kFcfPanidCompression | Frame::kFcfAckRequest | Frame::kFcfSecurityEnabled;
UpdateFrameControlField(/* aIsTimeSync */ false, fcf);
UpdateFrameControlField(nullptr, /* aIsTimeSync */ false, fcf);
if (dst.IsExtended())
{
@@ -859,6 +915,10 @@ otError Mac::PrepareDataRequest(TxFrame &aFrame)
aFrame.SetSrcAddr(src);
aFrame.SetDstAddr(dst);
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
IgnoreError(AppendHeaderIe(false, aFrame));
#endif
IgnoreError(aFrame.SetCommandId(Frame::kMacCmdDataRequest));
exit:
@@ -1006,6 +1066,11 @@ void Mac::ProcessTransmitSecurity(TxFrame &aFrame)
VerifyOrExit(aFrame.GetTimeIeOffset() == 0, OT_NOOP);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// Transmit security will be processed after time IE content is updated.
VerifyOrExit(aFrame.mInfo.mTxInfo.mCslPresent == 0, OT_NOOP);
#endif
aFrame.ProcessTransmitAesCcm(*extAddress);
exit:
@@ -1020,6 +1085,9 @@ void Mac::BeginTransmit(void)
VerifyOrExit(IsEnabled(), error = OT_ERROR_ABORT);
sendFrame.SetIsARetransmission(false);
sendFrame.SetIsSecurityProcessed(false);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
sendFrame.SetTxPeriod(0);
#endif
switch (mOperation)
{
@@ -1069,7 +1137,22 @@ void Mac::BeginTransmit(void)
sendFrame.SetSequence(mDataSequence++);
}
break;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kOperationTransmitDataCsl:
sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsCsl);
sendFrame.SetMaxFrameRetries(kMaxFrameRetriesCsl);
SuccessOrExit(error = Get<CslTxScheduler>().HandleFrameRequest(sendFrame));
// If the frame is marked as a retransmission, then data sequence number is already set.
if (!sendFrame.IsARetransmission())
{
sendFrame.SetSequence(mDataSequence++);
}
break;
#endif
#endif // OPENTHREAD_FTD
case kOperationTransmitOutOfBandFrame:
sendFrame.CopyFrom(*mOobFrame);
@@ -1213,6 +1296,14 @@ void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame,
if ((aError == OT_ERROR_NONE) && ackRequested && (aAckFrame != nullptr) && (neighbor != nullptr))
{
neighbor->GetLinkInfo().AddRss(aAckFrame->GetRssi());
#if OPENTHREAD_FTD
if (aAckFrame->GetVersion() == Frame::kFcfFrameVersion2015)
{
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
ProcessCsl(*aAckFrame, dstAddr);
#endif
}
#endif // OPENTHREAD_FTD
}
// Update MAC counters.
@@ -1345,6 +1436,17 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError
break;
#if OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kOperationTransmitDataCsl:
mCounters.mTxData++;
otDumpDebgMac("TX", aFrame.GetHeader(), aFrame.GetLength());
FinishOperation();
Get<CslTxScheduler>().HandleSentFrame(aFrame, aError);
PerformNextOperation();
break;
#endif
case kOperationTransmitDataIndirect:
mCounters.mTxData++;
@@ -1402,17 +1504,25 @@ void Mac::HandleTimer(void)
PerformNextOperation();
break;
#if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
case kOperationIdle:
if (mDelayingSleep)
if (!mRxOnWhenIdle)
{
otLogDebgMac("Sleep delay timeout expired");
mDelayingSleep = false;
UpdateIdleMode();
}
break;
#if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
if (mDelayingSleep)
{
otLogDebgMac("Sleep delay timeout expired");
mDelayingSleep = false;
UpdateIdleMode();
}
#endif
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
else if (mPendingTransmitDataCsl)
{
PerformNextOperation();
}
#endif
break;
default:
OT_ASSERT(false);
@@ -1689,6 +1799,13 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, otError aError)
ExitNow();
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (aFrame->GetVersion() == Frame::kFcfFrameVersion2015)
{
ProcessCsl(*aFrame, srcaddr);
}
#endif
Get<DataPollSender>().ProcessFrame(*aFrame);
if (neighbor != nullptr)
@@ -1797,6 +1914,8 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, otError aError)
otDumpDebgMac("RX", aFrame->GetHeader(), aFrame->GetLength());
Get<MeshForwarder>().HandleReceivedFrame(*aFrame);
UpdateIdleMode();
exit:
if (error != OT_ERROR_NONE)
@@ -1965,6 +2084,12 @@ const char *Mac::OperationToString(Operation aOperation)
case kOperationTransmitDataIndirect:
retval = "TransmitDataIndirect";
break;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kOperationTransmitDataCsl:
retval = "TransmitDataCsl";
break;
#endif
#endif
case kOperationTransmitPoll:
@@ -2061,8 +2186,87 @@ exit:
}
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void Mac::SetCslChannel(uint8_t aChannel)
{
VerifyOrExit(GetCslChannel() != aChannel, OT_NOOP);
mSubMac.SetCslChannel(aChannel);
if (IsCslEnabled())
{
Get<Mle::Mle>().ScheduleChildUpdateRequest();
}
exit:
return;
}
void Mac::SetCslPeriod(uint16_t aPeriod)
{
mSubMac.SetCslPeriod(aPeriod);
IgnoreError(Get<Radio>().EnableCsl(GetCslPeriod(), &Get<Mle::Mle>().GetParent().GetExtAddress()));
if (IsCslEnabled())
{
Get<Mle::Mle>().ScheduleChildUpdateRequest();
}
UpdateIdleMode();
}
void Mac::SetCslTimeout(uint32_t aTimeout)
{
VerifyOrExit(GetCslTimeout() != aTimeout, OT_NOOP);
mSubMac.SetCslTimeout(aTimeout);
if (IsCslEnabled())
{
Get<Mle::Mle>().ScheduleChildUpdateRequest();
}
exit:
return;
}
bool Mac::IsCslEnabled(void) const
{
return (GetCslPeriod() > 0) && !GetRxOnWhenIdle() && Get<Mle::MleRouter>().IsChild() &&
Get<Mle::Mle>().GetParent().IsEnhancedKeepAliveSupported();
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr)
{
const uint8_t *cur = aFrame.GetHeaderIe(Frame::kHeaderIeCsl);
Child * child = Get<ChildTable>().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid);
const CslIe * csl;
VerifyOrExit(cur != nullptr && child != nullptr && aFrame.GetSecurityEnabled(), OT_NOOP);
csl = reinterpret_cast<const CslIe *>(cur + sizeof(HeaderIe));
child->SetCslPeriod(csl->GetPeriod());
// Use ceiling to ensure the the time diff will be within kUsPerTenSymbols
child->SetCslPhase(((aFrame.GetTimestamp() + kUsPerTenSymbols - 1) / kUsPerTenSymbols + csl->GetPhase()) %
csl->GetPeriod());
child->SetCslSynchronized(true);
child->SetCslLastHeard(TimerMilli::GetNow());
otLogDebgMac("Timestamp=%u Sequence=%u CslPeriod=%hu CslPhase=%hu TransmitPhase=%hu",
static_cast<uint32_t>(aFrame.GetTimestamp()), aFrame.GetSequence(), csl->GetPeriod(), csl->GetPhase(),
child->GetCslPhase());
Get<CslTxScheduler>().Update();
exit:
return;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
otError Mac::AppendHeaderIe(bool aIsTimeSync, TxFrame &aFrame)
otError Mac::AppendHeaderIe(bool aIsTimeSync, TxFrame &aFrame) const
{
OT_UNUSED_VARIABLE(aIsTimeSync);
@@ -2083,6 +2287,23 @@ otError Mac::AppendHeaderIe(bool aIsTimeSync, TxFrame &aFrame)
}
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (IsCslEnabled())
{
OT_ASSERT(aFrame.GetSecurityEnabled());
aFrame.mInfo.mTxInfo.mCslPresent = true;
ieList[ieCount].Init();
ieList[ieCount].SetId(Frame::kHeaderIeCsl);
ieList[ieCount].SetLength(sizeof(CslIe));
ieCount++;
}
else
#endif
{
aFrame.mInfo.mTxInfo.mCslPresent = false;
}
if (ieCount > 0)
{
ieList[ieCount].Init();
@@ -2107,9 +2328,10 @@ exit:
}
#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
void Mac::UpdateFrameControlField(bool aIsTimeSync, uint16_t &aFcf)
void Mac::UpdateFrameControlField(const Neighbor *aNeighbor, bool aIsTimeSync, uint16_t &aFcf) const
{
OT_UNUSED_VARIABLE(aIsTimeSync);
OT_UNUSED_VARIABLE(aNeighbor);
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
@@ -2119,6 +2341,21 @@ void Mac::UpdateFrameControlField(bool aIsTimeSync, uint16_t &aFcf)
}
else
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (IsCslEnabled())
{
aFcf |= Frame::kFcfFrameVersion2015 | Frame::kFcfIePresent;
}
else
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (aNeighbor != nullptr && !Get<Mle::MleRouter>().IsActiveRouter(aNeighbor->GetRloc16()) &&
static_cast<const Child *>(aNeighbor)->IsCslSynchronized())
{
aFcf |= Frame::kFcfFrameVersion2015;
}
else
#endif
#endif
{
aFcf |= Frame::kFcfFrameVersion2006;
+90 -5
View File
@@ -41,6 +41,7 @@
#include "common/locator.hpp"
#include "common/tasklet.hpp"
#include "common/time.hpp"
#include "common/timer.hpp"
#include "mac/channel_mask.hpp"
#include "mac/mac_filter.hpp"
@@ -81,12 +82,13 @@ enum
OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT, ///< macMaxCsmaBackoffs for direct transmissions
kMaxCsmaBackoffsIndirect =
OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_INDIRECT, ///< macMaxCsmaBackoffs for indirect transmissions
kMaxCsmaBackoffsCsl = 0, ///< macMaxCsmaBackoffs for CSL transmissions
kDefaultMaxFrameRetriesDirect =
OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT, ///< macDefaultMaxFrameRetries for direct transmissions
kDefaultMaxFrameRetriesIndirect =
OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_INDIRECT, ///< macDefaultMaxFrameRetries for indirect
///< transmissions
kMaxFrameRetriesCsl = 0, ///< macMaxFrameRetries for CSL transmissions
kTxNumBcast = OPENTHREAD_CONFIG_MAC_TX_NUM_BCAST ///< Number of times each broadcast frame is transmitted
};
@@ -215,6 +217,17 @@ public:
*
*/
void RequestIndirectFrameTransmission(void);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
/**
* This method requests `Mac` to start a Csl Tx operation after a delay of @p aDelay time.
*
* @param[in] aDelay Delay time for `Mac` to start a Csl Tx, in units of milliseconds.
*
*/
void RequestCslFrameTransmission(uint32_t aDelay);
#endif
#endif
/**
@@ -663,6 +676,65 @@ public:
*/
bool IsEnabled(void) const { return mEnabled; }
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method gets the CSL channel.
*
* @returns CSL channel.
*
*/
uint8_t GetCslChannel(void) const { return mSubMac.GetCslChannel(); }
/**
* This method sets the CSL channel.
*
* @param[in] aChannel The CSL channel.
*
*/
void SetCslChannel(uint8_t aChannel);
/**
* This method gets the CSL period.
*
* @returns CSL period in units of 10 symbols.
*
*/
uint16_t GetCslPeriod(void) const { return mSubMac.GetCslPeriod(); }
/**
* This method sets the CSL period.
*
* @param[in] aPeriod The CSL period in 10 symbols.
*
*/
void SetCslPeriod(uint16_t aPeriod);
/**
* This method gets the CSL timeout.
*
* @returns CSL timeout in seconds.
*
*/
uint32_t GetCslTimeout(void) const { return mSubMac.GetCslTimeout(); }
/**
* This method sets the CSL timeout.
*
* @param[in] aTimeout The CSL timeout in seconds.
*
*/
void SetCslTimeout(uint32_t aTimeout);
/**
* This method indicates whether CSL is started at the moment.
*
* @retval TURE if CSL is actually running at the moment, FALSE otherwise.
*
*/
bool IsCslEnabled(void) const;
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
/**
* This method appends header IEs to a TX-frame according to its
@@ -675,8 +747,8 @@ public:
* @retval OT_ERROR_NOT_FOUND If cannot find header IE position in the frame.
*
*/
static otError AppendHeaderIe(bool aIsTimeSync, TxFrame &aFrame);
#endif
otError AppendHeaderIe(bool aIsTimeSync, TxFrame &aFrame) const;
#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
/**
* This method updates frame control field.
@@ -685,11 +757,12 @@ public:
* If this is a csl transmission frame or header IE is present in this frame,
* the version should be set to 2015. Otherwise, the version would be set to 2006.
*
* @param[in] aNeighbor A pointer to the destination device, could be `nullptr`.
* @param[in] aIsTimeSync A boolean indicates if time sync is being used.
* @param[out] aFcf A reference to the frame control field to set.
*
*/
static void UpdateFrameControlField(bool aIsTimeSync, uint16_t &aFcf);
void UpdateFrameControlField(const Neighbor *aNeighbor, bool aIsTimeSync, uint16_t &aFcf) const;
private:
enum
@@ -708,6 +781,9 @@ private:
kOperationTransmitDataDirect,
#if OPENTHREAD_FTD
kOperationTransmitDataIndirect,
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
kOperationTransmitDataCsl,
#endif
#endif
kOperationTransmitPoll,
kOperationWaitingForData,
@@ -737,8 +813,8 @@ private:
};
#endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
void ProcessTransmitSecurity(TxFrame &aFrame);
otError ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor);
void ProcessTransmitSecurity(TxFrame &aFrame);
void UpdateIdleMode(void);
void StartOperation(Operation aOperation);
void FinishOperation(void);
@@ -771,6 +847,9 @@ private:
uint8_t GetTimeIeOffset(const Frame &aFrame);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
void ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr);
#endif
static const char *OperationToString(Operation aOperation);
static const otMacKey sMode2Key;
@@ -786,6 +865,9 @@ private:
bool mPendingTransmitDataDirect : 1;
#if OPENTHREAD_FTD
bool mPendingTransmitDataIndirect : 1;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
bool mPendingTransmitDataCsl : 1;
#endif
bool mPendingTransmitPoll : 1;
bool mPendingTransmitOobFrame : 1;
@@ -820,6 +902,9 @@ private:
#if OPENTHREAD_FTD
uint8_t mMaxFrameRetriesIndirect;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
TimeMilli mCslTxFireTime;
#endif
union
{
+3 -1
View File
@@ -941,11 +941,13 @@ void Frame::SetCslIe(uint16_t aCslPeriod, uint16_t aCslPhase)
uint8_t *cur = GetHeaderIe(Frame::kHeaderIeCsl);
CslIe * csl;
OT_ASSERT(cur != nullptr);
VerifyOrExit(cur != nullptr, OT_NOOP);
csl = reinterpret_cast<CslIe *>(cur + sizeof(HeaderIe));
csl->SetPeriod(aCslPeriod);
csl->SetPhase(aCslPhase);
exit:
return;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+5
View File
@@ -1296,6 +1296,11 @@ public:
*
*/
otError GenerateEnhAck(const RxFrame &aFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
void SetTxPhase(uint16_t aPhase) { mInfo.mTxInfo.mPhase = aPhase; }
void SetTxPeriod(uint16_t aPeriod) { mInfo.mTxInfo.mPeriod = aPeriod; }
#endif
};
OT_TOOL_PACKED_BEGIN
+223 -1
View File
@@ -35,6 +35,9 @@
#include <stdio.h>
#include <openthread/platform/time.h>
#include "mac_frame.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
@@ -62,6 +65,13 @@ SubMac::SubMac(Instance &aInstance)
, mFrameCounter(0)
, mKeyId(0)
, mTimer(aInstance, SubMac::HandleTimer, this)
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
, mCslTimeout(OPENTHREAD_CONFIG_CSL_TIMEOUT)
, mCslPeriod(0)
, mCslChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
, mCslState(kCslIdle)
, mCslTimer(aInstance, SubMac::HandleCslTimer, this)
#endif
{
mExtAddress.Clear();
mPrevKey.Clear();
@@ -143,6 +153,7 @@ otError SubMac::Enable(void)
SuccessOrExit(error = Get<Radio>().Enable());
SuccessOrExit(error = Get<Radio>().Sleep());
SetState(kStateSleep);
exit:
@@ -195,6 +206,36 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otError SubMac::CslSample(void)
{
otError error = OT_ERROR_NONE;
switch (mCslState)
{
case kCslSample:
error = Get<Radio>().Receive(mCslChannel);
break;
case kCslSleep:
error = Get<Radio>().Sleep();
break;
case kCslIdle:
ExitNow(error = OT_ERROR_INVALID_STATE);
default:
OT_ASSERT(false);
}
SetState(kStateCslSample);
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnMac("CslSample() failed, error: %s", otThreadErrorToString(error));
}
return error;
}
#endif
void SubMac::HandleReceiveDone(RxFrame *aFrame, otError aError)
{
if (mPcapCallback && (aFrame != nullptr) && (aError == OT_ERROR_NONE))
@@ -218,6 +259,9 @@ otError SubMac::Send(void)
{
case kStateDisabled:
case kStateCsmaBackoff:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
#endif
case kStateTransmit:
case kStateEnergyScan:
ExitNow(error = OT_ERROR_INVALID_STATE);
@@ -225,6 +269,9 @@ otError SubMac::Send(void)
case kStateSleep:
case kStateReceive:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
case kStateCslSample:
#endif
break;
}
@@ -278,6 +325,30 @@ void SubMac::StartCsmaBackoff(void)
uint32_t backoff;
uint32_t backoffExponent = kMinBE + mTransmitRetries + mCsmaBackoffs;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (mTransmitFrame.mInfo.mTxInfo.mPeriod != 0)
{
uint32_t phaseNow = (otPlatTimeGet() / kUsPerTenSymbols) % mTransmitFrame.mInfo.mTxInfo.mPeriod;
uint32_t phaseDesired = mTransmitFrame.mInfo.mTxInfo.mPhase;
SetState(kStateCslTransmit);
if (phaseNow < phaseDesired)
{
mTimer.Start((phaseDesired - phaseNow) * kUsPerTenSymbols);
}
else if (phaseNow > phaseDesired)
{
mTimer.Start((phaseDesired + mTransmitFrame.mInfo.mTxInfo.mPeriod - phaseNow) * kUsPerTenSymbols);
}
else
{
BeginTransmit();
}
ExitNow();
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
SetState(kStateCsmaBackoff);
VerifyOrExit(ShouldHandleCsmaBackOff(), BeginTransmit());
@@ -315,9 +386,13 @@ void SubMac::BeginTransmit(void)
OT_UNUSED_VARIABLE(error);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateCslTransmit, OT_NOOP);
#else
VerifyOrExit(mState == kStateCsmaBackoff, OT_NOOP);
#endif
mTransmitFrame.SetCsmaCaEnabled(true);
mTransmitFrame.SetCsmaCaEnabled(mTransmitFrame.mInfo.mTxInfo.mPeriod != 0);
if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)
{
@@ -454,11 +529,17 @@ otError SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
case kStateDisabled:
case kStateCsmaBackoff:
case kStateTransmit:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
#endif
case kStateEnergyScan:
ExitNow(error = OT_ERROR_INVALID_STATE);
case kStateReceive:
case kStateSleep:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
case kStateCslSample:
#endif
break;
}
@@ -529,6 +610,11 @@ void SubMac::HandleTimer(void)
{
switch (mState)
{
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
BeginTransmit();
break;
#endif
case kStateCsmaBackoff:
BeginTransmit();
break;
@@ -723,6 +809,16 @@ const char *SubMac::StateToString(State aState)
case kStateEnergyScan:
str = "EnergyScan";
break;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
str = "CslTransmit";
break;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
case kStateCslSample:
str = "CslSample";
break;
#endif
}
return str;
@@ -730,5 +826,131 @@ const char *SubMac::StateToString(State aState)
// LCOV_EXCL_STOP
//---------------------------------------------------------------------------------------------------------------------
// CSL Receiver methods
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void SubMac::SetCslChannel(uint8_t aChannel)
{
mCslChannel = aChannel;
}
void SubMac::SetCslPeriod(uint16_t aPeriod)
{
VerifyOrExit(mCslPeriod != aPeriod, OT_NOOP);
mCslPeriod = aPeriod;
mCslTimer.Stop();
if (mCslPeriod > 0)
{
mCslSampleTime = mCslTimer.GetNow();
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
mCslState = kCslSample;
HandleCslTimer();
}
else
{
mCslState = kCslIdle;
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Sleep());
SetState(kStateSleep);
}
}
otLogDebgMac("Csl Period: %u", mCslPeriod);
exit:
return;
}
void SubMac::SetCslTimeout(uint32_t aTimeout)
{
mCslTimeout = aTimeout;
}
void SubMac::FillCsl(Frame &aFrame)
{
uint8_t *cur = aFrame.GetHeaderIe(Frame::kHeaderIeCsl);
if (cur != nullptr)
{
CslIe *csl = reinterpret_cast<CslIe *>(cur + sizeof(HeaderIe));
csl->SetPeriod(mCslPeriod);
csl->SetPhase(GetCslPhase());
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otLogDebgMac("%10u:FillCsl() seq=%u phase=%hu", static_cast<uint32_t>(otPlatTimeGet()), aFrame.GetSequence(),
csl->GetPhase());
#endif
}
}
void SubMac::HandleCslTimer(Timer &aTimer)
{
aTimer.GetOwner<SubMac>().HandleCslTimer();
}
void SubMac::HandleCslTimer(void)
{
switch (mCslState)
{
case kCslSample:
mCslState = kCslSleep;
// kUsPerTenSymbols: computing CSL Phase using floor division.
mCslTimer.StartAt(mCslSampleTime, mCslPeriod * kUsPerTenSymbols - kUsPerTenSymbols);
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Sleep());
otLogDebgMac("CSL sleep %u", mCslTimer.GetNow().GetValue());
}
break;
case kCslSleep:
mCslState = kCslSample;
mCslSampleTime += mCslPeriod * kUsPerTenSymbols;
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
mCslTimer.StartAt(mCslSampleTime, kCslSampleWindow);
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
otLogDebgMac("CSL Sample %u", mCslTimer.GetNow().GetValue());
}
break;
case kCslIdle:
break;
default:
OT_ASSERT(false);
break;
}
}
uint16_t SubMac::GetCslPhase(void) const
{
TimeMicro now = TimerMicro::GetNow();
uint32_t delta;
if (mCslSampleTime < now)
{
delta = mCslSampleTime + mCslPeriod * kUsPerTenSymbols - now;
}
else
{
delta = mCslSampleTime - now;
}
return static_cast<uint16_t>(delta / kUsPerTenSymbols);
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
} // namespace Mac
} // namespace ot
+116
View File
@@ -297,6 +297,22 @@ public:
*/
otError Receive(uint8_t aChannel);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method lets `SubMac` start CSL sample.
*
* `SubMac` would switch the radio state between `Receive` and `Sleep` according the CSL timer. When CslSample is
* started, `mState` will become `kStateCslSample`. But it could be doing `Sleep` or `Receive` at this moment
* (depending on `mCslState`).
*
* @retval OT_ERROR_NONE Successfully entered CSL operation (sleep or receive according to CSL timer).
* @retval OT_ERROR_BUSY The radio was transmitting.
* @retval OT_ERROR_INVALID_STATE The radio was disabled.
*
*/
otError CslSample(void);
#endif
/**
* This method gets the radio transmit frame.
*
@@ -355,6 +371,65 @@ public:
*/
int8_t GetNoiseFloor(void);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method gets the CSL channel.
*
* @returns CSL channel.
*
*/
uint8_t GetCslChannel(void) const { return mCslChannel; }
/**
* This method sets the CSL channel.
*
* @param[in] aChannel The CSL channel.
*
*/
void SetCslChannel(uint8_t aChannel);
/**
* This method gets the CSL period.
*
* @returns CSL period.
*
*/
uint16_t GetCslPeriod(void) const { return mCslPeriod; }
/**
* This method sets the CSL period.
*
* @param[in] aPeriod The CSL period in 10 symbols.
*
*/
void SetCslPeriod(uint16_t aPeriod);
/**
* This method gets the CSL timeout.
*
* @returns CSL timeout
*
*/
uint32_t GetCslTimeout(void) const { return mCslTimeout; }
/**
* This method sets the CSL timeout.
*
* @param[in] aTimeout The CSL timeout in seconds.
*
*/
void SetCslTimeout(uint32_t aTimeout);
/**
* This method fills the CSL parameter to the frame.
*
* @param[inout] aFrame A reference to the frame to fill CSL parameter.
*
*/
void FillCsl(Frame &aFrame);
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method sets MAC keys and key index.
*
@@ -408,6 +483,12 @@ public:
void SetFrameCounter(uint32_t aFrameCounter);
private:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static void HandleCslTimer(Timer &aTimer);
void HandleCslTimer(void);
uint16_t GetCslPhase(void) const;
#endif
enum
{
kMinBE = 3, ///< macMinBE (IEEE 802.15.4-2006).
@@ -431,6 +512,12 @@ private:
kStateCsmaBackoff, ///< CSMA backoff before transmission.
kStateTransmit, ///< Radio is transmitting.
kStateEnergyScan, ///< Energy scan.
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
kStateCslTransmit, ///< CSL transmission.
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
kStateCslSample, ///< CSL receive.
#endif
};
bool RadioSupportsCsmaBackoff(void) const
@@ -489,6 +576,35 @@ private:
#else
TimerMilli mTimer;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* The SSED sample window in units of 10 symbols.
*
*/
enum : uint32_t{
kCslSampleWindow = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * kUsPerTenSymbols,
};
/**
* Csl state, always updated by `mCslTimer`.
*
*/
enum CslState : uint8_t{
kCslIdle = 0, ///< CSL receiver is not started.
kCslSample, ///< Sampling CSL channel.
kCslSleep, ///< Radio in sleep.
};
uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds.
TimeMicro mCslSampleTime; ///< The CSL sample time of the current period.
uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds).
uint8_t mCslChannel; ///< The CSL sample channel.
CslState mCslState;
TimerMicro mCslTimer;
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
};
/**
+34
View File
@@ -44,6 +44,16 @@
namespace ot {
enum
{
kUsPerTenSymbols = OT_US_PER_TEN_SYMBOLS, ///< The microseconds per 10 symbols.
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
kMinCslPeriod = OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD * 1000 /
kUsPerTenSymbols, ///< Minimum CSL period supported in units of 10 symbols.
kMaxCslTimeout = OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT
#endif
};
/**
* @addtogroup core-radio
*
@@ -403,6 +413,30 @@ public:
*/
otError Receive(uint8_t aChannel) { return otPlatRadioReceive(GetInstance(), aChannel); }
/**
* This method updates the csl sample time in radio.
*
* @param[in] aCslSampleTime The csl sample time.
*
*/
void UpdateCslSampleTime(uint32_t aCslSampleTime) { otPlatRadioUpdateCslSampleTime(GetInstance(), aCslSampleTime); }
/** This method enables csl sampling in radio.
*
* @param[in] aCslPeriod CSL period, 0 for disabling CSL.
* @param[in] aExtAddr The extended source address of CSL receiver's parent device (when the platforms
* generate enhanced ack, platforms may need to know acks to which address should include CSL IE).
*
* @retval OT_ERROR_NOT_SUPPORTED Radio driver doesn't support CSL.
* @retval OT_ERROR_FAILED Other platform specific errors.
* @retval OT_ERROR_NONE Successfully enabled or disabled CSL.
*
*/
otError EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr)
{
return otPlatRadioEnableCsl(GetInstance(), aCslPeriod, aExtAddr);
}
/**
* This method gets the radio transmit frame buffer.
*
+269
View File
@@ -0,0 +1,269 @@
/*
* Copyright (c) 2020, 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 "csl_tx_scheduler.hpp"
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/time.hpp"
#include "mac/mac.hpp"
namespace ot {
CslTxScheduler::Callbacks::Callbacks(Instance &aInstance)
: InstanceLocator(aInstance)
{
}
inline otError CslTxScheduler::Callbacks::PrepareFrameForChild(Mac::TxFrame &aFrame,
FrameContext &aContext,
Child & aChild)
{
return Get<IndirectSender>().PrepareFrameForChild(aFrame, aContext, aChild);
}
inline void CslTxScheduler::Callbacks::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
const FrameContext &aContext,
otError aError,
Child & aChild)
{
Get<IndirectSender>().HandleSentFrameToChild(aFrame, aContext, aError, aChild);
}
//---------------------------------------------------------
//
CslTxScheduler::CslTxScheduler(Instance &aInstance)
: InstanceLocator(aInstance)
, mCslTxChild(nullptr)
, mCslTxMessage(nullptr)
, mFrameContext()
, mCallbacks(aInstance)
{
}
void CslTxScheduler::Update(void)
{
if (mCslTxMessage == nullptr)
{
RescheduleCslTx();
}
else if ((mCslTxChild != nullptr) && (mCslTxChild->GetIndirectMessage() != mCslTxMessage))
{
// `Mac` has already started the CSL tx, so wait for tx done callback
// to call `RescheduleCslTx`
mCslTxChild = nullptr;
mFrameContext.mMessageNextOffset = 0;
}
}
void CslTxScheduler::Clear(void)
{
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
{
child.SetCslTxAttempts(0);
child.SetCslSynchronized(false);
child.SetCslChannel(0);
child.SetCslTimeout(0);
child.SetCslPeriod(0);
child.SetCslPhase(0);
child.SetCslLastHeard(TimeMilli(0));
}
mFrameContext.mMessageNextOffset = 0;
mCslTxChild = nullptr;
mCslTxMessage = nullptr;
}
/**
* This method always finds the most recent csl tx among all children,
* and request `Mac` to do csl tx at specific time. It shouldn't be called
* when `Mac` is already starting to do the csl tx (indicated by `mCslTxMessage`).
*
*/
void CslTxScheduler::RescheduleCslTx(void)
{
uint64_t radioNow = otPlatRadioGetNow(&GetInstance());
uint32_t minDelayTime = TimeMicro::kMaxDuration;
Child * bestChild = nullptr;
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
{
uint32_t delay;
if (!child.IsCslSynchronized() || child.GetIndirectMessageCount() == 0 ||
child.GetCslTxAttempts() >= kMaxCslTriggeredTxAttempts)
{
continue;
}
delay = GetNextCslTransmissionDelay(child, radioNow);
if (delay < minDelayTime)
{
minDelayTime = delay;
bestChild = &child;
}
}
if (bestChild != nullptr)
{
Get<Mac::Mac>().RequestCslFrameTransmission(minDelayTime / 1000UL);
}
mCslTxChild = bestChild;
}
uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild, uint64_t aRadioNow)
{
uint32_t delay;
uint16_t period_offset = (aRadioNow / kUsPerTenSymbols) % aChild.GetCslPeriod();
if (aChild.GetCslPhase() > period_offset + kCslFrameRequestAheadThreshold)
{
delay = static_cast<uint16_t>(aChild.GetCslPhase() - period_offset - kCslFrameRequestAheadThreshold) *
kUsPerTenSymbols;
}
else
{
delay = static_cast<uint16_t>(aChild.GetCslPeriod() + aChild.GetCslPhase() - period_offset -
kCslFrameRequestAheadThreshold) *
kUsPerTenSymbols;
}
return delay;
}
otError CslTxScheduler::HandleFrameRequest(Mac::TxFrame &aFrame)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mCslTxChild != nullptr, error = OT_ERROR_ABORT);
SuccessOrExit(error = mCallbacks.PrepareFrameForChild(aFrame, mFrameContext, *mCslTxChild));
mCslTxMessage = mCslTxChild->GetIndirectMessage();
if (mCslTxChild->GetIndirectTxAttempts() > 0 || mCslTxChild->GetCslTxAttempts() > 0)
{
// For a re-transmission of an indirect frame to a sleepy
// child, we ensure to use the same frame counter, key id, and
// data sequence number as the previous attempt.
aFrame.SetIsARetransmission(true);
aFrame.SetSequence(mCslTxChild->GetIndirectDataSequenceNumber());
if (aFrame.GetSecurityEnabled())
{
aFrame.SetFrameCounter(mCslTxChild->GetIndirectFrameCounter());
aFrame.SetKeyId(mCslTxChild->GetIndirectKeyId());
}
}
else
{
aFrame.SetIsARetransmission(false);
}
aFrame.SetChannel(mCslTxChild->GetCslChannel());
aFrame.SetTxPhase(mCslTxChild->GetCslPhase());
aFrame.SetTxPeriod(mCslTxChild->GetCslPeriod());
exit:
return error;
}
void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, otError aError)
{
Child *child = mCslTxChild;
VerifyOrExit(child != nullptr, OT_NOOP); // The result is no longer interested by upper layer
mCslTxChild = nullptr;
mCslTxMessage = nullptr;
HandleSentFrame(aFrame, aError, *child);
exit:
return;
}
void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, otError aError, Child &aChild)
{
switch (aError)
{
case OT_ERROR_NONE:
aChild.ResetCslTxAttempts();
aChild.ResetIndirectTxAttempts();
break;
case OT_ERROR_NO_ACK:
aChild.IncrementCslTxAttempts();
otLogInfoMac("Csl tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetCslTxAttempts(),
kMaxCslTriggeredTxAttempts);
// Fall through
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
case OT_ERROR_ABORT:
// Even if Csl Tx attempts count reaches max, the message won't be
// dropped until indirect tx attempts count reaches max. So here it
// would set sequence number and schedule next csl tx.
if (!aFrame.IsEmpty())
{
aChild.SetIndirectDataSequenceNumber(aFrame.GetSequence());
if (aFrame.GetSecurityEnabled())
{
uint32_t frameCounter;
uint8_t keyId;
IgnoreError(aFrame.GetFrameCounter(frameCounter));
aChild.SetIndirectFrameCounter(frameCounter);
IgnoreError(aFrame.GetKeyId(keyId));
aChild.SetIndirectKeyId(keyId);
}
}
RescheduleCslTx();
ExitNow();
default:
OT_ASSERT(false);
OT_UNREACHABLE_CODE(break);
}
mCallbacks.HandleSentFrameToChild(aFrame, mFrameContext, aError, aChild);
exit:
return;
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
+215
View File
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2020, 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 CSL_TX_SCHEDULER_HPP_
#define CSL_TX_SCHEDULER_HPP_
#include "openthread-core-config.h"
#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/time.hpp"
#include "mac/mac.hpp"
#include "mac/mac_frame.hpp"
#include "thread/indirect_sender_frame_context.hpp"
namespace ot {
/**
* @addtogroup core-mesh-forwarding
*
* @brief
* This module includes definitions for CSL transmission scheduling.
*
* @{
*/
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
class Child;
/**
* This class implements CSL tx scheduling functionality.
*
*/
class CslTxScheduler : public InstanceLocator
{
friend class Mac::Mac;
friend class IndirectSender;
public:
enum
{
kMaxCslTriggeredTxAttempts = OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS,
kCslFrameRequestAheadThreshold = 2000 / kUsPerTenSymbols,
};
/**
* This class defines all the child info required for scheduling CSL transmissions.
*
* `Child` class publicly inherits from this class.
*
*/
class ChildInfo
{
public:
uint8_t GetCslTxAttempts(void) const { return mCslTxAttempts; }
void SetCslTxAttempts(uint8_t aCslTxAttempts) { mCslTxAttempts = aCslTxAttempts; }
void IncrementCslTxAttempts(void) { mCslTxAttempts++; }
void ResetCslTxAttempts(void) { SetCslTxAttempts(0); }
bool IsCslSynchronized(void) const { return mCslSynchronized && mCslPeriod > 0; }
void SetCslSynchronized(bool aCslSynchronized) { mCslSynchronized = aCslSynchronized; }
uint8_t GetCslChannel(void) const { return mCslChannel; }
void SetCslChannel(uint8_t aChannel) { mCslChannel = aChannel; }
uint32_t GetCslTimeout(void) const { return mCslTimeout; }
void SetCslTimeout(uint32_t aTimeout) { mCslTimeout = aTimeout; }
uint16_t GetCslPeriod(void) const { return mCslPeriod; }
void SetCslPeriod(uint16_t aPeriod) { mCslPeriod = aPeriod; }
uint16_t GetCslPhase(void) const { return mCslPhase; }
void SetCslPhase(uint16_t aPhase) { mCslPhase = aPhase; }
TimeMilli GetCslLastHeard(void) const { return mCslLastHeard; }
void SetCslLastHeard(TimeMilli aCslLastHeard) { mCslLastHeard = aCslLastHeard; }
private:
uint8_t mCslTxAttempts : 7; ///< Number of csl triggered tx attempts.
bool mCslSynchronized : 1; ///< Indicates whether or not the child is CSL synchronized.
uint8_t mCslChannel; ///< The channel the device will listen on.
uint32_t mCslTimeout; ///< The sync timeout, in seconds.
uint16_t mCslPeriod; ///< CSL sampled listening period in units of 10 symbols (160 microseconds).
uint16_t mCslPhase; ///< The time when the next CSL sample will start.
TimeMilli mCslLastHeard; ///< Time when last frame containing CSL IE heard.
static_assert(kMaxCslTriggeredTxAttempts < (1 << 7), "mCslTxAttempts cannot fit max!");
};
/**
* This class defines the callbacks used by the `CslTxScheduler`.
*
*/
class Callbacks : public InstanceLocator
{
friend class CslTxScheduler;
private:
typedef IndirectSenderBase::FrameContext FrameContext;
/**
* This constructor initializes the callbacks object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit Callbacks(Instance &aInstance);
/**
* This callback method requests a frame to be prepared for CSL transmission to a given SSED.
*
* @param[out] aFrame A reference to a MAC frame where the new frame would be placed.
* @param[out] aContext A reference to a `FrameContext` where the context for the new frame would be placed.
* @param[in] aChild The child for which to prepare the frame.
*
* @retval OT_ERROR_NONE Frame was prepared successfully.
* @retval OT_ERROR_ABORT CSL transmission should be aborted (no frame for the child).
*
*/
otError PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild);
/**
* This callback method notifies the end of CSL frame transmission to a child.
*
* @param[in] aFrame The transmitted frame.
* @param[in] aContext The context associated with the frame when it was prepared.
* @param[in] aError OT_ERROR_NONE when the frame was transmitted successfully,
* OT_ERROR_NO_ACK when the frame was transmitted but no ACK was received,
* OT_ERROR_CHANNEL_ACCESS_FAILURE tx failed due to activity on the channel,
* OT_ERROR_ABORT when transmission was aborted for other reasons.
* @param[in] aChild The child to which the frame was transmitted.
*
*/
void HandleSentFrameToChild(const Mac::TxFrame &aFrame,
const FrameContext &aContext,
otError aError,
Child & aChild);
};
/**
* This constructor initializes the csl tx scheduler object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit CslTxScheduler(Instance &aInstance);
/**
* This method updates the next CSL transmission (finds the nearest child).
*
* It would then request the `Mac` to do the CSL tx. If the last CSL tx has been fired at `Mac` but hasn't been
* done yet, and it's aborted, this method would set `mCslTxChild` to `nullptr` to notify the `HandleTransmitDone`
* that the operation has been aborted.
*
*/
void Update(void);
/**
* This method clears all the states inside `CslTxScheduler` and the related states in each child.
*
*/
void Clear(void);
private:
void RescheduleCslTx(void);
uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint64_t aRadioNow);
// Callbacks from `Mac`
otError HandleFrameRequest(Mac::TxFrame &aFrame);
void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError);
void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError, Child &aChild);
Child * mCslTxChild;
Message * mCslTxMessage;
Callbacks::FrameContext mFrameContext;
Callbacks mCallbacks;
};
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
/**
* @}
*
*/
} // namespace ot
#endif // CSL_TX_SCHEDULER_HPP_
+24
View File
@@ -65,6 +65,9 @@ IndirectSender::IndirectSender(Instance &aInstance)
, mEnabled(false)
, mSourceMatchController(aInstance)
, mDataPollHandler(aInstance)
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
, mCslTxScheduler(aInstance)
#endif
{
}
@@ -79,6 +82,9 @@ void IndirectSender::Stop(void)
}
mDataPollHandler.Clear();
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Clear();
#endif
exit:
mEnabled = false;
@@ -147,6 +153,9 @@ void IndirectSender::ClearAllMessagesForSleepyChild(Child &aChild)
mSourceMatchController.ResetMessageCount(aChild);
mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Update();
#endif
exit:
return;
@@ -189,6 +198,9 @@ void IndirectSender::HandleChildModeChange(Child &aChild, Mle::DeviceMode aOldMo
mSourceMatchController.ResetMessageCount(aChild);
mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Update();
#endif
}
// Since the queuing delays for direct transmissions are expected to
@@ -255,6 +267,9 @@ void IndirectSender::RequestMessageUpdate(Child &aChild)
aChild.SetWaitingForMessageUpdate(true);
mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Update();
#endif
ExitNow();
}
@@ -284,6 +299,9 @@ void IndirectSender::RequestMessageUpdate(Child &aChild)
aChild.SetWaitingForMessageUpdate(true);
mDataPollHandler.RequestFrameChange(DataPollHandler::kReplaceFrame, aChild);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Update();
#endif
exit:
return;
@@ -312,6 +330,9 @@ void IndirectSender::UpdateIndirectMessage(Child &aChild)
Mac::Address childAddress;
mDataPollHandler.HandleNewFrame(aChild);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Update();
#endif
aChild.GetMacAddress(childAddress);
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, &childAddress, OT_ERROR_NONE);
@@ -473,6 +494,9 @@ void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
{
aChild.SetIndirectFragmentOffset(nextOffset);
mDataPollHandler.HandleNewFrame(aChild);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
mCslTxScheduler.Update();
#endif
ExitNow();
}
+9
View File
@@ -40,6 +40,7 @@
#include "common/message.hpp"
#include "mac/data_poll_handler.hpp"
#include "mac/mac_frame.hpp"
#include "thread/csl_tx_scheduler.hpp"
#include "thread/indirect_sender_frame_context.hpp"
#include "thread/mle_types.hpp"
#include "thread/src_match_controller.hpp"
@@ -65,6 +66,9 @@ class IndirectSender : public InstanceLocator, public IndirectSenderBase
{
friend class Instance;
friend class DataPollHandler::Callbacks;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
friend class CslTxScheduler::Callbacks;
#endif
public:
/**
@@ -76,6 +80,8 @@ public:
class ChildInfo
{
friend class IndirectSender;
friend class DataPollHandler;
friend class CslTxScheduler;
friend class SourceMatchController;
public:
@@ -221,6 +227,9 @@ private:
bool mEnabled;
SourceMatchController mSourceMatchController;
DataPollHandler mDataPollHandler;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
CslTxScheduler mCslTxScheduler;
#endif
};
/**
@@ -73,6 +73,7 @@ public:
struct FrameContext
{
friend class IndirectSender;
friend class CslTxScheduler;
private:
uint16_t mMessageNextOffset; ///< The next offset into the message associated with the prepared frame.
+7 -2
View File
@@ -434,7 +434,12 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame)
{
SuccessOrExit(error = Get<Mle::DiscoverScanner>().PrepareDiscoveryRequestFrame(aFrame));
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().IsCslEnabled() && mSendMessage->IsSubTypeMle())
{
mSendMessage->SetLinkSecurityEnabled(true);
}
#endif
mMessageNextOffset =
PrepareDataFrame(aFrame, *mSendMessage, mMacSource, mMacDest, mAddMeshHeader, mMeshSource, mMeshDest);
@@ -508,7 +513,7 @@ start:
// Initialize MAC header
fcf = Mac::Frame::kFcfFrameData;
Get<Mac::Mac>().UpdateFrameControlField(aMessage.IsTimeSync(), fcf);
Get<Mac::Mac>().UpdateFrameControlField(Get<NeighborTable>().FindNeighbor(aMacDest), aMessage.IsTimeSync(), fcf);
fcf |= (aMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (aMacSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
+16 -1
View File
@@ -346,13 +346,28 @@ void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame)
// initialize MAC header
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfDstAddrShort |
Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfAckRequest | Mac::Frame::kFcfSecurityEnabled;
Get<Mac::Mac>().UpdateFrameControlField(aMessage.IsTimeSync(), fcf);
Get<Mac::Mac>().UpdateFrameControlField(nullptr, aMessage.IsTimeSync(), fcf);
aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
aFrame.SetDstPanId(Get<Mac::Mac>().GetPanId());
aFrame.SetDstAddr(mMacDest.GetShort());
aFrame.SetSrcAddr(mMacSource.GetShort());
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().IsCslEnabled())
{
Mac::HeaderIe ieList[2]; // CSL + Termination
ieList[0].Init();
ieList[0].SetId(Mac::Frame::kHeaderIeCsl);
ieList[0].SetLength(sizeof(Mac::CslIe));
ieList[1].Init();
ieList[1].SetId(Mac::Frame::kHeaderIeTermination2);
ieList[1].SetLength(0);
IgnoreError(aFrame.AppendHeaderIe(ieList, 2));
}
#endif
// write payload
OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength());
aMessage.Read(0, aMessage.GetLength(), aFrame.GetPayload());
+62 -1
View File
@@ -174,6 +174,12 @@ exit:
return error;
}
void Mle::ScheduleChildUpdateRequest(void)
{
mChildUpdateRequestState = kChildUpdateRequestPending;
ScheduleMessageTransmissionTimer();
}
otError Mle::Disable(void)
{
otError error = OT_ERROR_NONE;
@@ -676,6 +682,13 @@ void Mle::SetStateChild(uint16_t aRloc16)
InformPreviousParent();
mPreviousParentRloc = mParent.GetRloc16();
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().IsCslEnabled())
{
ScheduleChildUpdateRequest();
}
#endif
}
void Mle::InformPreviousChannel(void)
@@ -1376,6 +1389,32 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otError Mle::AppendCslChannel(Message &aMessage)
{
otError error = OT_ERROR_NONE;
CslChannelTlv cslChannel;
VerifyOrExit(Get<Mac::Mac>().GetPanChannel() != Get<Mac::Mac>().GetCslChannel(), OT_NOOP);
cslChannel.Init();
cslChannel.SetChannelPage(0);
cslChannel.SetChannel(Get<Mac::Mac>().GetCslChannel());
SuccessOrExit(error = aMessage.Append(&cslChannel, sizeof(CslChannelTlv)));
exit:
return error;
}
otError Mle::AppendCslTimeout(Message &aMessage)
{
OT_ASSERT(Get<Mac::Mac>().IsCslEnabled());
return Tlv::AppendUint32Tlv(aMessage, Tlv::kCslTimeout,
Get<Mac::Mac>().GetCslTimeout() == 0 ? mTimeout : Get<Mac::Mac>().GetCslTimeout());
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void Mle::HandleNotifierEvents(Events aEvents)
{
VerifyOrExit(!IsDisabled(), OT_NOOP);
@@ -2066,7 +2105,18 @@ void Mle::ScheduleMessageTransmissionTimer(void)
ExitNow(interval = kChildUpdateRequestPendingDelay);
case kChildUpdateRequestActive:
ExitNow(interval = kUnicastRetransmissionDelay);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// CSL transmitter may respond in next CSL cycle.
if (Get<Mac::Mac>().IsCslEnabled())
{
ExitNow(interval = Get<Mac::Mac>().GetCslPeriod() * kUsPerTenSymbols / 1000 +
static_cast<uint32_t>(kUnicastRetransmissionDelay));
}
else
#endif
{
ExitNow(interval = kUnicastRetransmissionDelay);
}
}
switch (mDataRequestState)
@@ -2195,6 +2245,13 @@ otError Mle::SendChildUpdateRequest(void)
SuccessOrExit(error = AppendSourceAddress(*message));
SuccessOrExit(error = AppendLeaderData(*message));
SuccessOrExit(error = AppendTimeout(*message, mTimeout));
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().IsCslEnabled())
{
SuccessOrExit(error = AppendCslChannel(*message));
SuccessOrExit(error = AppendCslTimeout(*message));
}
#endif
break;
case kRoleDisabled:
@@ -2216,7 +2273,11 @@ otError Mle::SendChildUpdateRequest(void)
if (!IsRxOnWhenIdle())
{
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
Get<DataPollSender>().SetAttachMode(!Get<Mac::Mac>().IsCslEnabled());
#else
Get<DataPollSender>().SetAttachMode(true);
#endif
Get<MeshForwarder>().SetRxOnWhenIdle(false);
}
else
+30
View File
@@ -918,6 +918,12 @@ public:
otError GetLocatorAddress(Ip6::Address &aAddress, uint16_t aLocator) const;
/**
* This method schedules a Child Update Request.
*
*/
void ScheduleChildUpdateRequest(void);
/*
* This method indicates whether or not the device has restored the network information from
* non-volatile settings after boot.
*
@@ -1349,6 +1355,30 @@ protected:
otError AppendXtalAccuracy(Message &aMessage);
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method appends a CSL Channel TLV to a message.
*
* @param[in] aMessage A reference to the message.
*
* @retval OT_ERROR_NONE Successfully appended the CSL Channel TLV.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to append the CSL Channel TLV.
*
*/
otError AppendCslChannel(Message &aMessage);
/**
* This method appends a CSL Sync Timeout TLV to a message.
*
* @param[in] aMessage A reference to the message.
*
* @retval OT_ERROR_NONE Successfully appended the CSL Timeout TLV.
* @retval OT_ERROR_NO_BUFS Insufficient buffers available to append the CSL Timeout TLV.
*
*/
otError AppendCslTimeout(Message &aMessage);
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method appends a Active Timestamp TLV to a message.
*
+32
View File
@@ -1853,6 +1853,16 @@ void MleRouter::HandleStateUpdateTimer(void)
OT_UNREACHABLE_CODE(break);
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (child.IsCslSynchronized() &&
TimerMilli::GetNow() - child.GetCslLastHeard() >= Time::SecToMsec(child.GetCslTimeout()))
{
otLogInfoMle("Child CSL synchronization expired");
child.SetCslSynchronized(false);
Get<CslTxScheduler>().Update();
}
#endif
if (TimerMilli::GetNow() - child.GetLastHeard() >= timeout)
{
otLogInfoMle("Child timeout expired");
@@ -2523,6 +2533,28 @@ void MleRouter::HandleChildUpdateRequest(const Message & aMessage,
ExitNow(error = OT_ERROR_PARSE);
}
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (child->IsCslSynchronized())
{
CslChannelTlv cslChannel;
uint32_t cslTimeout;
if (Tlv::FindUint32Tlv(aMessage, Tlv::kCslTimeout, cslTimeout) == OT_ERROR_NONE)
{
child->SetCslTimeout(cslTimeout);
}
if (Tlv::FindTlv(aMessage, Tlv::kCslChannel, sizeof(cslChannel), cslChannel) == OT_ERROR_NONE)
{
child->SetCslChannel(static_cast<uint8_t>(cslChannel.GetChannel()));
}
else
{
child->SetCslChannel(Get<Mac::Mac>().GetPanChannel());
}
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
child->SetLastHeard(TimerMilli::GetNow());
if (oldMode != child->GetDeviceMode())
+69
View File
@@ -101,6 +101,8 @@ public:
kActiveDataset = 24, ///< Active Operational Dataset TLV
kPendingDataset = 25, ///< Pending Operational Dataset TLV
kDiscovery = 26, ///< Thread Discovery TLV
kCslChannel = 80, ///< CSL Channel TLV
kCslTimeout = 85, ///< CSL Timeout TLV
/**
* Applicable/Required only when time synchronization service
@@ -1196,6 +1198,73 @@ public:
bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); }
} OT_TOOL_PACKED_END;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
/**
* This class implements CSL Channel TLV generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class CslChannelTlv : public Tlv
{
public:
/**
* This method initializes the TLV.
*
*/
void Init(void)
{
SetType(kCslChannel);
SetLength(sizeof(*this) - sizeof(Tlv));
}
/**
* This method indicates whether or not the TLV appears to be well-formed.
*
* @retval TRUE If the TLV appears to be well-formed.
* @retval FALSE If the TLV does not appear to be well-formed.
*
*/
bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); }
/**
* This method returns the Channel Page value.
*
* @returns The Channel Page value.
*
*/
uint8_t GetChannelPage(void) const { return mChannelPage; }
/**
* This method sets the Channel Page value.
*
* @param[in] aChannelPage The Channel Page value.
*
*/
void SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; }
/**
* This method returns the Channel value.
*
* @returns The Channel value.
*
*/
uint16_t GetChannel(void) const { return HostSwap16(mChannel); }
/**
* This method sets the Channel value.
*
* @param[in] aChannel The Channel value.
*
*/
void SetChannel(uint16_t aChannel) { mChannel = HostSwap16(aChannel); }
private:
uint8_t mChannelPage;
uint16_t mChannel;
} OT_TOOL_PACKED_END;
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
/**
* @}
*
+8 -1
View File
@@ -45,6 +45,7 @@
#include "common/timer.hpp"
#include "mac/mac_types.hpp"
#include "net/ip6.hpp"
#include "thread/csl_tx_scheduler.hpp"
#include "thread/indirect_sender.hpp"
#include "thread/link_quality.hpp"
#include "thread/mle_tlvs.hpp"
@@ -614,7 +615,13 @@ private:
* This class represents a Thread Child.
*
*/
class Child : public Neighbor, public IndirectSender::ChildInfo, public DataPollHandler::ChildInfo
class Child : public Neighbor,
public IndirectSender::ChildInfo,
public DataPollHandler::ChildInfo
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
,
public CslTxScheduler::ChildInfo
#endif
{
class AddressIteratorBuilder;
+1
View File
@@ -229,6 +229,7 @@ def create_default_mle_tlvs_factories():
mle.TlvType.PANID: mle.PanIdFactory(),
mle.TlvType.ACTIVE_TIMESTAMP: mle.ActiveTimestampFactory(),
mle.TlvType.PENDING_TIMESTAMP: mle.PendingTimestampFactory(),
mle.TlvType.CSL_SYNCHRONIZED_TIMEOUT: mle.CslSynchronizedTimeoutFactory(),
mle.TlvType.ACTIVE_OPERATIONAL_DATASET: mle.ActiveOperationalDatasetFactory(),
mle.TlvType.PENDING_OPERATIONAL_DATASET: mle.PendingOperationalDatasetFactory(),
mle.TlvType.TIME_REQUEST: mle.TimeRequestFactory(),
+1
View File
@@ -76,6 +76,7 @@ class TlvType(IntEnum):
PERIOD = 55
SCAN_DURATION = 56
ENERGY_LIST = 57
CSL_SYNCHRONIZED_TIMEOUT = 85
DISCOVERY_REQUEST = 128
DISCOVERY_RESPONSE = 129
+14
View File
@@ -87,6 +87,7 @@ class TlvType(IntEnum):
ACTIVE_OPERATIONAL_DATASET = 24
PENDING_OPERATIONAL_DATASET = 25
THREAD_DISCOVERY = 26
CSL_SYNCHRONIZED_TIMEOUT = 85
TIME_REQUEST = 252
TIME_PARAMETER = 253
@@ -1047,6 +1048,19 @@ class ThreadDiscoveryFactory:
return ThreadDiscovery(tlvs)
class CslSynchronizedTimeout:
# TODO: Not implemented yet
def __init__(self):
print("CslSynchronizedTimeout is not implemented yet.")
class CslSynchronizedTimeoutFactory:
def parse(self, data, message_info):
return CslSynchronizedTimeout()
class TimeRequest:
# TODO: Not implemented yet
+16
View File
@@ -660,6 +660,22 @@ class Node:
self.send_command('pollperiod %d' % pollperiod)
self._expect('Done')
def get_csl_info(self):
self.send_command('csl')
self._expect('Done')
def set_csl_channel(self, csl_channel):
self.send_command('csl channel %d' % csl_channel)
self._expect('Done')
def set_csl_period(self, csl_period):
self.send_command('csl period %d' % csl_period)
self._expect('Done')
def set_csl_timeout(self, csl_timeout):
self.send_command('csl timeout %d' % csl_timeout)
self._expect('Done')
def set_router_upgrade_threshold(self, threshold):
cmd = 'routerupgradethreshold %d' % threshold
self.send_command(cmd)
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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.
#
import unittest
import thread_cert
LEADER = 1
SSED_1 = 2
CSL_PERIOD = 500 * 6.25 # 500ms
CSL_TIMEOUT = 30 # 30s
CSL_CHANNEL = 12
class SSED_CslTransmission(thread_cert.TestCase):
TOPOLOGY = {
LEADER: {
'version': '1.2',
},
SSED_1: {
'version': '1.2',
'mode': 's',
},
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[SSED_1].set_csl_period(CSL_PERIOD)
self.nodes[SSED_1].set_csl_timeout(CSL_TIMEOUT)
self.nodes[SSED_1].set_csl_channel(CSL_CHANNEL)
self.nodes[SSED_1].get_csl_info()
self.nodes[LEADER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[SSED_1].start()
self.simulator.go(7)
self.assertEqual(self.nodes[SSED_1].get_state(), 'child')
print('SSED rloc:%s' % self.nodes[SSED_1].get_rloc())
self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED_1].get_rloc()))
self.simulator.go(5)
self.nodes[SSED_1].set_csl_period(0)
self.assertFalse(self.nodes[LEADER].ping(self.nodes[SSED_1].get_rloc()))
self.simulator.go(2)
self.nodes[SSED_1].set_pollperiod(1000)
self.simulator.go(2)
self.nodes[SSED_1].set_pollperiod(0)
self.simulator.go(5)
if __name__ == '__main__':
unittest.main()
+18 -10
View File
@@ -592,20 +592,28 @@ void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffse
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
uint64_t otPlatTimeGet(void)
{
struct timeval tv;
gettimeofday(&tv, nullptr);
return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
}
uint16_t otPlatTimeGetXtalAccuracy(void)
{
return 0;
}
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const otExtAddress *aExtAddr)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aCslPeriod);
OT_UNUSED_VARIABLE(aExtAddr);
return OT_ERROR_NONE;
}
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aCslSampleTime);
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_OTNS_ENABLE
void otPlatOtnsStatus(const char *aStatus)