[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).
This commit is contained in:
Abtin Keshavarzian
2017-07-27 13:06:25 -07:00
committed by Jonathan Hui
parent ecebcbe2a2
commit a304692566
4 changed files with 398 additions and 241 deletions
+344 -209
View File
@@ -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<uint32_t>(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<uint8_t>(otPlatRandomGet())),
mDataSequence(static_cast<uint8_t>(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<Frame *>(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<uint32_t>(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<otRadioFrame *>(aSendFrame));
SuccessOrExit(error = otPlatRadioTransmit(&GetInstance(), static_cast<otRadioFrame *>(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
+33 -32
View File
@@ -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;
};
/**
+13
View File
@@ -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<uint8_t>(GetPayload() - GetPsdu());
+8
View File
@@ -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.
*