[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:
Vaas Krishnamurthy
2017-06-28 12:25:48 -07:00
committed by Jonathan Hui
parent f8f167bc40
commit e9216475f1
3 changed files with 130 additions and 22 deletions
+109 -22
View File
@@ -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)
+10
View File
@@ -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;
};
/**
+11
View File
@@ -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_