From a3046925663715dd84aff3de1506aad9bc6d67fd Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 27 Jul 2017 13:06:25 -0700 Subject: [PATCH] [mac] track and ensure single operation running at a time (#2024) This commit contains different changes and enhancement to MAC layer implementation. It changes how the MAC layer maintains its operations. It is ensured that only a single operation is active at a time. An operation can be: - active or energy scan, - beacon or data frame transmission, - waiting for data (after data poll ack with frame pending), or, - idle operation (where radio is put in either rx or sleep mode). Method `StartOperation()` starts an operation and `FinishOperation()` is used to indicate when is is done. If there is an ongoing operation, a subsequent call to `StartOperation()` ensures that the next operation is marked as pending and gets started after the current one is finished. This commit also changes the `StartCsmaBackoff()` implementation such that the radio is put in either receive or sleep mode depending on the state `mRxOnWhenIdle` before starting the backoff timer. This ensures that on a sleepy device the radio is put to sleep while backing off for all transmission attempts including any MAC retries. Another change added in this commit is to allow a received data frame to be processed during an ongoing active/energy scan only if the current scan channel matches the receive channel. This commit also update `Mac::Frame` class by adding a new helper method `IsDataRequestCommand()` to indicate if the frame is a MAC Data Request command (data poll). --- src/core/mac/mac.cpp | 553 +++++++++++++++++++++++-------------- src/core/mac/mac.hpp | 65 ++--- src/core/mac/mac_frame.cpp | 13 + src/core/mac/mac_frame.hpp | 8 + 4 files changed, 398 insertions(+), 241 deletions(-) diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 13e29263f..125720ae5 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -88,6 +88,7 @@ void Mac::StartCsmaBackoff(void) { uint32_t backoffExponent = kMinBE + mTransmitAttempts + mCsmaAttempts; uint32_t backoff; + bool shouldReceive; if (backoffExponent > kMaxBE) { @@ -97,6 +98,38 @@ void Mac::StartCsmaBackoff(void) backoff = (otPlatRandomGet() % (1UL << backoffExponent)); backoff *= (static_cast(kUnitBackoffPeriod) * OT_RADIO_SYMBOL_TIME); + // Put the radio in either sleep or receive mode depending on + // `mRxOnWhenIdle` flag before starting the backoff timer. + + shouldReceive = (mRxOnWhenIdle || otPlatRadioGetPromiscuous(&GetInstance())); + + if (!shouldReceive) + { + if (RadioSleep() == OT_ERROR_INVALID_STATE) + { + // If `RadioSleep()` returns `OT_ERROR_INVALID_STATE` + // indicating sleep is being delayed, the radio should + // be put in receive mode. + + shouldReceive = true; + } + } + + if (shouldReceive) + { + switch (mOperation) + { + case kOperationActiveScan: + case kOperationEnergyScan: + RadioReceive(mScanChannel); + break; + + default: + RadioReceive(mChannel); + break; + } + } + #if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER mBackoffTimer.Start(backoff); #else // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER @@ -118,21 +151,27 @@ Mac::Mac(ThreadNetif &aThreadNetif): mSendTail(NULL), mReceiveHead(NULL), mReceiveTail(NULL), - mState(kStateIdle), + mOperation(kOperationIdle), + mPendingActiveScan(false), + mPendingEnergyScan(false), + mPendingTransmitBeacon(false), + mPendingTransmitData(false), + mPendingWaitingForData(false), + mRxOnWhenIdle(false), + mBeaconsEnabled(false), +#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS + mDelaySleep(false), +#endif mBeaconSequence(static_cast(otPlatRandomGet())), mDataSequence(static_cast(otPlatRandomGet())), - mRxOnWhenIdle(false), mCsmaAttempts(0), mTransmitAttempts(0), - mTransmitBeacon(false), - mBeaconsEnabled(false), - mPendingScanRequest(kScanTypeNone), - mScanChannel(OT_RADIO_CHANNEL_MIN), mScanChannels(0xff), mScanDuration(0), - mScanContext(NULL), - mActiveScanHandler(NULL), // initialize mActiveScanHandler and mEnergyScanHandler union + mScanChannel(OT_RADIO_CHANNEL_MIN), mEnergyScanCurrentMaxRssi(kInvalidRssiValue), + mScanContext(NULL), + mActiveScanHandler(NULL), // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union mEnergyScanSampleRssiTask(aThreadNetif.GetInstance(), &Mac::HandleEnergyScanSampleRssi, this), mPcapCallback(NULL), mPcapCallbackContext(NULL), @@ -140,11 +179,7 @@ Mac::Mac(ThreadNetif &aThreadNetif): mFilter(), #endif // OPENTHREAD_ENABLE_MAC_FILTER mTxFrame(static_cast(otPlatRadioGetTransmitBuffer(&aThreadNetif.GetInstance()))), - mKeyIdMode2FrameCounter(0), -#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS - mDelaySleep(false), -#endif - mWaitingForData(false) + mKeyIdMode2FrameCounter(0) { GenerateExtAddress(&mExtAddress); @@ -163,7 +198,7 @@ otError Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveSc { otError error; - SuccessOrExit(error = Scan(kScanTypeActive, aScanChannels, aScanDuration, aContext)); + SuccessOrExit(error = Scan(kOperationActiveScan, aScanChannels, aScanDuration, aContext)); mActiveScanHandler = aHandler; exit: @@ -174,19 +209,18 @@ otError Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergySc { otError error; - SuccessOrExit(error = Scan(kScanTypeEnergy, aScanChannels, aScanDuration, aContext)); + SuccessOrExit(error = Scan(kOperationEnergyScan, aScanChannels, aScanDuration, aContext)); mEnergyScanHandler = aHandler; exit: return error; } -otError Mac::Scan(ScanType aScanType, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext) +otError Mac::Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext) { otError error = OT_ERROR_NONE; - VerifyOrExit((mState != kStateActiveScan) && (mState != kStateEnergyScan) && (mPendingScanRequest == kScanTypeNone), - error = OT_ERROR_BUSY); + VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = OT_ERROR_BUSY); mScanContext = aContext; mScanChannels = (aScanChannels == 0) ? static_cast(kScanChannelsAll) : aScanChannels; @@ -201,22 +235,7 @@ otError Mac::Scan(ScanType aScanType, uint32_t aScanChannels, uint16_t aScanDura mScanChannel++; } - if (mState == kStateIdle) - { - if (aScanType == kScanTypeActive) - { - mState = kStateActiveScan; - StartCsmaBackoff(); - } - else if (aScanType == kScanTypeEnergy) - { - StartEnergyScan(); - } - } - else - { - mPendingScanRequest = aScanType; - } + StartOperation(aScanOperation); exit: return error; @@ -224,17 +243,17 @@ exit: bool Mac::IsActiveScanInProgress(void) { - return (mState == kStateActiveScan) || (mPendingScanRequest == kScanTypeActive); + return (mOperation == kOperationActiveScan) || (mPendingActiveScan); } bool Mac::IsEnergyScanInProgress(void) { - return (mState == kStateEnergyScan) || (mPendingScanRequest == kScanTypeEnergy); + return (mOperation == kOperationEnergyScan) || (mPendingEnergyScan); } bool Mac::IsInTransmitState(void) { - return (mState == kStateTransmitData) || (mState == kStateTransmitBeacon); + return (mOperation == kOperationTransmitData) || (mOperation == kOperationTransmitBeacon); } otError Mac::ConvertBeaconToActiveScanResult(Frame *aBeaconFrame, otActiveScanResult &aResult) @@ -284,14 +303,12 @@ exit: void Mac::StartEnergyScan(void) { - mState = kStateEnergyScan; - if (!(otPlatRadioGetCaps(&GetInstance()) & OT_RADIO_CAPS_ENERGY_SCAN)) { mEnergyScanCurrentMaxRssi = kInvalidRssiValue; mMacTimer.Start(mScanDuration); mEnergyScanSampleRssiTask.Post(); - NextOperation(); + RadioReceive(mScanChannel); } else { @@ -301,7 +318,7 @@ void Mac::StartEnergyScan(void) { // Cancel scan mEnergyScanHandler(mScanContext, NULL); - ScheduleNextTransmission(); + FinishOperation(); } } } @@ -345,12 +362,11 @@ void Mac::EnergyScanDone(int8_t aEnergyScanMaxRssi) mScanChannel++; // If we have scanned all the channels, then fire the final callback - // and start the next transmission task + // and finish scan operation. if (mScanChannels == 0 || mScanChannel > OT_RADIO_CHANNEL_MAX) { - RadioReceive(mChannel); mEnergyScanHandler(mScanContext, NULL); - ScheduleNextTransmission(); + FinishOperation(); ExitNow(); } } @@ -372,7 +388,7 @@ void Mac::HandleEnergyScanSampleRssi(void) { int8_t rssi; - VerifyOrExit(mState == kStateEnergyScan); + VerifyOrExit(mOperation == kOperationEnergyScan); rssi = otPlatRadioGetRssi(&GetInstance()); @@ -410,12 +426,29 @@ otError Mac::RegisterReceiver(Receiver &aReceiver) void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle) { + VerifyOrExit(mRxOnWhenIdle != aRxOnWhenIdle); + mRxOnWhenIdle = aRxOnWhenIdle; - if (mState == kStateIdle) + // If the new value for `mRxOnWhenIdle` is `true` (i.e., radio should + // remain in Rx while idle) we stop any ongoing or pending `WaitinForData` + // operation (since this operation only applies to sleepy devices). + + if (mRxOnWhenIdle) { - NextOperation(); + mPendingWaitingForData = false; + + if (mOperation == kOperationWaitingForData) + { + mReceiveTimer.Stop(); + FinishOperation(); + } } + + UpdateIdleMode(); + +exit: + return; } void Mac::GenerateExtAddress(ExtAddress *aExtAddress) @@ -482,11 +515,7 @@ otError Mac::SetChannel(uint8_t aChannel) VerifyOrExit(OT_RADIO_CHANNEL_MIN <= aChannel && aChannel <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_INVALID_ARGS); mChannel = aChannel; - - if (mState == kStateIdle) - { - NextOperation(); - } + UpdateIdleMode(); exit: otLogFuncExit(); @@ -540,76 +569,129 @@ otError Mac::SendFrameRequest(Sender &aSender) mSendTail = &aSender; } - if (mState == kStateIdle) - { - mState = kStateTransmitData; - StartCsmaBackoff(); - } + StartOperation(kOperationTransmitData); exit: return error; } -void Mac::NextOperation(void) +void Mac::UpdateIdleMode(void) { - switch (mState) + VerifyOrExit(mOperation == kOperationIdle); + + if (!mRxOnWhenIdle && !mReceiveTimer.IsRunning() && !otPlatRadioGetPromiscuous(&GetInstance())) { - case kStateActiveScan: - case kStateEnergyScan: - RadioReceive(mScanChannel); - break; - - default: - if (mRxOnWhenIdle || mReceiveTimer.IsRunning() || otPlatRadioGetPromiscuous(&GetInstance())) + if (RadioSleep() != OT_ERROR_INVALID_STATE) { - RadioReceive(mChannel); - } - else - { - RadioSleep(); + otLogDebgMac(GetInstance(), "Idle mode: Radio sleeping"); + ExitNow(); } - break; + // If `RadioSleep()` returns `OT_ERROR_INVALID_STATE` + // indicating sleep is being delayed, continue to put + // the radio in receive mode. } + + otLogDebgMac(GetInstance(), "Idle mode: Radio receiving on channel %d", mChannel); + RadioReceive(mChannel); + +exit: + return; } -void Mac::ScheduleNextTransmission(void) +void Mac::StartOperation(Operation aOperation) { - VerifyOrExit(!mWaitingForData); - - if (mPendingScanRequest == kScanTypeActive) + if (aOperation != kOperationIdle) { - mPendingScanRequest = kScanTypeNone; - mState = kStateActiveScan; + otLogDebgMac(GetInstance(), "Request to start operation \"%s\"", OperationToString(aOperation)); + } + + switch (aOperation) + { + case kOperationIdle: + break; + + case kOperationActiveScan: + mPendingActiveScan = true; + break; + + case kOperationEnergyScan: + mPendingEnergyScan = true; + break; + + case kOperationTransmitBeacon: + mPendingTransmitBeacon = true; + break; + + case kOperationTransmitData: + mPendingTransmitData = true; + break; + + case kOperationWaitingForData: + mPendingWaitingForData = true; + break; + } + + VerifyOrExit(mOperation == kOperationIdle); + + // `WaitingForData` should be checked before any other pending + // operations since radio should remain in receive mode after + // a data poll ack indicating a pending frame from parent. + + if (mPendingWaitingForData) + { + mPendingWaitingForData = false; + mOperation = kOperationWaitingForData; + RadioReceive(mChannel); + } + else if (mPendingActiveScan) + { + mPendingActiveScan = false; + mOperation = kOperationActiveScan; StartCsmaBackoff(); } - else if (mPendingScanRequest == kScanTypeEnergy) + else if (mPendingEnergyScan) { - mPendingScanRequest = kScanTypeNone; + mPendingEnergyScan = false; + mOperation = kOperationEnergyScan; StartEnergyScan(); } - else if (mTransmitBeacon) + else if (mPendingTransmitBeacon) { - mTransmitBeacon = false; - mState = kStateTransmitBeacon; + mPendingTransmitBeacon = false; + mOperation = kOperationTransmitBeacon; StartCsmaBackoff(); } - else if (mSendHead != NULL) + else if (mPendingTransmitData) { - mState = kStateTransmitData; + mPendingTransmitData = false; + mOperation = kOperationTransmitData; StartCsmaBackoff(); } else { - mState = kStateIdle; + UpdateIdleMode(); } - NextOperation(); + if (mOperation != kOperationIdle) + { + otLogDebgMac(GetInstance(), "Starting operation \"%s\"", OperationToString(mOperation)); + } exit: return; } +void Mac::FinishOperation(void) +{ + // Clear the current operation and start any pending ones. + + otLogDebgMac(GetInstance(), "Finishing operation \"%s\"", OperationToString(mOperation)); + + mOperation = kOperationIdle; + StartOperation(kOperationIdle); +} + void Mac::GenerateNonce(const ExtAddress &aAddress, uint32_t aFrameCounter, uint8_t aSecurityLevel, uint8_t *aNonce) { // source address @@ -796,9 +878,9 @@ void Mac::HandleBeginTransmit(void) { sendFrame.SetPower(mMaxTransmitPower); - switch (mState) + switch (mOperation) { - case kStateActiveScan: + case kOperationActiveScan: otPlatRadioSetPanId(&GetInstance(), kPanIdBroadcast); sendFrame.SetChannel(mScanChannel); SendBeaconRequest(sendFrame); @@ -806,14 +888,14 @@ void Mac::HandleBeginTransmit(void) sendFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts); break; - case kStateTransmitBeacon: + case kOperationTransmitBeacon: sendFrame.SetChannel(mChannel); SendBeacon(sendFrame); sendFrame.SetSequence(mBeaconSequence++); sendFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts); break; - case kStateTransmitData: + case kOperationTransmitData: sendFrame.SetChannel(mChannel); SuccessOrExit(error = mSendHead->HandleFrameRequest(sendFrame)); @@ -841,8 +923,8 @@ void Mac::HandleBeginTransmit(void) error = RadioReceive(sendFrame.GetChannel()); assert(error == OT_ERROR_NONE); - error = RadioTransmit(&sendFrame); + error = RadioTransmit(&sendFrame); assert(error == OT_ERROR_NONE); if (mPcapCallback) @@ -960,24 +1042,20 @@ void Mac::TransmitDoneTask(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otErro mCsmaAttempts = 0; - switch (mState) + switch (mOperation) { - case kStateTransmitData: - { - uint8_t commandId; + case kOperationTransmitData: - if ((txFrame->GetType() == Frame::kFcfFrameMacCmd) && (txFrame->GetCommandId(commandId) == OT_ERROR_NONE) && - (commandId == Frame::kMacCmdDataRequest) && (framePending)) + if (framePending && txFrame->IsDataRequestCommand()) { - mWaitingForData = true; mReceiveTimer.Start(kDataPollTimeout); + StartOperation(kOperationWaitingForData); } - } // fall through - case kStateActiveScan: - case kStateTransmitBeacon: + case kOperationActiveScan: + case kOperationTransmitBeacon: SentFrame(aError); break; @@ -992,6 +1070,8 @@ exit: otError Mac::RadioTransmit(Frame *aSendFrame) { + otError error = OT_ERROR_NONE; + #if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS if (!mRxOnWhenIdle) @@ -1004,12 +1084,19 @@ otError Mac::RadioTransmit(Frame *aSendFrame) } #endif - // Transmit packet - return otPlatRadioTransmit(&GetInstance(), static_cast(aSendFrame)); + + SuccessOrExit(error = otPlatRadioTransmit(&GetInstance(), static_cast(aSendFrame))); + + otLogWarnMac(GetInstance(), "otPlatRadioTransmit() failed with error %s", otThreadErrorToString(error)); + +exit: + return error; } otError Mac::RadioReceive(uint8_t aChannel) { + otError error = OT_ERROR_NONE; + #if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS if (!mRxOnWhenIdle) @@ -1019,25 +1106,42 @@ otError Mac::RadioReceive(uint8_t aChannel) } #endif - // Receive - return otPlatRadioReceive(&GetInstance(), aChannel); + + SuccessOrExit(error = otPlatRadioReceive(&GetInstance(), aChannel)); + + otLogWarnMac(GetInstance(), "otPlatRadioReceive() failed with error %s", otThreadErrorToString(error)); + +exit: + return error; } -void Mac::RadioSleep(void) +otError Mac::RadioSleep(void) { + otError error = OT_ERROR_NONE; + #if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS if (mDelaySleep) { - // Start delay sleep timer + otLogDebgMac(GetInstance(), "Delaying sleep waiting for frame rx/tx"); + mReceiveTimer.Start(kSleepDelay); mDelaySleep = false; + + // If sleep is delayed, `OT_ERROR_INVALID_STATE` is + // returned to inform the caller to put/keep the + // radio in receive mode. + + ExitNow(error = OT_ERROR_INVALID_STATE); } - else + #endif - { - otPlatRadioSleep(&GetInstance()); - } + SuccessOrExit(error = otPlatRadioSleep(&GetInstance())); + + otLogWarnMac(GetInstance(), "otPlatRadioSleep() failed with error %s", otThreadErrorToString(error)); + +exit: + return error; } void Mac::HandleMacTimer(Timer &aTimer) @@ -1049,9 +1153,9 @@ void Mac::HandleMacTimer(void) { Address addr; - switch (mState) + switch (mOperation) { - case kStateActiveScan: + case kOperationActiveScan: do { mScanChannels >>= 1; @@ -1059,10 +1163,9 @@ void Mac::HandleMacTimer(void) if (mScanChannels == 0 || mScanChannel > OT_RADIO_CHANNEL_MAX) { - RadioReceive(mChannel); otPlatRadioSetPanId(&GetInstance(), mPanId); mActiveScanHandler(mScanContext, NULL); - ScheduleNextTransmission(); + FinishOperation(); ExitNow(); } } @@ -1071,13 +1174,12 @@ void Mac::HandleMacTimer(void) StartCsmaBackoff(); break; - case kStateEnergyScan: + case kOperationEnergyScan: EnergyScanDone(mEnergyScanCurrentMaxRssi); break; - case kStateTransmitData: + case kOperationTransmitData: otLogDebgMac(GetInstance(), "Ack timer fired"); - RadioReceive(mChannel); mCounters.mTxTotal++; mTxFrame->GetDstAddr(addr); @@ -1112,19 +1214,29 @@ void Mac::HandleReceiveTimer(Timer &aTimer) void Mac::HandleReceiveTimer(void) { - if (mWaitingForData) + // `mReceiveTimer` is used for two purposes: (1) for data poll timeout + // (i.e., waiting to receive a data frame after a data poll ack + // indicating a pending frame from parent), and (2) for delaying sleep + // when feature `OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS` is + // enabled. + + if (mOperation == kOperationWaitingForData) { otLogDebgMac(GetInstance(), "Data poll timeout"); - mWaitingForData = false; + FinishOperation(); for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext) { receiver->HandleDataPollTimeout(); } } + else + { + otLogDebgMac(GetInstance(), "Sleep delay timeout expired"); - ScheduleNextTransmission(); + UpdateIdleMode(); + } } void Mac::SentFrame(otError aError) @@ -1184,19 +1296,19 @@ void Mac::SentFrame(otError aError) mCounters.mTxNoAckRequested++; } - switch (mState) + switch (mOperation) { - case kStateActiveScan: + case kOperationActiveScan: mCounters.mTxBeaconRequest++; mMacTimer.Start(mScanDuration); break; - case kStateTransmitBeacon: + case kOperationTransmitBeacon: mCounters.mTxBeacon++; - ScheduleNextTransmission(); + FinishOperation(); break; - case kStateTransmitData: + case kOperationTransmitData: if (mReceiveTimer.IsRunning()) { mCounters.mTxDataPoll++; @@ -1224,7 +1336,7 @@ void Mac::SentFrame(otError aError) otDumpDebgMac(GetInstance(), "TX", sendFrame.GetHeader(), sendFrame.GetLength()); sender->HandleSentFrame(sendFrame, aError); - ScheduleNextTransmission(); + FinishOperation(); break; default: @@ -1412,8 +1524,6 @@ void Mac::ReceiveDoneTask(Frame *aFrame, otError aError) PanId panid; Neighbor *neighbor; bool receive = false; - uint8_t commandId; - bool scheduleNextTrasmission = false; otError error = aError; #if OPENTHREAD_ENABLE_MAC_FILTER int8_t rssi = OT_MAC_FILTER_FIXED_RSS_DISABLED; @@ -1549,10 +1659,7 @@ void Mac::ReceiveDoneTask(Frame *aFrame, otError aError) case Neighbor::kStateChildUpdateRequest: // Only accept a "MAC Data Request" frame from a child being restored. - VerifyOrExit(aFrame->GetType() == Frame::kFcfFrameMacCmd, error = OT_ERROR_DROP); - VerifyOrExit(aFrame->GetCommandId(commandId) == OT_ERROR_NONE, error = OT_ERROR_DROP); - VerifyOrExit(commandId == Frame::kMacCmdDataRequest, error = OT_ERROR_DROP); - + VerifyOrExit(aFrame->IsDataRequestCommand(), error = OT_ERROR_DROP); break; default: @@ -1561,82 +1668,84 @@ void Mac::ReceiveDoneTask(Frame *aFrame, otError aError) } } - switch (mState) + switch (mOperation) { - case kStateActiveScan: + case kOperationActiveScan: + if (aFrame->GetType() == Frame::kFcfFrameBeacon) { mCounters.mRxBeacon++; mActiveScanHandler(mScanContext, aFrame); + ExitNow(); } - else + + // Fall-through + + case kOperationEnergyScan: + + // We can possibly receive a data frame while either active or + // energy scan is ongoing. We continue to process the frame only + // if the current scan channel matches `mChannel`. + + VerifyOrExit(mScanChannel == mChannel, mCounters.mRxOther++); + break; + + case kOperationWaitingForData: + + if (dstaddr.mLength != 0) { - mCounters.mRxOther++; + mReceiveTimer.Stop(); + +#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS + mDelaySleep = aFrame->GetFramePending(); +#endif + FinishOperation(); } break; default: - if (dstaddr.mLength != 0) - { - mWaitingForData = false; - - if (!mRxOnWhenIdle) - { - mReceiveTimer.Stop(); - scheduleNextTrasmission = true; -#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS - mDelaySleep = aFrame->GetFramePending(); -#endif - } - } - - switch (aFrame->GetType()) - { - case Frame::kFcfFrameMacCmd: - if (HandleMacCommand(*aFrame) == OT_ERROR_DROP) - { - ExitNow(error = OT_ERROR_NONE); - } - - receive = true; - break; - - case Frame::kFcfFrameBeacon: - mCounters.mRxBeacon++; - receive = true; - break; - - case Frame::kFcfFrameData: - mCounters.mRxData++; - receive = true; - break; - - default: - mCounters.mRxOther++; - break; - } - - if (receive) - { - otDumpDebgMac(GetInstance(), "RX", aFrame->GetHeader(), aFrame->GetLength()); - - for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext) - { - receiver->HandleReceivedFrame(*aFrame); - } - } - break; } -exit: - - if (scheduleNextTrasmission) + switch (aFrame->GetType()) { - ScheduleNextTransmission(); + case Frame::kFcfFrameMacCmd: + if (HandleMacCommand(*aFrame) == OT_ERROR_DROP) + { + ExitNow(error = OT_ERROR_NONE); + } + + receive = true; + break; + + case Frame::kFcfFrameBeacon: + mCounters.mRxBeacon++; + receive = true; + break; + + case Frame::kFcfFrameData: + mCounters.mRxData++; + receive = true; + break; + + default: + mCounters.mRxOther++; + break; } + if (receive) + { + otDumpDebgMac(GetInstance(), "RX", aFrame->GetHeader(), aFrame->GetLength()); + + for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext) + { + receiver->HandleReceivedFrame(*aFrame); + } + } + +exit: + if (error != OT_ERROR_NONE) { if (aFrame == NULL) @@ -1713,14 +1822,7 @@ otError Mac::HandleMacCommand(Frame &aFrame) #endif // OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_IF_JOINABLE ) { - mTransmitBeacon = true; - - if (mState == kStateIdle) - { - mState = kStateTransmitBeacon; - mTransmitBeacon = false; - StartCsmaBackoff(); - } + StartOperation(kOperationTransmitBeacon); } ExitNow(error = OT_ERROR_DROP); @@ -1752,19 +1854,18 @@ bool Mac::IsPromiscuous(void) void Mac::SetPromiscuous(bool aPromiscuous) { otPlatRadioSetPromiscuous(&GetInstance(), aPromiscuous); - - if (mState == kStateIdle) - { - NextOperation(); - } + UpdateIdleMode(); } bool Mac::RadioSupportsCsmaBackoff(void) { - /* Check either of the following conditions: - * 1) Radio provides the CSMA backoff capability (i.e., `OT_RADIO_CAPS_CSMA_BACKOFF` bit is set) or; - * 2) It provides `OT_RADIO_CAPS_TRANSMIT_RETRIES` which indicates support for MAC retries along with CSMA backoff. - */ + // Check for either of the following conditions: + // 1) Radio provides the CSMA backoff capability (i.e., + // `OT_RADIO_CAPS_CSMA_BACKOFF` bit is set) or; + // 2) It provides `OT_RADIO_CAPS_TRANSMIT_RETRIES` which + // indicates support for MAC retries along with CSMA + // backoff. + return (otPlatRadioGetCaps(&GetInstance()) & (OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF)) != 0; } @@ -1824,5 +1925,39 @@ Mac &Mac::GetOwner(const Context &aContext) return mac; } +const char *Mac::OperationToString(Operation aOperation) +{ + const char *retval = ""; + + switch (aOperation) + { + case kOperationIdle: + retval = "Idle"; + break; + + case kOperationActiveScan: + retval = "ActiveScan"; + break; + + case kOperationEnergyScan: + retval = "EnergyScan"; + break; + + case kOperationTransmitBeacon: + retval = "TransmitBeacon"; + break; + + case kOperationTransmitData: + retval = "TransmitData"; + break; + + case kOperationWaitingForData: + retval = "WaitingForData"; + break; + } + + return retval; +} + } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index b08d07018..5438f7ada 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -259,7 +259,8 @@ public: * completes. * * @param[in] aContext A pointer to arbitrary context information. - * @param[in] aResult A valid pointer to the energy scan result information or NULL when the energy scan completes. + * @param[in] aResult A valid pointer to the energy scan result information or NULL when the energy scan + * completes. * */ typedef void (*EnergyScanHandler)(void *aContext, otEnergyScanResult *aResult); @@ -632,23 +633,27 @@ public: bool RadioSupportsRetries(void); private: - enum ScanType - { - kScanTypeNone = 0, - kScanTypeActive, - kScanTypeEnergy, - }; - enum { kInvalidRssiValue = 127 }; + enum Operation + { + kOperationIdle = 0, + kOperationActiveScan, + kOperationEnergyScan, + kOperationTransmitBeacon, + kOperationTransmitData, + kOperationWaitingForData, + }; + void GenerateNonce(const ExtAddress &aAddress, uint32_t aFrameCounter, uint8_t aSecurityLevel, uint8_t *aNonce); - void NextOperation(void); void ProcessTransmitSecurity(Frame &aFrame); otError ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor); - void ScheduleNextTransmission(void); + void UpdateIdleMode(void); + void StartOperation(Operation aOperation); + void FinishOperation(void); void SentFrame(otError aError); void SendBeaconRequest(Frame &aFrame); void SendBeacon(Frame &aFrame); @@ -666,14 +671,16 @@ private: void HandleEnergyScanSampleRssi(void); void StartCsmaBackoff(void); - otError Scan(ScanType aType, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext); + otError Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext); otError RadioTransmit(Frame *aSendFrame); otError RadioReceive(uint8_t aChannel); - void RadioSleep(void); + otError RadioSleep(void); static Mac &GetOwner(const Context &aContext); + static const char *OperationToString(Operation aOperation); + TimerMilli mMacTimer; #if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_TIMER TimerMicro mBackoffTimer; @@ -694,35 +701,34 @@ private: Sender *mSendHead, *mSendTail; Receiver *mReceiveHead, *mReceiveTail; - enum - { - kStateIdle = 0, - kStateActiveScan, - kStateEnergyScan, - kStateTransmitBeacon, - kStateTransmitData, - }; - uint8_t mState; + Operation mOperation; + + bool mPendingActiveScan : 1; + bool mPendingEnergyScan : 1; + bool mPendingTransmitBeacon : 1; + bool mPendingTransmitData : 1; + bool mPendingWaitingForData : 1; + bool mRxOnWhenIdle : 1; + bool mBeaconsEnabled : 1; +#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS + bool mDelaySleep : 1; +#endif uint8_t mBeaconSequence; uint8_t mDataSequence; - bool mRxOnWhenIdle; uint8_t mCsmaAttempts; uint8_t mTransmitAttempts; - bool mTransmitBeacon; - bool mBeaconsEnabled; - ScanType mPendingScanRequest; - uint8_t mScanChannel; uint32_t mScanChannels; uint16_t mScanDuration; + uint8_t mScanChannel; + int8_t mEnergyScanCurrentMaxRssi; void *mScanContext; union { ActiveScanHandler mActiveScanHandler; EnergyScanHandler mEnergyScanHandler; }; - int8_t mEnergyScanCurrentMaxRssi; Tasklet mEnergyScanSampleRssiTask; otLinkPcapCallback mPcapCallback; @@ -736,11 +742,6 @@ private: otMacCounters mCounters; uint32_t mKeyIdMode2FrameCounter; - -#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS - bool mDelaySleep; -#endif - bool mWaitingForData; }; /** diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index d58f6cf8f..3b3d73f3b 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -870,6 +870,19 @@ exit: return error; } +bool Frame::IsDataRequestCommand(void) +{ + bool isDataRequest = false; + uint8_t commandId = 0; + + VerifyOrExit(GetType() == kFcfFrameMacCmd); + SuccessOrExit(GetCommandId(commandId)); + isDataRequest = (commandId == kMacCmdDataRequest); + +exit: + return isDataRequest; +} + uint8_t Frame::GetHeaderLength(void) { return static_cast(GetPayload() - GetPsdu()); diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 8db7c9841..8d90f4167 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -541,6 +541,14 @@ public: */ otError SetCommandId(uint8_t aCommandId); + /** + * This method indicates whether the frame is a MAC Data Request command (data poll) + * + * @returns TRUE if frame is a MAC Data Request command, FALSE otherwise. + * + */ + bool IsDataRequestCommand(void); + /** * This method returns the MAC Frame Length. *