mirror of
https://github.com/espressif/openthread.git
synced 2026-11-12 09:21:07 +00:00
[mac] hold-off sleep and tx during fragments (#1888)
* Pause transmission while waiting for data. * Adding feature to keep radio awake between fragments. In some platforms, it is more energy efficient to keep the radio awake between fragments (both received and sent) instead of sleeping and immediately waking up. This feature can be enabled using OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS.
This commit is contained in:
committed by
Jonathan Hui
parent
f8f167bc40
commit
e9216475f1
+109
-22
@@ -149,7 +149,11 @@ Mac::Mac(ThreadNetif &aThreadNetif):
|
||||
mWhitelist(),
|
||||
mBlacklist(),
|
||||
mTxFrame(static_cast<Frame *>(otPlatRadioGetTransmitBuffer(aThreadNetif.GetInstance()))),
|
||||
mKeyIdMode2FrameCounter(0)
|
||||
mKeyIdMode2FrameCounter(0),
|
||||
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
mDelaySleep(false),
|
||||
#endif
|
||||
mWaitingForData(false)
|
||||
{
|
||||
GenerateExtAddress(&mExtAddress);
|
||||
|
||||
@@ -353,7 +357,7 @@ void Mac::EnergyScanDone(int8_t aEnergyScanMaxRssi)
|
||||
// and start the next transmission task
|
||||
if (mScanChannels == 0 || mScanChannel > OT_RADIO_CHANNEL_MAX)
|
||||
{
|
||||
otPlatRadioReceive(GetInstance(), mChannel);
|
||||
RadioReceive(mChannel);
|
||||
mEnergyScanHandler(mScanContext, NULL);
|
||||
ScheduleNextTransmission();
|
||||
ExitNow();
|
||||
@@ -555,17 +559,17 @@ void Mac::NextOperation(void)
|
||||
{
|
||||
case kStateActiveScan:
|
||||
case kStateEnergyScan:
|
||||
otPlatRadioReceive(GetInstance(), mScanChannel);
|
||||
RadioReceive(mScanChannel);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (mRxOnWhenIdle || mReceiveTimer.IsRunning() || otPlatRadioGetPromiscuous(GetInstance()))
|
||||
{
|
||||
otPlatRadioReceive(GetInstance(), mChannel);
|
||||
RadioReceive(mChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
otPlatRadioSleep(GetInstance());
|
||||
RadioSleep();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -574,6 +578,8 @@ void Mac::NextOperation(void)
|
||||
|
||||
void Mac::ScheduleNextTransmission(void)
|
||||
{
|
||||
VerifyOrExit(!mWaitingForData);
|
||||
|
||||
if (mPendingScanRequest == kScanTypeActive)
|
||||
{
|
||||
mPendingScanRequest = kScanTypeNone;
|
||||
@@ -602,6 +608,9 @@ void Mac::ScheduleNextTransmission(void)
|
||||
}
|
||||
|
||||
NextOperation();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Mac::GenerateNonce(const ExtAddress &aAddress, uint32_t aFrameCounter, uint8_t aSecurityLevel, uint8_t *aNonce)
|
||||
@@ -830,9 +839,10 @@ void Mac::HandleBeginTransmit(void)
|
||||
}
|
||||
}
|
||||
|
||||
error = otPlatRadioReceive(GetInstance(), sendFrame.GetChannel());
|
||||
error = RadioReceive(sendFrame.GetChannel());
|
||||
assert(error == OT_ERROR_NONE);
|
||||
error = otPlatRadioTransmit(GetInstance(), static_cast<otRadioFrame *>(&sendFrame));
|
||||
error = RadioTransmit(&sendFrame);
|
||||
|
||||
assert(error == OT_ERROR_NONE);
|
||||
|
||||
if (sendFrame.GetAckRequest() && !(otPlatRadioGetCaps(GetInstance()) & OT_RADIO_CAPS_ACK_TIMEOUT))
|
||||
@@ -926,10 +936,16 @@ void Mac::TransmitDoneTask(otRadioFrame *aFrame, bool aRxPending, otError aError
|
||||
switch (mState)
|
||||
{
|
||||
case kStateTransmitData:
|
||||
if (aRxPending)
|
||||
{
|
||||
uint8_t commandId;
|
||||
|
||||
if ((frame->GetType() == Frame::kFcfFrameMacCmd) && (frame->GetCommandId(commandId) == OT_ERROR_NONE) &&
|
||||
(commandId == Frame::kMacCmdDataRequest) && (aRxPending))
|
||||
{
|
||||
mWaitingForData = true;
|
||||
mReceiveTimer.Start(kDataPollTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
// fall through
|
||||
|
||||
@@ -1030,10 +1046,16 @@ void Mac::TransmitDoneTask(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otErro
|
||||
switch (mState)
|
||||
{
|
||||
case kStateTransmitData:
|
||||
if (framePending)
|
||||
{
|
||||
uint8_t commandId;
|
||||
|
||||
if ((txFrame->GetType() == Frame::kFcfFrameMacCmd) && (txFrame->GetCommandId(commandId) == OT_ERROR_NONE) &&
|
||||
(commandId == Frame::kMacCmdDataRequest) && (framePending))
|
||||
{
|
||||
mWaitingForData = true;
|
||||
mReceiveTimer.Start(kDataPollTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
// fall through
|
||||
|
||||
@@ -1053,6 +1075,55 @@ exit:
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE
|
||||
|
||||
otError Mac::RadioTransmit(Frame *aSendFrame)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
|
||||
if (!mRxOnWhenIdle)
|
||||
{
|
||||
// Cancel delay sleep timer
|
||||
mReceiveTimer.Stop();
|
||||
|
||||
// Delay sleep if we have another frame pending to transmit
|
||||
mDelaySleep = aSendFrame->GetFramePending();
|
||||
}
|
||||
|
||||
#endif
|
||||
// Transmit packet
|
||||
return otPlatRadioTransmit(GetInstance(), static_cast<otRadioFrame *>(aSendFrame));
|
||||
}
|
||||
|
||||
otError Mac::RadioReceive(uint8_t aChannel)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
|
||||
if (!mRxOnWhenIdle)
|
||||
{
|
||||
// Cancel delay sleep timer
|
||||
mReceiveTimer.Stop();
|
||||
}
|
||||
|
||||
#endif
|
||||
// Receive
|
||||
return otPlatRadioReceive(GetInstance(), aChannel);
|
||||
}
|
||||
|
||||
void Mac::RadioSleep(void)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
|
||||
if (mDelaySleep)
|
||||
{
|
||||
// Restart delay sleep timer
|
||||
mReceiveTimer.Start(kSleepDelay);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
otPlatRadioSleep(GetInstance());
|
||||
}
|
||||
}
|
||||
|
||||
void Mac::HandleMacTimer(void *aContext)
|
||||
{
|
||||
static_cast<Mac *>(aContext)->HandleMacTimer();
|
||||
@@ -1072,7 +1143,7 @@ void Mac::HandleMacTimer(void)
|
||||
|
||||
if (mScanChannels == 0 || mScanChannel > OT_RADIO_CHANNEL_MAX)
|
||||
{
|
||||
otPlatRadioReceive(GetInstance(), mChannel);
|
||||
RadioReceive(mChannel);
|
||||
otPlatRadioSetPanId(GetInstance(), mPanId);
|
||||
mActiveScanHandler(mScanContext, NULL);
|
||||
ScheduleNextTransmission();
|
||||
@@ -1090,7 +1161,7 @@ void Mac::HandleMacTimer(void)
|
||||
|
||||
case kStateTransmitData:
|
||||
otLogDebgMac(GetInstance(), "Ack timer fired");
|
||||
otPlatRadioReceive(GetInstance(), mChannel);
|
||||
RadioReceive(mChannel);
|
||||
mCounters.mTxTotal++;
|
||||
|
||||
mTxFrame->GetDstAddr(addr);
|
||||
@@ -1125,17 +1196,19 @@ void Mac::HandleReceiveTimer(void *aContext)
|
||||
|
||||
void Mac::HandleReceiveTimer(void)
|
||||
{
|
||||
otLogDebgMac(GetInstance(), "Data poll timeout");
|
||||
|
||||
for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext)
|
||||
if (mWaitingForData)
|
||||
{
|
||||
receiver->HandleDataPollTimeout();
|
||||
otLogDebgMac(GetInstance(), "Data poll timeout");
|
||||
|
||||
mWaitingForData = false;
|
||||
|
||||
for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext)
|
||||
{
|
||||
receiver->HandleDataPollTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
if (mState == kStateIdle)
|
||||
{
|
||||
NextOperation();
|
||||
}
|
||||
ScheduleNextTransmission();
|
||||
}
|
||||
|
||||
void Mac::SentFrame(otError aError)
|
||||
@@ -1412,6 +1485,7 @@ void Mac::ReceiveDoneTask(Frame *aFrame, otError aError)
|
||||
int8_t rssi;
|
||||
bool receive = false;
|
||||
uint8_t commandId;
|
||||
bool scheduleNextTrasmission = false;
|
||||
otError error = aError;
|
||||
|
||||
mCounters.mRxTotal++;
|
||||
@@ -1562,10 +1636,18 @@ void Mac::ReceiveDoneTask(Frame *aFrame, otError aError)
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!mRxOnWhenIdle && dstaddr.mLength != 0)
|
||||
if (dstaddr.mLength != 0)
|
||||
{
|
||||
mReceiveTimer.Stop();
|
||||
otPlatRadioSleep(GetInstance());
|
||||
mWaitingForData = false;
|
||||
|
||||
if (!mRxOnWhenIdle)
|
||||
{
|
||||
mReceiveTimer.Stop();
|
||||
scheduleNextTrasmission = true;
|
||||
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
mDelaySleep = aFrame->GetFramePending();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
switch (aFrame->GetType())
|
||||
@@ -1609,6 +1691,11 @@ void Mac::ReceiveDoneTask(Frame *aFrame, otError aError)
|
||||
|
||||
exit:
|
||||
|
||||
if (scheduleNextTrasmission)
|
||||
{
|
||||
ScheduleNextTransmission();
|
||||
}
|
||||
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
if (aFrame == NULL)
|
||||
|
||||
@@ -77,6 +77,7 @@ enum
|
||||
|
||||
kAckTimeout = 16, ///< Timeout for waiting on an ACK (milliseconds).
|
||||
kDataPollTimeout = 100, ///< Timeout for receiving Data Frame (milliseconds).
|
||||
kSleepDelay = 300, ///< Max sleep delay when frame is pending (milliseconds).
|
||||
kNonceSize = 13, ///< Size of IEEE 802.15.4 Nonce (bytes).
|
||||
|
||||
kScanChannelsAll = OT_CHANNEL_ALL, ///< All channels.
|
||||
@@ -690,6 +691,10 @@ private:
|
||||
void StartCsmaBackoff(void);
|
||||
otError Scan(ScanType aType, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext);
|
||||
|
||||
otError RadioTransmit(Frame *aSendFrame);
|
||||
otError RadioReceive(uint8_t aChannel);
|
||||
void RadioSleep(void);
|
||||
|
||||
Timer mMacTimer;
|
||||
#if !OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_BACKOFF_TIMER
|
||||
Timer mBackoffTimer;
|
||||
@@ -751,6 +756,11 @@ private:
|
||||
|
||||
otMacCounters mCounters;
|
||||
uint32_t mKeyIdMode2FrameCounter;
|
||||
|
||||
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
bool mDelaySleep;
|
||||
#endif
|
||||
bool mWaitingForData;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -851,4 +851,15 @@
|
||||
#define OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @def OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
*
|
||||
* Define as 1 to stay awake between fragments while transmitting a large packet,
|
||||
* and to stay awake after receiving a packet with frame pending set to true.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
|
||||
#define OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS 0
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
|
||||
|
||||
Reference in New Issue
Block a user