mirror of
https://github.com/espressif/openthread.git
synced 2026-08-13 06:07:48 +00:00
[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:
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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[])
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user