diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index 3837de214..cfa57270f 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -105,8 +105,10 @@ typedef struct RadioPacket uint8_t mChannel; ///< Channel used to transmit/receive the frame. int8_t mPower; ///< Transmit/receive power in dBm. uint8_t mLqi; ///< Link Quality Indicator for received frames. + uint8_t mMaxTxAttempts; ///< Max number of transmit attempts for an outbound frame. bool mSecurityValid: 1; ///< Security Enabled flag is set and frame passes security checks. bool mDidTX: 1; ///< Set to true if this packet sent from the radio. Ignored by radio driver. + bool mIsARetx: 1; ///< Set to true if this packet is a retransmission. Should be ignored by radio driver. } RadioPacket; /** diff --git a/src/core/api/link_raw_api.cpp b/src/core/api/link_raw_api.cpp index 947f4ad6f..cb9fb459d 100644 --- a/src/core/api/link_raw_api.cpp +++ b/src/core/api/link_raw_api.cpp @@ -407,7 +407,7 @@ void LinkRaw::InvokeTransmitDone(RadioPacket *aPacket, bool aFramePending, Threa if (aError == kThreadError_NoAck) { - if (mTransmitAttempts < Mac::kMaxFrameAttempts) + if (mTransmitAttempts < aPacket->mMaxTxAttempts) { mTransmitAttempts++; StartCsmaBackoff(); diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index f36077b45..fd8978695 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -694,24 +694,41 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame) { case Frame::kKeyIdMode0: key = mNetif.GetKeyManager().GetKek(); - frameCounter = mNetif.GetKeyManager().GetKekFrameCounter(); - mNetif.GetKeyManager().IncrementKekFrameCounter(); extAddress = &mExtAddress; + + if (!aFrame.IsARetransmission()) + { + aFrame.SetFrameCounter(mNetif.GetKeyManager().GetKekFrameCounter()); + mNetif.GetKeyManager().IncrementKekFrameCounter(); + } + break; case Frame::kKeyIdMode1: key = mNetif.GetKeyManager().GetCurrentMacKey(); - frameCounter = mNetif.GetKeyManager().GetMacFrameCounter(); - mNetif.GetKeyManager().IncrementMacFrameCounter(); - aFrame.SetKeyId((mNetif.GetKeyManager().GetCurrentKeySequence() & 0x7f) + 1); extAddress = &mExtAddress; + + // If the frame is marked as a retransmission, the `Mac::Sender` which + // prepared the frame should set the frame counter and key id to the + // same values used in the earlier transmit attempt. For a new frame (not + // a retransmission), we get a new frame counter and key id from the key + // manager. + + if (!aFrame.IsARetransmission()) + { + aFrame.SetFrameCounter(mNetif.GetKeyManager().GetMacFrameCounter()); + mNetif.GetKeyManager().IncrementMacFrameCounter(); + aFrame.SetKeyId((mNetif.GetKeyManager().GetCurrentKeySequence() & 0x7f) + 1); + } + break; case Frame::kKeyIdMode2: { const uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff}; key = sMode2Key; - frameCounter = mKeyIdMode2FrameCounter++; + mKeyIdMode2FrameCounter++; + aFrame.SetFrameCounter(mKeyIdMode2FrameCounter); aFrame.SetKeySource(keySource); aFrame.SetKeyId(0xff); extAddress = static_cast(&sMode2ExtAddress); @@ -724,7 +741,7 @@ void Mac::ProcessTransmitSecurity(Frame &aFrame) } aFrame.GetSecurityLevel(securityLevel); - aFrame.SetFrameCounter(frameCounter); + aFrame.GetFrameCounter(frameCounter); GenerateNonce(*extAddress, frameCounter, securityLevel, nonce); @@ -768,7 +785,13 @@ void Mac::HandleBeginTransmit(void) case kStateTransmitData: sendFrame.SetChannel(mChannel); SuccessOrExit(error = mSendHead->HandleFrameRequest(sendFrame)); - sendFrame.SetSequence(mDataSequence); + + // If the frame is marked as a retransmission, then data sequence number is already set by the `Sender`. + if (!sendFrame.IsARetransmission()) + { + sendFrame.SetSequence(mDataSequence); + } + break; default: @@ -990,7 +1013,7 @@ void Mac::SentFrame(ThreadError aError) otDumpDebgMac("NO ACK", sendFrame.GetHeader(), 16); if (!RadioSupportsRetries() && - mTransmitAttempts < kMaxFrameAttempts) + mTransmitAttempts < sendFrame.GetMaxTxAttempts()) { mTransmitAttempts++; StartCsmaBackoff(); @@ -1056,7 +1079,11 @@ void Mac::SentFrame(ThreadError aError) sender->mNext = NULL; - mDataSequence++; + if (!sendFrame.IsARetransmission()) + { + mDataSequence++; + } + otDumpDebgMac("TX", sendFrame.GetHeader(), sendFrame.GetLength()); sender->HandleSentFrame(sendFrame, aError); @@ -1245,7 +1272,7 @@ void Mac::ReceiveDoneTask(Frame *aFrame, ThreadError aError) VerifyOrExit(error == kThreadError_None, ;); VerifyOrExit(aFrame != NULL, error = kThreadError_NoFrameReceived); - aFrame->mSecurityValid = false; + aFrame->SetSecurityValid(false); if (mPcapCallback) { diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 0c48b9deb..ce77fec7c 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -72,11 +72,9 @@ enum kMinBE = 3, ///< macMinBE (IEEE 802.15.4-2006) kMaxBE = 5, ///< macMaxBE (IEEE 802.15.4-2006) kMaxCSMABackoffs = 4, ///< macMaxCSMABackoffs (IEEE 802.15.4-2006) - kMaxFrameRetries = 3, ///< macMaxFrameRetries (IEEE 802.15.4-2006) kUnitBackoffPeriod = 20, ///< Number of symbols (IEEE 802.15.4-2006) kMinBackoff = 1, ///< Minimum backoff (milliseconds). - kMaxFrameAttempts = kMaxFrameRetries + 1, ///< Number of transmission attempts. kAckTimeout = 16, ///< Timeout for waiting on an ACK (milliseconds). kDataPollTimeout = 100, ///< Timeout for receiving Data Frame (milliseconds). diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 0f44d8af7..c92b8fdcb 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -632,6 +632,23 @@ public: */ void SetLqi(uint8_t aLqi) { mLqi = aLqi; } + /** + * This method returns the maximum number of transmit attempts for the frame. + * + * @returns The maximum number of transmit attempts. + * + */ + uint8_t GetMaxTxAttempts(void) const { return mMaxTxAttempts; } + + + /** + * This method set the maximum number of transmit attempts for frame. + * + * @returns The maximum number of transmit attempts. + * + */ + void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; } + /** * This method indicates whether or not frame security was enabled and passed security validation. * @@ -649,6 +666,23 @@ public: */ void SetSecurityValid(bool aSecurityValid) { mSecurityValid = aSecurityValid; } + /** + * This method indicates whether or not the frame is a retransmission. + * + * @retval TRUE Frame is a retransmission + * @retval FALSE This is a new frame and not a retransmission of an earlier frame. + * + */ + bool IsARetransmission(void) const { return mIsARetx; } + + /** + * This method sets the retransmission flag attribute. + * + * @param[in] aIsARetx TRUE if frame is a retransmission of an earlier frame, FALSE otherwise. + * + */ + void SetIsARetransmission(bool aIsARetx) { mIsARetx = aIsARetx; } + /** * This method returns the IEEE 802.15.4 PSDU length. * diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 69c4fd323..e7563fff5 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -115,6 +115,40 @@ #define OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER 0 #endif // OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER +/** + * @def OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT + * + * Maximum number of MAC layer transmit attempts for an outbound direct frame. + * Per IEEE 802.15.4-2006, default value is set to (macMaxFrameRetries + 1) with macMaxFrameRetries = 3. + * + */ +#ifndef OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT +#define OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT 4 +#endif // OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT + +/** + * @def OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL + * + * Maximum number of MAC layer transmit attempts for an outbound indirect frame (to a sleepy child) after receiving + * a data request command (data poll) from the child. + * + */ +#ifndef OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL +#define OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL 1 +#endif // OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL + +/** + * @def OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS + * + * Maximum number of transmit attempts for an outbound indirect frame (for a sleepy child) each triggered by the + * reception of a new data request command (a new data poll) from the sleepy child. Each data poll triggered attempt is + * retried by the MAC layer up to `OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL` times. + * + */ +#ifndef OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS +#define OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS 4 +#endif // OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS + /** * @def OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD * @@ -536,7 +570,7 @@ * */ #ifndef OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE -#define OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE 1500 +#define OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE 1300 #endif // OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE /** @@ -546,7 +580,7 @@ * */ #ifndef OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE -#define OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE 1500 +#define OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE 1300 #endif // OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE /** diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index f8db6f8d0..55903d48e 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -174,11 +174,6 @@ void MeshForwarder::HandleResolved(const Ip6::Address &aEid, ThreadError aError) } } -void MeshForwarder::ScheduleTransmissionTask(void *aContext) -{ - static_cast(aContext)->ScheduleTransmissionTask(); -} - void MeshForwarder::ClearChildIndirectMessages(Child &aChild) { Message *nextMessage; @@ -230,7 +225,12 @@ void MeshForwarder::UpdateIndirectMessages(void) } } -void MeshForwarder::ScheduleTransmissionTask() +void MeshForwarder::ScheduleTransmissionTask(void *aContext) +{ + static_cast(aContext)->ScheduleTransmissionTask(); +} + +void MeshForwarder::ScheduleTransmissionTask(void) { ThreadError error = kThreadError_None; uint8_t numChildren; @@ -240,6 +240,8 @@ void MeshForwarder::ScheduleTransmissionTask() UpdateIndirectMessages(); + mSendMessageIsARetransmission = false; + children = mNetif.GetMle().GetChildren(&numChildren); for (int i = 0; i < numChildren; i++) @@ -251,18 +253,16 @@ void MeshForwarder::ScheduleTransmissionTask() continue; } - mSendMessage = child.mIndirectSendMessage; + mSendMessage = child.mIndirectSendInfo.mMessage; + mSendMessageMaxMacTxAttempts = kIndirectFrameMacTxAttempts; if (mSendMessage == NULL) { mSendMessage = GetIndirectTransmission(child); - child.mIndirectSendMessage = mSendMessage; - child.mFragmentOffset = 0; } if (mSendMessage != NULL) { - mSendMessage->SetOffset(child.mFragmentOffset); PrepareIndirectTransmission(*mSendMessage, child); } else @@ -294,6 +294,7 @@ void MeshForwarder::ScheduleTransmissionTask() if ((mSendMessage = GetDirectTransmission()) != NULL) { mNetif.GetMac().SendFrameRequest(mMacSender); + mSendMessageMaxMacTxAttempts = kDirectFrameMacTxAttempts; ExitNow(); } @@ -570,7 +571,7 @@ exit: return curMessage; } -Message *MeshForwarder::GetIndirectTransmission(const Child &aChild) +Message *MeshForwarder::GetIndirectTransmission(Child &aChild) { Message *message = NULL; uint8_t childIndex = mNetif.GetMle().GetChildIndex(aChild); @@ -583,11 +584,25 @@ Message *MeshForwarder::GetIndirectTransmission(const Child &aChild) } } + aChild.mIndirectSendInfo.mMessage = message; + aChild.mIndirectSendInfo.mFragmentOffset = 0; + aChild.mIndirectSendInfo.mTxAttemptCounter = 0; + return message; } -void MeshForwarder::PrepareIndirectTransmission(const Message &aMessage, const Child &aChild) +void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &aChild) { + if (aChild.mIndirectSendInfo.mTxAttemptCounter > 0) + { + mSendMessageIsARetransmission = true; + mSendMessageFrameCounter = aChild.mIndirectSendInfo.mFrameCounter; + mSendMessageKeyId = aChild.mIndirectSendInfo.mKeyId; + mSendMessageDataSequenceNumber = aChild.mIndirectSendInfo.mDataSequenceNumber; + } + + aMessage.SetOffset(aChild.mIndirectSendInfo.mFragmentOffset); + switch (aMessage.GetType()) { case Message::kTypeIp6: @@ -1070,6 +1085,8 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame) if (mSendMessage == NULL) { SendEmptyFrame(aFrame); + aFrame.SetIsARetransmission(false); + aFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts); ExitNow(); } @@ -1149,6 +1166,24 @@ ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame) aFrame.SetFramePending(true); } + aFrame.SetIsARetransmission(mSendMessageIsARetransmission); + aFrame.SetMaxTxAttempts(mSendMessageMaxMacTxAttempts); + + if (mSendMessageIsARetransmission) + { + // If this is the 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 last attempt. + + aFrame.SetSequence(mSendMessageDataSequenceNumber); + + if (aFrame.GetSecurityEnabled()) + { + aFrame.SetFrameCounter(mSendMessageFrameCounter); + aFrame.SetKeyId(mSendMessageKeyId); + } + } + exit: return error; } @@ -1600,19 +1635,59 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError) VerifyOrExit(mSendMessage != NULL, ;); + if (mSendMessage == child->mIndirectSendInfo.mMessage) + { + switch (aError) + { + case kThreadError_None: + child->mIndirectSendInfo.mTxAttemptCounter = 0; + break; + + default: + child->mIndirectSendInfo.mTxAttemptCounter++; + + if (child->mIndirectSendInfo.mTxAttemptCounter < kMaxPollTriggeredTxAttempts) + { + // We save the frame counter, key id, and data sequence number of + // current frame so we use the same values for the retransmission + // of the frame following the receipt of a data request command (data + // poll) from the sleepy child. + + if (aFrame.GetSecurityEnabled()) + { + aFrame.GetFrameCounter(child->mIndirectSendInfo.mFrameCounter); + aFrame.GetKeyId(child->mIndirectSendInfo.mKeyId); + child->mIndirectSendInfo.mDataSequenceNumber = aFrame.GetSequence(); + } + + ExitNow(); + } + + child->mIndirectSendInfo.mTxAttemptCounter = 0; + + // We set the NextOffset to end of message, since there is no need to + // send any remaining fragments in the message to the child, if all tx + // attempts of current frame already failed. + + mMessageNextOffset = mSendMessage->GetLength(); + + break; + } + } + if (mMessageNextOffset < mSendMessage->GetLength()) { - if (mSendMessage == child->mIndirectSendMessage) + if (mSendMessage == child->mIndirectSendInfo.mMessage) { - child->mFragmentOffset = mMessageNextOffset; + child->mIndirectSendInfo.mFragmentOffset = mMessageNextOffset; } } else { - if (mSendMessage == child->mIndirectSendMessage) + if (mSendMessage == child->mIndirectSendInfo.mMessage) { - child->mFragmentOffset = 0; - child->mIndirectSendMessage = NULL; + child->mIndirectSendInfo.mFragmentOffset = 0; + child->mIndirectSendInfo.mMessage = NULL; } mSendMessage->ClearChildMask(mNetif.GetMle().GetChildIndex(*child)); diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 75d353ea0..2c9d06fbe 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -248,6 +248,30 @@ private: kDataRequestRetryDelay = 200, ///< Retry delay in milliseconds (for sending data request if no buffer). }; + enum + { + /** + * Maximum number of MAC layer tx attempts for an outbound direct frame. + * + */ + kDirectFrameMacTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT, + + /** + * Maximum number of MAC layer tx attempts for an outbound indirect frame (for a sleepy child) after receiving + * a data request command (data poll) from the child. + * + */ + kIndirectFrameMacTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL, + + /** + * Maximum number of tx attempts by `MeshForwarder` for an outbound indirect frame (for a sleepy child). The + * `MeshForwader` attempts occur following the reception of a new data request command (a new data poll) from + * the sleepy child. + * + */ + kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS, + }; + ThreadError CheckReachability(uint8_t *aFrame, uint8_t aFrameLength, const Mac::Address &aMeshSource, const Mac::Address &aMeshDest); @@ -255,8 +279,8 @@ private: ThreadError GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); ThreadError GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *GetDirectTransmission(void); - Message *GetIndirectTransmission(const Child &aChild); - void PrepareIndirectTransmission(const Message &aMessage, const Child &aChild); + Message *GetIndirectTransmission(Child &aChild); + void PrepareIndirectTransmission(Message &aMessage, const Child &aChild); void HandleMesh(uint8_t *aFrame, uint8_t aPayloadLength, const Mac::Address &aMacSource, const ThreadMessageInfo &aMessageInfo); void HandleFragment(uint8_t *aFrame, uint8_t aPayloadLength, @@ -311,7 +335,13 @@ private: uint16_t mMessageNextOffset; uint32_t mPollPeriod; uint32_t mAssignPollPeriod; ///< only for certification test + + uint32_t mSendMessageFrameCounter; Message *mSendMessage; + bool mSendMessageIsARetransmission; + uint8_t mSendMessageMaxMacTxAttempts; + uint8_t mSendMessageKeyId; + uint8_t mSendMessageDataSequenceNumber; Mac::Address mMacSource; Mac::Address mMacDest; diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 58c2101d6..cc119e979 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -127,10 +127,18 @@ public: kMaxIp6AddressPerChild = OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD, kMaxRequestTlvs = 5, }; + Ip6::Address mIp6Address[kMaxIp6AddressPerChild]; ///< Registered IPv6 addresses uint32_t mTimeout; ///< Child timeout - uint16_t mFragmentOffset; ///< 6LoWPAN fragment offset for the indirect message - Message *mIndirectSendMessage; ///< Current indirect message being sent. + struct + { + uint32_t mFrameCounter; ///< Frame counter for current indirect message (used fore retx). + Message *mMessage; ///< Current indirect message. + uint16_t mFragmentOffset; ///< 6LoWPAN fragment offset for the indirect message. + uint8_t mKeyId; ///< Key Id for current indirect message (used for retx). + uint8_t mTxAttemptCounter; ///< Number of data poll triggered tx attempts. + uint8_t mDataSequenceNumber; ///< MAC level Data Sequence Number (DSN) for retx attempts. + } mIndirectSendInfo; ///< Info about current outbound indirect message. union { uint8_t mRequestTlvs[kMaxRequestTlvs]; ///< Requested MLE TLVs diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index f3cf1117e..b5e9900e5 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -3939,6 +3939,9 @@ ThreadError NcpBase::SetPropertyHandler_STREAM_RAW(uint8_t header, spinel_prop_k packet->mLength = static_cast(frame_len); memcpy(packet->mPsdu, frame_buffer, packet->mLength); + // TODO: This should be later added in the STREAM_RAW argument to allow user to directly specify it. + packet->mMaxTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT; + // Pass packet to the radio layer. Note, this fails if we // haven't enabled raw stream or are already transmitting. errorCode = otLinkRawTransmit(mInstance, packet, &NcpBase::LinkRawTransmitDone);