From 529c291f7a0cab3715e9b13887c536baa402f488 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 10 Jul 2017 08:47:23 -0700 Subject: [PATCH] [ncp-buffer] Frame Priority Support (#1948) This commit adds support for setting two levels of priority (high or low) to frames stored in `NcpFrameBuffer`. High-priority frames are read before any queued low-priority frames. Within the same priority level FIFO order is preserved. The `NcpBase` is updated to send/queue the spinel response frames (with non- zero `tid`) as higher-priority frames. This ensures faster response time for spinel commands. This commit also adds/updates test cases for unit testing the NCP buffer and its new priority feature. --- src/ncp/ncp_base.cpp | 92 +++--- src/ncp/ncp_base.hpp | 6 +- src/ncp/ncp_buffer.cpp | 343 +++++++++++++++------- src/ncp/ncp_buffer.hpp | 327 ++++++++++++++------- src/ncp/ncp_spi.cpp | 4 +- src/ncp/ncp_spi.hpp | 2 +- src/ncp/ncp_uart.cpp | 3 +- src/ncp/ncp_uart.hpp | 2 +- tests/unit/test_ncp_buffer.cpp | 517 ++++++++++++++++++++++++++------- tests/unit/test_util.h | 34 +-- 10 files changed, 962 insertions(+), 368 deletions(-) diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 311801634..ead72374a 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -732,9 +732,21 @@ NcpBase *NcpBase::GetNcpInstance(void) // MARK: Outbound Frame methods // ---------------------------------------------------------------------------- -otError NcpBase::OutboundFrameBegin(void) +otError NcpBase::OutboundFrameBegin(uint8_t aHeader) { - return mTxFrameBuffer.InFrameBegin(); + NcpFrameBuffer::Priority priority; + + // Non-zero tid indicates this is a response to a spinel command. + if (SPINEL_HEADER_GET_TID(aHeader) != 0) + { + priority = NcpFrameBuffer::kPriorityHigh; + } + else + { + priority = NcpFrameBuffer::kPriorityLow; + } + + return mTxFrameBuffer.InFrameBegin(priority); } otError NcpBase::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength) @@ -767,13 +779,14 @@ void NcpBase::HandleTmfProxyStream(otMessage *aMessage, uint16_t aLocator, uint1 { otError error = OT_ERROR_NONE; uint16_t length = otMessageGetLength(aMessage); + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(header)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_TMF_PROXY_STREAM, length @@ -823,15 +836,16 @@ void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext) void NcpBase::HandleDatagramFromStack(otMessage *aMessage) { otError error = OT_ERROR_NONE; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; bool isSecure = otMessageIsLinkSecurityEnabled(aMessage); uint16_t length = otMessageGetLength(aMessage); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(header)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + header, SPINEL_CMD_PROP_VALUE_IS, isSecure ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE, length @@ -859,7 +873,7 @@ exit: if (error != OT_ERROR_NONE) { - SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED); + SendLastStatus(header, SPINEL_STATUS_DROPPED); mDroppedOutboundIpFrameCounter++; } else @@ -887,13 +901,14 @@ void NcpBase::HandleRawFrame(const otRadioFrame *aFrame, void *aContext) void NcpBase::HandleRawFrame(const otRadioFrame *aFrame) { uint16_t flags = 0; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; if (!mIsRawStreamEnabled) { goto exit; } - SuccessOrExit(OutboundFrameBegin()); + SuccessOrExit(OutboundFrameBegin(header)); if (aFrame->mDidTX) { @@ -904,7 +919,7 @@ void NcpBase::HandleRawFrame(const otRadioFrame *aFrame) SuccessOrExit( OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW, aFrame->mLength @@ -1070,8 +1085,9 @@ void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aEr void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) { uint16_t flags = 0; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; - SuccessOrExit(OutboundFrameBegin()); + SuccessOrExit(OutboundFrameBegin(header)); if (aFrame->mDidTX) { @@ -1082,7 +1098,7 @@ void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) SuccessOrExit( OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW, (aError == OT_ERROR_NONE) ? aFrame->mLength : 0 @@ -1467,9 +1483,11 @@ void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength) } void NcpBase::HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag, - NcpFrameBuffer *aNcpBuffer) + NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpBuffer) { OT_UNUSED_VARIABLE(aNcpBuffer); + OT_UNUSED_VARIABLE(aPriority); + static_cast(aContext)->HandleFrameRemovedFromNcpBuffer(aFrameTag); } @@ -1750,7 +1768,7 @@ otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_pr va_list args; va_start(args, aPackFormat); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey)); SuccessOrExit(error = OutboundFrameFeedVPacked(aPackFormat, args)); SuccessOrExit(error = OutboundFrameSend()); @@ -1765,7 +1783,7 @@ otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_pr { otError error = OT_ERROR_NONE; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey)); SuccessOrExit(error = OutboundFrameFeedData(aValuePtr, aValueLen)); SuccessOrExit(error = OutboundFrameSend()); @@ -1778,7 +1796,7 @@ otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_pr { otError error = OT_ERROR_NONE; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey)); SuccessOrExit(error = OutboundFrameFeedMessage(aMessage)); @@ -2069,7 +2087,7 @@ otError NcpBase::CommandHandler_PEEK(uint8_t aHeader, unsigned int aCommand, con VerifyOrExit(mAllowPeekDelegate(address, count), spinelError = SPINEL_STATUS_INVALID_ARGUMENT); } - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( ( @@ -2193,7 +2211,7 @@ otError NcpBase::GetPropertyHandler_CAPS(uint8_t aHeader, spinel_prop_key_t aKey { otError error = OT_ERROR_NONE; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -2493,7 +2511,7 @@ otError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t aHeader, spinel_pr { otError error = OT_ERROR_NONE; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -2770,7 +2788,7 @@ otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t aHeader, spinel_ &networkDataLen ); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -2798,7 +2816,7 @@ otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t aHeader, &networkDataLen ); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -2827,7 +2845,7 @@ otError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t aHeader, &networkDataLen ); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -2855,7 +2873,7 @@ otError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t aH &networkDataLen ); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -2969,7 +2987,7 @@ otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t aHeader, spinel_p mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -3036,7 +3054,7 @@ otError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t aHeader, spine mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS, aKey)); @@ -3087,7 +3105,7 @@ otError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spin uint8_t numEntries = 0; const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS, aKey)); @@ -3134,7 +3152,7 @@ otError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_ mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -3342,7 +3360,7 @@ otError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_p mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -3415,7 +3433,7 @@ otError NcpBase::GetPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spin mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -3839,7 +3857,7 @@ otError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t aHeader, spinel_ otMessageGetBufferInfo(mInstance, &bufferInfo); - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -3957,7 +3975,7 @@ otError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_k mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -4019,7 +4037,7 @@ otError NcpBase::GetPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_k mDisableStreamWrite = true; - SuccessOrExit(error = OutboundFrameBegin()); + SuccessOrExit(error = OutboundFrameBegin(aHeader)); SuccessOrExit( error = OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S, @@ -7415,14 +7433,16 @@ exit: void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) { + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH); - SuccessOrExit(OutboundFrameBegin()); + SuccessOrExit(OutboundFrameBegin(header)); SuccessOrExit( OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_DATA_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH @@ -7436,14 +7456,16 @@ exit: void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) { + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + mLegacyNodeDidJoin = true; - SuccessOrExit(OutboundFrameBegin()); + SuccessOrExit(OutboundFrameBegin(header)); SuccessOrExit( OutboundFrameFeedPacked( SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_EUI64_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_LEGACY_JOINED_NODE, aExtAddr->m8 diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 2d3a180cc..2c9e580b7 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -111,11 +111,13 @@ protected: /** * This method is called to start a new outbound frame. * + * param[in] aHeader The spinel header byte + * * @retval OT_ERROR_NONE Successfully started a new frame. * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame. * */ - otError OutboundFrameBegin(void); + otError OutboundFrameBegin(uint8_t aHeader); /** * This method adds data to the current outbound frame being written. @@ -192,7 +194,7 @@ private: #endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD static void HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag, - NcpFrameBuffer *aNcpBuffer); + NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpBuffer); void HandleFrameRemovedFromNcpBuffer(NcpFrameBuffer::FrameTag aFrameTag); static void HandleDatagramFromStack(otMessage *aMessage, void *aContext); diff --git a/src/ncp/ncp_buffer.cpp b/src/ncp/ncp_buffer.cpp index 38d800926..d88561232 100644 --- a/src/ncp/ncp_buffer.cpp +++ b/src/ncp/ncp_buffer.cpp @@ -35,6 +35,7 @@ #include "ncp_buffer.hpp" #include "utils/wrap_string.h" #include "common/code_utils.hpp" +#include "common/debug.hpp" namespace ot { @@ -45,7 +46,11 @@ NcpFrameBuffer::NcpFrameBuffer(uint8_t *aBuffer, uint16_t aBufferLen) : mBufferEnd(aBuffer + aBufferLen), mBufferLength(aBufferLen) { - otMessageQueueInit(&mMessageQueue); + for (uint8_t priority = 0; priority < kNumPrios; priority++) + { + otMessageQueueInit(&mMessageQueue[priority]); + } + otMessageQueueInit(&mWriteFrameMessageQueue); SetFrameAddedCallback(NULL, NULL); SetFrameRemovedCallback(NULL, NULL); @@ -57,16 +62,19 @@ void NcpFrameBuffer::Clear(void) otMessage *message; // Write (InFrame) related variables - mWriteFrameStart = mBuffer; + mWriteFrameStart[kPriorityLow] = mBuffer; + mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward); + mWriteDirection = kUnknown; mWriteSegmentHead = mBuffer; mWriteSegmentTail = mBuffer; mWriteFrameTag = kInvalidTag; // Read (OutFrame) related variables - mReadState = kReadStateDone; + mReadState = kReadStateNotActive; mReadFrameLength = kUnknownFrameLength; - mReadFrameStart = mBuffer; + mReadFrameStart[kPriorityLow] = mBuffer; + mReadFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mBuffer, 1, kBackward); mReadSegmentHead = mBuffer; mReadSegmentTail = mBuffer; mReadPointer = mBuffer; @@ -83,10 +91,13 @@ void NcpFrameBuffer::Clear(void) otMessageFree(message); } - while ((message = otMessageQueueGetHead(&mMessageQueue)) != NULL) + for (uint8_t priority = 0; priority < kNumPrios; priority++) { - otMessageQueueDequeue(&mMessageQueue, message); - otMessageFree(message); + while ((message = otMessageQueueGetHead(&mMessageQueue[priority])) != NULL) + { + otMessageQueueDequeue(&mMessageQueue[priority], message); + otMessageFree(message); + } } } @@ -102,84 +113,129 @@ void NcpFrameBuffer::SetFrameRemovedCallback(BufferCallback aFrameRemovedCallbac mFrameRemovedContext = aFrameRemovedContext; } -// Increments the buffer pointer by one byte while handling the the wrap-around at the end of buffer. -uint8_t *NcpFrameBuffer::Next(uint8_t *aBufPtr) const +// Returns an updated buffer pointer by moving forward/backward (based on `aDirection`) from `aBufPtr` by a given +// offset. The resulting buffer pointer is ensured to stay within the `mBuffer` boundaries. +uint8_t *NcpFrameBuffer::GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const { - aBufPtr++; - return (aBufPtr == mBufferEnd) ? mBuffer : aBufPtr; -} + uint8_t *ptr = aBufPtr; -// Returns an advanced (moved forward) version of the given buffer pointer by the given offset. -uint8_t *NcpFrameBuffer::Advance(uint8_t *aBufPtr, uint16_t aOffset) const -{ - aBufPtr += aOffset; - - while (aBufPtr >= mBufferEnd) + switch (aDirection) { - aBufPtr -= mBufferLength; + case kForward: + ptr += aOffset; + + while (ptr >= mBufferEnd) + { + ptr -= mBufferLength; + } + + break; + + case kBackward: + ptr -= aOffset; + + while (ptr < mBuffer) + { + ptr += mBufferLength; + } + + break; + + case kUnknown: + assert(false); + break; } - return aBufPtr; + return ptr; } -// Gets the distance between two buffer pointers (adjusts for the wrap-around). -uint16_t NcpFrameBuffer::GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr) const +// Gets the distance between two buffer pointers (adjusts for the wrap-around) given a direction (forward or backward). +uint16_t NcpFrameBuffer::GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr, Direction aDirection) const { - size_t distance; + size_t distance = 0; - if (aEndPtr >= aStartPtr) + switch (aDirection) { - distance = static_cast(aEndPtr - aStartPtr); - } - else - { - distance = static_cast(mBufferEnd - aStartPtr); - distance += static_cast(aEndPtr - mBuffer); + case kForward: + + if (aEndPtr >= aStartPtr) + { + distance = static_cast(aEndPtr - aStartPtr); + } + else + { + distance = static_cast(mBufferEnd - aStartPtr); + distance += static_cast(aEndPtr - mBuffer); + } + + break; + + case kBackward: + + if (aEndPtr <= aStartPtr) + { + distance = static_cast(aStartPtr - aEndPtr); + } + else + { + distance = static_cast(mBufferEnd - aEndPtr); + distance += static_cast(aStartPtr - mBuffer); + } + + break; + + case kUnknown: + assert(false); + break; } return static_cast(distance); } // Writes a uint16 value at the given buffer pointer (big-endian style). -void NcpFrameBuffer::WriteUint16At(uint8_t *aBufPtr, uint16_t aValue) +void NcpFrameBuffer::WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection) { *aBufPtr = (aValue >> 8); - *Next(aBufPtr) = (aValue & 0xff); + *GetUpdatedBufPtr(aBufPtr, 1, aDirection) = (aValue & 0xff); } // Reads a uint16 value at the given buffer pointer (big-endian style). -uint16_t NcpFrameBuffer::ReadUint16At(uint8_t *aBufPtr) +uint16_t NcpFrameBuffer::ReadUint16At(uint8_t *aBufPtr, Direction aDirection) { uint16_t value; value = static_cast((*aBufPtr) << 8); - value += *Next(aBufPtr); + value += *GetUpdatedBufPtr(aBufPtr, 1, aDirection); return value; } -// Writes a bytes at the write tail, discards the frame if buffer gets full. +// Writes a byte at the write tail, discards the frame if buffer gets full. otError NcpFrameBuffer::InFrameFeedByte(uint8_t aByte) { otError error = OT_ERROR_NONE; - uint8_t *newTail = Next(mWriteSegmentTail); + uint8_t *newTail; - VerifyOrExit(newTail != mReadFrameStart, error = OT_ERROR_NO_BUFS); + assert(mWriteDirection != kUnknown); - *mWriteSegmentTail = aByte; - mWriteSegmentTail = newTail; + newTail = GetUpdatedBufPtr(mWriteSegmentTail, 1, mWriteDirection); -exit: - - if (error != OT_ERROR_NONE) + // Ensure the `newTail` has not reached the `mWriteFrameStart` for other direction (other priority level). + if (newTail != mWriteFrameStart[(mWriteDirection == kForward) ? kBackward : kForward]) { + *mWriteSegmentTail = aByte; + mWriteSegmentTail = newTail; + } + else + { + error = OT_ERROR_NO_BUFS; InFrameDiscard(); } return error; } -// This method begins a new segment (if one is not already open) +// This method begins a new segment (if one is not already open). otError NcpFrameBuffer::InFrameBeginSegment(void) { otError error = OT_ERROR_NONE; @@ -188,8 +244,8 @@ otError NcpFrameBuffer::InFrameBeginSegment(void) // Verify that segment is not yet started (i.e., head and tail are the same). VerifyOrExit(mWriteSegmentHead == mWriteSegmentTail); - // If this is the start of a new frame (i.e., frame start is same as segment head) - if (mWriteFrameStart == mWriteSegmentHead) + // Check if this is the start of a new frame (i.e., frame start is same as segment head). + if (mWriteFrameStart[mWriteDirection] == mWriteSegmentHead) { headerFlags |= kSegmentHeaderNewFrameFlag; } @@ -200,8 +256,8 @@ otError NcpFrameBuffer::InFrameBeginSegment(void) SuccessOrExit(error = InFrameFeedByte(0)); } - // Write the flags at the segment head - WriteUint16At(mWriteSegmentHead, headerFlags); + // Write the flags at the segment head. + WriteUint16At(mWriteSegmentHead, headerFlags, mWriteDirection); exit: return error; @@ -213,7 +269,7 @@ void NcpFrameBuffer::InFrameEndSegment(uint16_t aHeaderFlags) uint16_t segmentLength; uint16_t header; - segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail); + segmentLength = GetDistance(mWriteSegmentHead, mWriteSegmentTail, mWriteDirection); if (segmentLength >= kSegmentHeaderSize) { @@ -221,10 +277,10 @@ void NcpFrameBuffer::InFrameEndSegment(uint16_t aHeaderFlags) segmentLength -= kSegmentHeaderSize; // Update the length and the flags in segment header (at segment head pointer). - header = ReadUint16At(mWriteSegmentHead); + header = ReadUint16At(mWriteSegmentHead, mWriteDirection); header |= (segmentLength & kSegmentHeaderLengthMask); header |= aHeaderFlags; - WriteUint16At(mWriteSegmentHead, header); + WriteUint16At(mWriteSegmentHead, header, mWriteDirection); // Move the segment head to current tail (to be ready for a possible next segment). mWriteSegmentHead = mWriteSegmentTail; @@ -236,13 +292,15 @@ void NcpFrameBuffer::InFrameEndSegment(uint16_t aHeaderFlags) } } -// This method discards the current frame. +// This method discards the current frame being written. void NcpFrameBuffer::InFrameDiscard(void) { otMessage *message; + VerifyOrExit(mWriteDirection != kUnknown); + // Move the write segment head and tail pointers back to frame start. - mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart; + mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection]; // Free any messages associated with current frame. while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL) @@ -250,13 +308,38 @@ void NcpFrameBuffer::InFrameDiscard(void) otMessageQueueDequeue(&mWriteFrameMessageQueue, message); otMessageFree(message); } + + mWriteDirection = kUnknown; + +exit: + UpdateReadWriteStartPointers(); } -otError NcpFrameBuffer::InFrameBegin(void) +// Returns `true` if in middle of writing a frame with given priority. +bool NcpFrameBuffer::InFrameIsWriting(Priority aPriority) const { - // Discard any previous frame. + return (mWriteDirection == static_cast(aPriority)); +} + +otError NcpFrameBuffer::InFrameBegin(Priority aPriority) +{ + // Discard any previous unfinished frame. InFrameDiscard(); + switch (aPriority) + { + case kPriorityHigh: + mWriteDirection = kBackward; + break; + + case kPriorityLow: + mWriteDirection = kForward; + break; + } + + // Set up the segment head and tail + mWriteSegmentHead = mWriteSegmentTail = mWriteFrameStart[mWriteDirection]; + return OT_ERROR_NONE; } @@ -264,6 +347,8 @@ otError NcpFrameBuffer::InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDa { otError error = OT_ERROR_NONE; + VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE); + // Begin a new segment (if we are not in middle of segment already). SuccessOrExit(error = InFrameBeginSegment()); @@ -281,6 +366,9 @@ otError NcpFrameBuffer::InFrameFeedMessage(otMessage *aMessage) { otError error = OT_ERROR_NONE; + VerifyOrExit(aMessage != NULL, error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE); + // Begin a new segment (if we are not in middle of segment already). SuccessOrExit(error = InFrameBeginSegment()); @@ -297,29 +385,35 @@ exit: otError NcpFrameBuffer::InFrameEnd(void) { otMessage *message; + otError error = OT_ERROR_NONE; + + VerifyOrExit(mWriteDirection != kUnknown, error = OT_ERROR_INVALID_STATE); // End/Close the current segment (if any). InFrameEndSegment(kSegmentHeaderNoFlag); // Save and use the frame start pointer as the tag associated with the frame. - mWriteFrameTag = mWriteFrameStart; + mWriteFrameTag = mWriteFrameStart[mWriteDirection]; // Update the frame start pointer to current segment head to be ready for next frame. - mWriteFrameStart = mWriteSegmentHead; + mWriteFrameStart[mWriteDirection] = mWriteSegmentHead; // Move all the messages from the frame queue to the main queue. while ((message = otMessageQueueGetHead(&mWriteFrameMessageQueue)) != NULL) { otMessageQueueDequeue(&mWriteFrameMessageQueue, message); - otMessageQueueEnqueue(&mMessageQueue, message); + otMessageQueueEnqueue(&mMessageQueue[mWriteDirection], message); } if (mFrameAddedCallback != NULL) { - mFrameAddedCallback(mFrameAddedContext, mWriteFrameTag, this); + mFrameAddedCallback(mFrameAddedContext, mWriteFrameTag, static_cast(mWriteDirection), this); } - return OT_ERROR_NONE; + mWriteDirection = kUnknown; + +exit: + return error; } NcpFrameBuffer::FrameTag NcpFrameBuffer::InFrameGetLastTag(void) const @@ -327,9 +421,22 @@ NcpFrameBuffer::FrameTag NcpFrameBuffer::InFrameGetLastTag(void) const return mWriteFrameTag; } +bool NcpFrameBuffer::HasFrame(Priority aPriority) const +{ + return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority]; +} + bool NcpFrameBuffer::IsEmpty(void) const { - return (mReadFrameStart == mWriteFrameStart); + return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow); +} + +void NcpFrameBuffer::OutFrameSelectReadDirection(void) +{ + if (mReadState == kReadStateNotActive) + { + mReadDirection = HasFrame(kPriorityHigh) ? kBackward : kForward; + } } // Start/Prepare a new segment for reading. @@ -344,24 +451,24 @@ otError NcpFrameBuffer::OutFramePrepareSegment(void) mReadSegmentHead = mReadSegmentTail; // Ensure there is something to read (i.e. segment head is not at start of frame being written). - VerifyOrExit(mReadSegmentHead != mWriteFrameStart, error = OT_ERROR_NOT_FOUND); + VerifyOrExit(mReadSegmentHead != mWriteFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND); // Read the segment header. - header = ReadUint16At(mReadSegmentHead); + header = ReadUint16At(mReadSegmentHead, mReadDirection); // Check if this segment is the start of a frame. if (header & kSegmentHeaderNewFrameFlag) { // Ensure that this segment is start of current frame, otherwise the current frame is finished. - VerifyOrExit(mReadSegmentHead == mReadFrameStart, error = OT_ERROR_NOT_FOUND); + VerifyOrExit(mReadSegmentHead == mReadFrameStart[mReadDirection], error = OT_ERROR_NOT_FOUND); } // Find tail/end of current segment. - mReadSegmentTail = Advance(mReadSegmentHead, - kSegmentHeaderSize + (header & kSegmentHeaderLengthMask)); + mReadSegmentTail = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), + mReadDirection); // Update the current read pointer to skip the segment header. - mReadPointer = Advance(mReadSegmentHead, kSegmentHeaderSize); + mReadPointer = GetUpdatedBufPtr(mReadSegmentHead, kSegmentHeaderSize, mReadDirection); // Check if there are data bytes to be read in this segment (i.e. read pointer not at the tail). if (mReadPointer != mReadSegmentTail) @@ -399,15 +506,15 @@ otError NcpFrameBuffer::OutFramePrepareMessage(void) uint16_t header; // Read the segment header - header = ReadUint16At(mReadSegmentHead); + header = ReadUint16At(mReadSegmentHead, mReadDirection); // Ensure that the segment header indicates that there is an associated message or return `NotFound` error. VerifyOrExit((header & kSegmentHeaderMessageIndicatorFlag) != 0, error = OT_ERROR_NOT_FOUND); // Update the current message from the queue. mReadMessage = (mReadMessage == NULL) ? - otMessageQueueGetHead(&mMessageQueue) : - otMessageQueueGetNext(&mMessageQueue, mReadMessage); + otMessageQueueGetHead(&mMessageQueue[mReadDirection]) : + otMessageQueueGetNext(&mMessageQueue[mReadDirection], mReadMessage); VerifyOrExit(mReadMessage != NULL, error = OT_ERROR_NOT_FOUND); @@ -456,20 +563,25 @@ otError NcpFrameBuffer::OutFrameBegin(void) { otError error = OT_ERROR_NONE; - mReadMessage = NULL; + VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND); + + OutFrameSelectReadDirection(); // Move the segment head and tail to start of frame. - mReadSegmentHead = mReadSegmentTail = mReadFrameStart; + mReadSegmentHead = mReadSegmentTail = mReadFrameStart[mReadDirection]; + + mReadMessage = NULL; // Prepare the current segment for reading. error = OutFramePrepareSegment(); +exit: return error; } bool NcpFrameBuffer::OutFrameHasEnded(void) { - return (mReadState == kReadStateDone); + return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive); } uint8_t NcpFrameBuffer::OutFrameReadByte(void) @@ -479,6 +591,10 @@ uint8_t NcpFrameBuffer::OutFrameReadByte(void) switch (mReadState) { + case kReadStateNotActive: + + // Fall through + case kReadStateDone: retval = kReadByteAfterFrameHasEnded; @@ -487,9 +603,9 @@ uint8_t NcpFrameBuffer::OutFrameReadByte(void) case kReadStateInSegment: - // Read a byte from current read pointer and move the read pointer forward. + // Read a byte from current read pointer and move the read pointer by 1 byte in the read direction. retval = *mReadPointer; - mReadPointer = Next(mReadPointer); + mReadPointer = GetUpdatedBufPtr(mReadPointer, 1, mReadDirection); // Check if at end of current segment. if (mReadPointer == mReadSegmentTail) @@ -508,7 +624,7 @@ uint8_t NcpFrameBuffer::OutFrameReadByte(void) case kReadStateInMessage: - // Read a byte from current read pointer and move the read pointer forward. + // Read a byte from current read pointer and move the read pointer by 1 byte. retval = *mReadPointer; mReadPointer++; @@ -553,23 +669,25 @@ otError NcpFrameBuffer::OutFrameRemove(void) VerifyOrExit(!IsEmpty(), error = OT_ERROR_NOT_FOUND); + OutFrameSelectReadDirection(); + // Save the frame start pointer as the tag associated with the frame being removed. - tag = mReadFrameStart; + tag = mReadFrameStart[mReadDirection]; // Begin at the start of current frame and move through all segments. - bufPtr = mReadFrameStart; + bufPtr = mReadFrameStart[mReadDirection]; - while (bufPtr != mWriteFrameStart) + while (bufPtr != mWriteFrameStart[mReadDirection]) { // Read the segment header - header = ReadUint16At(bufPtr); + header = ReadUint16At(bufPtr, mReadDirection); // If the current segment defines a new frame, and it is not the start of current frame, then we have reached // end of current frame. if (header & kSegmentHeaderNewFrameFlag) { - if (bufPtr != mReadFrameStart) + if (bufPtr != mReadFrameStart[mReadDirection]) { break; } @@ -578,31 +696,55 @@ otError NcpFrameBuffer::OutFrameRemove(void) // If current segment has an appended message, remove it from message queue and free it. if (header & kSegmentHeaderMessageIndicatorFlag) { - if ((message = otMessageQueueGetHead(&mMessageQueue)) != NULL) + if ((message = otMessageQueueGetHead(&mMessageQueue[mReadDirection])) != NULL) { - otMessageQueueDequeue(&mMessageQueue, message); + otMessageQueueDequeue(&mMessageQueue[mReadDirection], message); otMessageFree(message); } } // Move the pointer to next segment. - bufPtr = Advance(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask)); + bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection); } - mReadFrameStart = bufPtr; + mReadFrameStart[mReadDirection] = bufPtr; - mReadState = kReadStateDone; + UpdateReadWriteStartPointers(); + + mReadState = kReadStateNotActive; mReadFrameLength = kUnknownFrameLength; if (mFrameRemovedCallback != NULL) { - mFrameRemovedCallback(mFrameRemovedContext, tag, this); + mFrameRemovedCallback(mFrameRemovedContext, tag, static_cast(mReadDirection), this); } exit: return error; } +void NcpFrameBuffer::UpdateReadWriteStartPointers(void) +{ + // If there is no fully written high priority frame, and not in middle of writing a new frame either. + if (!HasFrame(kPriorityHigh) && !InFrameIsWriting(kPriorityHigh)) + { + // Move the high priority pointers to be right behind the low priority start. + mWriteFrameStart[kPriorityHigh] = GetUpdatedBufPtr(mReadFrameStart[kPriorityLow], 1, kBackward); + mReadFrameStart[kPriorityHigh] = mWriteFrameStart[kPriorityHigh]; + ExitNow(); + } + + // If there is no fully written low priority frame, and not in middle of writing a new frame either. + if (!HasFrame(kPriorityLow) && !InFrameIsWriting(kPriorityLow)) + { + // Move the low priority pointers to be 1 byte after the high priority start. + mWriteFrameStart[kPriorityLow] = GetUpdatedBufPtr(mReadFrameStart[kPriorityHigh], 1, kForward); + mReadFrameStart[kPriorityLow] = mWriteFrameStart[kPriorityLow]; + } + +exit: + return; +} uint16_t NcpFrameBuffer::OutFrameGetLength(void) { @@ -616,20 +758,22 @@ uint16_t NcpFrameBuffer::OutFrameGetLength(void) VerifyOrExit(!IsEmpty(), frameLength = 0); + OutFrameSelectReadDirection(); + // Calculate frame length by adding length of all segments and messages within the current frame. - bufPtr = mReadFrameStart; + bufPtr = mReadFrameStart[mReadDirection]; - while (bufPtr != mWriteFrameStart) + while (bufPtr != mWriteFrameStart[mReadDirection]) { // Read the segment header - header = ReadUint16At(bufPtr); + header = ReadUint16At(bufPtr, mReadDirection); // If the current segment defines a new frame, and it is not the start of current frame, then we have reached // end of current frame. if (header & kSegmentHeaderNewFrameFlag) { - if (bufPtr != mReadFrameStart) + if (bufPtr != mReadFrameStart[mReadDirection]) { break; } @@ -639,8 +783,8 @@ uint16_t NcpFrameBuffer::OutFrameGetLength(void) if (header & kSegmentHeaderMessageIndicatorFlag) { message = (message == NULL) ? - otMessageQueueGetHead(&mMessageQueue) : - otMessageQueueGetNext(&mMessageQueue, message); + otMessageQueueGetHead(&mMessageQueue[mReadDirection]) : + otMessageQueueGetNext(&mMessageQueue[mReadDirection], message); if (message != NULL) { @@ -652,22 +796,27 @@ uint16_t NcpFrameBuffer::OutFrameGetLength(void) frameLength += (header & kSegmentHeaderLengthMask); // Move the pointer to next segment. - bufPtr = Advance(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask)); + bufPtr = GetUpdatedBufPtr(bufPtr, kSegmentHeaderSize + (header & kSegmentHeaderLengthMask), mReadDirection); } - // Remember the calculated frame length for current frame. - mReadFrameLength = frameLength; + // Remember the calculated frame length for current active frame. + if (mReadState != kReadStateNotActive) + { + mReadFrameLength = frameLength; + } exit: return frameLength; } -NcpFrameBuffer::FrameTag NcpFrameBuffer::OutFrameGetTag(void) const +NcpFrameBuffer::FrameTag NcpFrameBuffer::OutFrameGetTag(void) { + OutFrameSelectReadDirection(); + // If buffer is empty use `kInvalidTag`, otherwise use the frame start pointer as the tag associated with // current out frame being read - return IsEmpty() ? kInvalidTag : mReadFrameStart; + return IsEmpty() ? kInvalidTag : mReadFrameStart[mReadDirection]; } } // namespace ot diff --git a/src/ncp/ncp_buffer.hpp b/src/ncp/ncp_buffer.hpp index 2d9fa39a8..d185992ce 100644 --- a/src/ncp/ncp_buffer.hpp +++ b/src/ncp/ncp_buffer.hpp @@ -39,11 +39,33 @@ namespace ot { + +/** + * This class implements a buffer/queue for storing NCP frames. + * + * A frame can consist of a sequence of data bytes and/or the content of an `otMessage` or a combination of the two. + * NcpFrameBuffer implements priority FIFO logic for storing and reading frames. Two priority levels of high and low + * are supported. Within same priority level first-in-first-out order is preserved. High priority frames are read + * ahead of the low priority ones. + * + */ class NcpFrameBuffer { public: + /** - * Defines the frame tag type. Frame tags can be compared with one another using operator `==`. + * Defines the priority of frame. High priority frames are read before low priority frames. Within same priority + * level FIFO order is preserved. + * + */ + enum Priority + { + kPriorityLow = 0, //< Indicates low/normal priority for a frame. + kPriorityHigh = 1, //< Indicates high priority for a frame. + }; + + /** + * Defines the (abstract) frame tag type. Frame tags can be compared with one another using operator `==`. * */ typedef const void *FrameTag; @@ -58,18 +80,19 @@ public: * Defines a function pointer callback which is invoked to inform a change in `NcpFrameBuffer` either when a new * frame is added/written to `NcpFrameBuffer` or when a frame is removed from `NcpFrameBuffer`. * - * @param[in] aContext A pointer to arbitrary context information. - * @param[in] aTag The tag associated with the frame which is added or removed. - * @param[in] aNcpFrameBuffer A pointer to the `NcpFrameBuffer`. + * @param[in] aContext A pointer to arbitrary context information. + * @param[in] aTag The tag associated with the frame which is added or removed. + * @param[in] aPriority The priority of frame. + * @param[in] aNcpFrameBuffer A pointer to the `NcpFrameBuffer`. * */ - typedef void (*BufferCallback)(void *aContext, FrameTag aTag, NcpFrameBuffer *aNcpFrameBuffer); + typedef void (*BufferCallback)(void *aContext, FrameTag aTag, Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer); /** * This constructor creates an NCP frame buffer. * - * @param[in] aBuffer A pointer to a buffer which will be used by NCP frame buffer. - * @param[in] aBufferLength The buffer size (in bytes). + * @param[in] aBuffer A pointer to a buffer which will be used by NCP frame buffer. + * @param[in] aBufferLength The buffer size (in bytes). * */ NcpFrameBuffer(uint8_t *aBuffer, uint16_t aBufferLength); @@ -85,8 +108,8 @@ public: * * Subsequent calls to this method will overwrite the previous callback and its context. * - * @param[in] aFrameAddedCallback Callback invoked when a new frame is successfully added to buffer. - * @param[in] aFrameAddedContext A pointer to arbitrary context used with frame added callback. + * @param[in] aFrameAddedCallback Callback invoked when a new frame is successfully added to buffer. + * @param[in] aFrameAddedContext A pointer to arbitrary context used with frame added callback. * */ void SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext); @@ -96,8 +119,8 @@ public: * * Subsequent calls to this method will overwrite the previous callback and its context. * - * @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer. - * @param[in] aFrameRemovedContext A pointer to arbitrary context used with frame removed callback. + * @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer. + * @param[in] aFrameRemovedContext A pointer to arbitrary context used with frame removed callback. * */ void SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext); @@ -105,40 +128,54 @@ public: /** * This method begins a new input frame to be added/written to the frame buffer. - * If there is a previous frame being written (`InFrameEnd()` has not yet been called on the frame), this method - * will discard and clear the previous unfinished frame. + * If there is a previous frame being written (for which `InFrameEnd()` has not yet been called), calling + * `InFrameBegin()` will discard and clear the previous unfinished frame. * - * @retval OT_ERROR_NONE Successfully started a new frame. - * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame. + * @param[in] aPriority Priority level of new input frame. + * + * @retval OT_ERROR_NONE Successfully started a new frame. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to start a new frame. * */ - otError InFrameBegin(void); + otError InFrameBegin(Priority aPriority); /** - * This method adds data to the current input frame being written to the buffer. + * This method adds data to the current input frame being written. * - * If no buffer space is available, this method will discard and clear the frame before returning an error status. + * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this + * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. * - * @param[in] aDataBuffer A pointer to data buffer. - * @param[in] aDataBufferLength The length of the data buffer. + * If no buffer space is available, this method will discard and clear the current input frame being written before + * returning the error status `OT_ERROR_NO_BUFS`. * - * @retval OT_ERROR_NONE Successfully added new data to the frame. - * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add data. + * @param[in] aDataBuffer A pointer to data buffer. + * @param[in] aDataBufferLength The length of the data buffer. + * + * @retval OT_ERROR_NONE Successfully added new data to the frame. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add data. + * @retval OT_ERROR_INVALID_STATE InFrameBegin() has not been called earlier to start the frame. * */ otError InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength); /** - * This method adds a message to the current input frame being written to the buffer. + * This method adds a message to the current input frame being written. + * + * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this + * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. + * + * If no buffer space is available, this method will discard and clear the frame before returning error status + * `OT_ERROR_NO_BUFS`. * - * If no buffer space is available, this method will discard and clear the frame before returning an error status. * In case of success, the passed-in message @p aMessage will be owned by the frame buffer instance and will be - * freed when either the the frame is removed or discarded. + * freed when either the the frame is removed or discarded. In case of failure @p aMessage remains unchanged. * - * @param[in] aMessage A message to be added to current frame. + * @param[in] aMessage A message to be added to current frame. * - * @retval OT_ERROR_NONE Successfully added the message to the frame. - * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. + * @retval OT_ERROR_NONE Successfully added the message to the frame. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. + * @retval OT_ERROR_INVALID_STATE InFrameBegin() has not been called earlier to start the frame. + * @retval OT_ERROR_INVALID_ARGS If @p aMessage is NULL. * */ otError InFrameFeedMessage(otMessage *aMessage); @@ -146,10 +183,15 @@ public: /** * This method finalizes/ends the current input frame being written to the buffer. * - * If no buffer space is available, this method will discard and clear the frame before returning an error status. + * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this + * method does nothing and returns error status `OT_ERROR_INVALID_STATE`. + + * If no buffer space is available, this method will discard and clear the frame before returning error status + * `OT_ERROR_NO_BUFS`. * - * @retval OT_ERROR_NONE Successfully added the message to the frame. - * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. + * @retval OT_ERROR_NONE Successfully added the message to the frame. + * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add message. + * @retval OT_ERROR_INVALID_STATE InFrameBegin() has not been called earlier to start the frame. * */ otError InFrameEnd(void); @@ -157,7 +199,7 @@ public: /** * This method returns the tag assigned to last successfully written/added frame to NcpBuffer (i.e., last input * frame for which `InFrameEnd()` was called and returned success status). The tag is a unique value (within - * currently queued frames) associated with each frame in the `NcpFrameBuffer`. The tag can be used to identify the + * currently queued frames) associated with a frame in the `NcpFrameBuffer`. The tag can be used to identify the * same frame when it is read and removed from the NcpBuffer. Tags can be compared using operator `==`. * * @returns The tag of last successfully written frame, or `kInvalidTag` if no frame is written so far. @@ -165,25 +207,28 @@ public: FrameTag InFrameGetLastTag(void) const; /** - * This method checks if the buffer is empty. An non-empty buffer contains at least one full frame for reading. + * This method checks if the buffer is empty. A non-empty buffer contains at least one full frame for reading. * - * @retval true Buffer is not empty and contains at least one full frame for reading. - * @retval false Buffer is empty and contains no frame for reading. + * @retval TRUE Buffer is not empty and contains at least one full frame for reading. + * @retval FALSE Buffer is empty and contains no frame for reading. * */ bool IsEmpty(void) const; /** - * This method begins/prepares a new output frame to be read from the frame buffer. + * This method begins/prepares an output frame to be read from the frame buffer if there is no current active output + * frame, or resets the read offset if there is a current active output frame. * * The NCP buffer maintains a read offset for the current frame being read. Before reading any bytes from the frame * this method should be called to prepare the frame and set the read offset. * - * If part of current frame has already been read, a sub-sequent call to this method will reset the read offset - * back to beginning of current output frame. + * If part or even all of current frame has been read, a sub-sequent call to this method will reset the read + * offset back to beginning of current output frame (note that the current output frame will remain unchanged even + * in case where a higher priority frame was written to buffer while reading current output frame). A prepared + * output frame will stay active as current output frame until it is explicitly removed using `OutFrameRemove()`. * - * @retval OT_ERROR_NONE Successfully started/prepared a new output frame for reading. - * @retval OT_ERROR_NOT_FOUND No frame available in buffer for reading. + * @retval OT_ERROR_NONE Successfully started/prepared a new output frame for reading. + * @retval OT_ERROR_NOT_FOUND No frame available in buffer for reading. * */ otError OutFrameBegin(void); @@ -191,11 +236,13 @@ public: /** * This method checks if the current output frame (being read) has ended. * - * The NCP buffer maintains a read offset for the current output frame being read. This method returns true if the - * read offset is at the end of the frame and there are no more bytes available to read from current frame. + * The NCP buffer maintains a read offset for the current output frame being read. This method returns `true` if + * the read offset has reached the end of the frame and there are no more bytes available to read, or if there is + * no currently active output frame. * - * @retval true Frame has ended (no more bytes available to read from current output frame). - * @retval false Frame has data (can read more bytes from current output frame). + * @retval TRUE Frame has ended (no more bytes available to read from current output frame), or + * there is currently no prepared/active output frame. + * @retval FALSE Frame still has more data to read. * */ bool OutFrameHasEnded(void); @@ -207,72 +254,74 @@ public: * the next byte from the current frame and moves the read offset forward. If read offset is already at the end * current output frame, this method returns zero. * - * @returns The next byte from the current output frame or zero if frame has ended. + * @returns The next byte from the current output frame or zero if current output frame has ended or there is + * prepared/active output from. * */ uint8_t OutFrameReadByte(void); /** - * This method reads bytes from the current output frame. + * This method reads and copies bytes from the current output frame into a given buffer. * * The NCP buffer maintains a read offset for the current output frame being read. This method attempts to read * the given number of bytes (@p aDataBufferLength) from the current frame and copies the bytes into the given - * data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are less bytes - * remaining in current frame, the available bytes are read/copied. This methods returns the actual number of bytes - * read. + * data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are fewer bytes + * remaining in current frame than the request @p aReadLength, the available bytes are read/copied. This methods + * returns the actual number of bytes read. * - * @param[in] aDataBuffer A pointer to a data buffer. - * @param[in] aReadLength Number of bytes to read. + * @param[in] aDataBuffer A pointer to a data buffer. + * @param[in] aReadLength Number of bytes to read. * - * @returns The number of bytes read and copied into data buffer. + * @returns The number of bytes read and copied into data buffer. * */ uint16_t OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer); /** - * This method removes the current/front output frame from the buffer. + * This method removes the current or front output frame from the buffer. * - * The NCP buffer stores the frames in FIFO order. This method removes the front frame (which may be the current - * output frame being read) from the buffer. There is no need to prepare/begin reading the current frame before - * removing it, so the front frame can be removed without a previous call to `OutFrameBegin()`. + * If there is an active output from being read (an output frame was prepared earlier with a successful call to + * `OutFrameBegin()`), this method removes the current active output frame. If there is no current active frame, + * the front frame in the queue (the next frame which would have been read) will be removed. * * When a frame is removed all its associated messages will be freed. * * If the remove operation is successful, this method will invoke the `FrameRemovedCallback` (if provided). * - * @retval OT_ERROR_NONE Successfully removed the front frame. - * @retval OT_ERROR_NOT_FOUND No frame available in NCP frame buffer to remove. + * @retval OT_ERROR_NONE Successfully removed the front frame. + * @retval OT_ERROR_NOT_FOUND No frame available in NCP frame buffer to remove. * */ otError OutFrameRemove(void); /** - * This method returns the number of bytes (length) of current/front frame in the NCP frame buffer. + * This method returns the number of bytes (length) of current or front frame in the NCP frame buffer. * - * The NCP buffer stores the frames in FIFO order. This method returns the length of the front frame (which may - * be the current output frame being read) from the buffer. There is no need to prepare/begin reading the current - * frame before calling this method so this method can be used without a previous call to `OutFrameBegin()`. + * If there is an active output from being read (an output frame was prepared earlier with successful call to + * `OutFrameBegin()`), this method returns the length of the current output frame. If there is no current active + * frame, the length of the front frame in the queue (the next frame which would have been read) will be returned. * * If there is no frame in buffer, this method returns zero. * - * @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer. + * @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer. * */ uint16_t OutFrameGetLength(void); /** - * This method returns the tag value associated to current/front frame in the NCP frame buffer. + * This method returns the tag value associated to current or front frame in the NCP frame buffer. * - * The NCP buffer stores the frames in FIFO order. This method returns the tag of the front frame (which may - * be the current output frame being read) from the buffer. There is no need to prepare/begin reading the current - * frame before calling this method so this method can be used without a previous call to `OutFrameBegin()`. + * If there is an active output from being read (an output frame was prepared earlier with successful call to + * `OutFrameBegin()`), this method returns the tag associated with current output frame. If there is no current + * active frame, the tag associated with the front frame in the queue (the next frame which would have been read) + * will be returned. * * If there is no frame in buffer, this method returns `kInvalidTag`. * - * @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer. + * @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer. * */ - FrameTag OutFrameGetTag(void) const; + FrameTag OutFrameGetTag(void); private: @@ -280,19 +329,20 @@ private: * NcpFrameBuffer Implementation * ----------------------------- * - * NcpFrameBuffer internally stores a frame as a sequence of data segments. The data segments are stored in the - * the main buffer `mBuffer`. mBuffer is utilized as a circular buffer. + * NcpFrameBuffer internally stores a frame as a sequence of data segments. Each segment stores a portion of frame. + * The data segments are stored in the the main buffer `mBuffer`. mBuffer is utilized as a circular buffer. - * Messages (which are added using `InFrameFeedMessaged()`) are not copied in the `mBuffer` but instead are - * enqueued in a message queue `mMessageQueue`. + * The content of messages (which are added using `InFrameFeedMessage()`) are not directly copied in the `mBuffer` + * but instead they are enqueued in a message queue `mMessageQueue`. * - * The data segments include a header before the data portion. The header is 2 bytes long is formated as follows + * Every data segments starts with a header before the data portion. The header is 2 bytes long with the following + * format: * * Bit 0-13: Give the length of the data segment (max segment len is 2^14 = 16,384 bytes). * Bit 14: Flag bit set to indicate that this segment has an associated `Message` (appended to its end). * Bit 15: Flag bit set to indicate that this segment defines the start of a new frame. * - * Bit 15 Bit 14 Bits: 0 - 13 + * Bit 15 Bit 14 Bits: 0 - 13 * +--------------+--------------+--------------------------------------------------------+ * | New Frame | Has Message | Length of segment (excluding the header) | * +--------------+--------------+--------------------------------------------------------+ @@ -311,7 +361,7 @@ private: * This frame is stored as two segments: * * - Segment #1 contains "HelloThere" with a header of `0xC00A` which shows that this segment contains 10 data - * bytes, and it starts a new frame, and also must include a message from the message queue. + * bytes, and it starts a new frame, and also must includes an appended message from the message queue. * * - Segment #2 contains "Bye" with a header value of `0x0003` showing length of 3 and no appended message. * @@ -322,24 +372,77 @@ private: * Segment #1 Header Segment #2 Header * * - * Buffer pointers: + * NcpFrameBuffer uses the `mBuffer` as a circular/ring buffer. To support two frame priorities the buffer is + * divided in two high-priority and low-priority regions. The high priority frames are stored in buffer in backward + * direction while the low-priority frames use the buffer in forward direction. This model ensures the available + * buffer space is utilized efficiently between all frame types. + * + * mReadFrameStart[kPriorityLow] + * | + * | mWriteFrameStart[kPriorityLow] + * | | + * V Low Priority Frames V + * --------------+------------------------------+------------------------------+------------------ + * ... | <-------- | --------> | ... + * --------------+------------------------------+------------------------------+------------------ + * ^ High Priority Frames ^ + * | | + * | mReadFrameStart[kPriorityHigh] + * | + * mWriteFrameStart[kPriorityHigh] + * + * + * + * When frames are removed, if possible, the `mReadFrameStart` and `mWriteFrameStart` pointers of two priority + * levels are moved closer to avoid gaps. + * + * For an output frame (frame being read), NcpFrameBuffer maintains a `ReadState` along with a set of pointers + * into the buffer: + * + * mReadFrameStart[priority]: Start of current/front frame. + * | + * | mReadSegmentHead: Start of current segment of active output frame. + * | | + * | | mReadPointer: Pointer to next byte to read in current segment. + * | | | + * | | | mReadSegmentTail: End of current segment. + * | | | | + * V V V V + * ---------+------------+--------------------------+------+----------------+----------------------------------- + * ... | Segment 1 | Segment 2 | ... | Last Segment | ... (possible) next frame + * ---------+------------+--------------------------+------+----------------+----------------------------------- + * \ | | / + * | \------------v-----------/ | + * | Current Segment | + * | | + * \---------------------------V-----------------------------/ + * Current OutFrame (being read) + * + * Note that the diagram above shows the pointers for a low-priority frame (with pointers increasing in forward + * direction). + * + * The `ReadState` indicates the state of current output frame and its read offset (e.g., read offset is in middle + * of a segment or it is in an appended message, or we are done with entire frame). + * + * For a input frame (frame being written), the following pointers are maintained: + * + * mWriteFrameWrite[priority]: Start of current/next frame being written. + * | + * | mWriteSegmentHead: Start of current segment of active input frame. + * | | + * | | mWriteSegmentTail: Pointer to next byte to write in current segment. + * | | | + * | | | + * | | | + * V V V + * ------------------+------------------+------------------------------------------------------------------- + * Previous Frames | Segment 1 | Segment 2 : . . . + * ------------------+------------------+------------------------------------------------------------------- + * \ / + * | | + * \------------------V-----------------/ + * Current InFrame (being written) * - * mReadFrameStart - * | - * | mReadSegmentHead mWriteFrameStart - * | | | - * | | mReadPointer | mWriteSegmentHead - * | | | | | - * mBuffer | | | mReadSegmentTail | | mWriteSegmentTail mBufferEnd - * | | | | | | | | | - * V V V V V V V V V - * +-------+---------+---------+-------+--------------------------+---------+---------+----------------------+- - * | ... | Seg 1 | Seg 2 | Seg 3 | . . . | Seg 1 | Seg 2 : . . . | - * +-------+---------+---------+-------+---------------------------+---------+--------+----------------------+- - * \ \ / / \ / - * | Cur segment | | | - * | | | | - * Current OutFrame (being read) Current InFrame (being written) * */ @@ -354,29 +457,43 @@ private: kSegmentHeaderNoFlag = 0, // No flags are set. kSegmentHeaderNewFrameFlag = (1 << 15), // Indicates that this segment starts a new frame. kSegmentHeaderMessageIndicatorFlag = (1 << 14), // Indicates this segment ends with a Message. + + kNumPrios = (kPriorityHigh + 1), // Number of priorities. }; enum ReadState { - kReadStateInSegment, // In middle of a data segment while reading current (out) frame. - kReadStateInMessage, // In middle of a message while reading current (out) frame. - kReadStateDone, // Current (out) frame is read fully. + kReadStateNotActive, // No current prepared out frame. + kReadStateInSegment, // In middle of a data segment while reading current frame. + kReadStateInMessage, // In middle of a message while reading current frame. + kReadStateDone, // Current (out) frame is read fully. + }; + + enum Direction + { + kForward = kPriorityLow, + kBackward = kPriorityHigh, + kUnknown, }; // Private methods - uint8_t * Next(uint8_t *aBufferPtr) const; - uint8_t * Advance(uint8_t *aBufPtr, uint16_t aOffset) const; - uint16_t GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr) const; + uint8_t * GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const; + uint16_t GetDistance(uint8_t *aStartPtr, uint8_t *aEndPtr, Direction aDirection) const; - uint16_t ReadUint16At(uint8_t *aBufPtr); - void WriteUint16At(uint8_t *aBufPtr, uint16_t aValue); + uint16_t ReadUint16At(uint8_t *aBufPtr, Direction aDirection); + void WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection); + + bool HasFrame(Priority aPriority) const; + void UpdateReadWriteStartPointers(void); otError InFrameFeedByte(uint8_t aByte); otError InFrameBeginSegment(void); void InFrameEndSegment(uint16_t aSegmentHeaderFlags); void InFrameDiscard(void); + bool InFrameIsWriting(Priority aPriority) const; + void OutFrameSelectReadDirection(void); otError OutFramePrepareSegment(void); void OutFrameMoveToNextSegment(void); otError OutFramePrepareMessage(void); @@ -391,18 +508,20 @@ private: BufferCallback mFrameRemovedCallback; // Callback to signal when a frame is removed. void * mFrameRemovedContext; // Context passed to `mFrameRemovedCallback`. - otMessageQueue mMessageQueue; // Main message queue. + otMessageQueue mMessageQueue[kNumPrios]; // Main message queues. + Direction mWriteDirection; // Direction (priority) for current frame being read. otMessageQueue mWriteFrameMessageQueue; // Message queue for the current frame being written. - uint8_t * mWriteFrameStart; // Pointer to start of current frame being written. + uint8_t * mWriteFrameStart[kNumPrios]; // Pointer to start of current frame being written. uint8_t * mWriteSegmentHead; // Pointer to start of current segment in the frame being written. uint8_t * mWriteSegmentTail; // Pointer to end of current segment in the frame being written. FrameTag mWriteFrameTag; // Tag associated with last successfully written frame. + Direction mReadDirection; // Direction (priority) for current frame being read. ReadState mReadState; // Read state. uint16_t mReadFrameLength; // Length of current frame being read. - uint8_t * mReadFrameStart; // Pointer to start of current frame being read. + uint8_t * mReadFrameStart[kNumPrios]; // Pointer to start of current frame being read. uint8_t * mReadSegmentHead; // Pointer to start of current segment in the frame being read. uint8_t * mReadSegmentTail; // Pointer to end of current segment in the frame being read. uint8_t * mReadPointer; // Pointer to next byte to read (either in segment or in msg buffer). diff --git a/src/ncp/ncp_spi.cpp b/src/ncp/ncp_spi.cpp index e8ee8e4bf..4e29194fc 100644 --- a/src/ncp/ncp_spi.cpp +++ b/src/ncp/ncp_spi.cpp @@ -246,10 +246,12 @@ void NcpSpi::SpiTransactionProcess(void) } } -void NcpSpi::HandleFrameAddedToTxBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpFrameBuffer) +void NcpSpi::HandleFrameAddedToTxBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag, + NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer) { OT_UNUSED_VARIABLE(aNcpFrameBuffer); OT_UNUSED_VARIABLE(aTag); + OT_UNUSED_VARIABLE(aPriority); static_cast(aContext)->mPrepareTxFrameTask.Post(); } diff --git a/src/ncp/ncp_spi.hpp b/src/ncp/ncp_spi.hpp index a80519e31..55cbf2276 100644 --- a/src/ncp/ncp_spi.hpp +++ b/src/ncp/ncp_spi.hpp @@ -82,7 +82,7 @@ private: void SpiTransactionProcess(void); static void HandleFrameAddedToTxBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag, - NcpFrameBuffer *aNcpFrameBuffer); + NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer); static void PrepareTxFrame(Tasklet &aTasklet); void PrepareTxFrame(void); diff --git a/src/ncp/ncp_uart.cpp b/src/ncp/ncp_uart.cpp index de1be8dee..53b30d565 100644 --- a/src/ncp/ncp_uart.cpp +++ b/src/ncp/ncp_uart.cpp @@ -106,10 +106,11 @@ NcpUart::NcpUart(otInstance *aInstance): } void NcpUart::HandleFrameAddedToNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag, - NcpFrameBuffer *aNcpFrameBuffer) + NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer) { OT_UNUSED_VARIABLE(aNcpFrameBuffer); OT_UNUSED_VARIABLE(aTag); + OT_UNUSED_VARIABLE(aPriority); static_cast(aContext)->HandleFrameAddedToNcpBuffer(); } diff --git a/src/ncp/ncp_uart.hpp b/src/ncp/ncp_uart.hpp index eb1fa4d16..a80d2a824 100644 --- a/src/ncp/ncp_uart.hpp +++ b/src/ncp/ncp_uart.hpp @@ -105,7 +105,7 @@ private: static void HandleFrame(void *context, uint8_t *aBuf, uint16_t aBufLength); static void HandleError(void *context, otError aError, uint8_t *aBuf, uint16_t aBufLength); static void HandleFrameAddedToNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag, - NcpFrameBuffer *aNcpFrameBuffer); + NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpFrameBuffer); Hdlc::Encoder mFrameEncoder; Hdlc::Decoder mFrameDecoder; diff --git a/tests/unit/test_ncp_buffer.cpp b/tests/unit/test_ncp_buffer.cpp index 914c4ff70..1d08b581c 100644 --- a/tests/unit/test_ncp_buffer.cpp +++ b/tests/unit/test_ncp_buffer.cpp @@ -42,9 +42,10 @@ namespace ot { // This module implements unit-test for NcpFrameBuffer class. +// Test related constants: enum { - kTestBufferSize = 2500, + kTestBufferSize = 800, kTestIterationAttemps = 10000, kTagArraySize = 1000, }; @@ -66,36 +67,54 @@ struct CallbackContext CallbackContext sContext; -NcpFrameBuffer::FrameTag sTagHistoryArray[kTagArraySize]; -uint32_t sTagHistoryHead = 0; -uint32_t sTagHistoryTail = 0; +enum +{ + kNumPrios = 2, // Number of priority levels. + + kTestFrame1Size = sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText) + sizeof(sHelloText), + kTestFrame2Size = sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText), + kTestFrame3Size = sizeof(sMysteryText), + kTestFrame4Size = sizeof(sOpenThreadText), +}; + +NcpFrameBuffer::FrameTag sTagHistoryArray[kNumPrios][kTagArraySize]; +uint32_t sTagHistoryHead[kNumPrios] = {0}; +uint32_t sTagHistoryTail[kNumPrios] = {0}; NcpFrameBuffer::FrameTag sExpectedRemovedTag = NcpFrameBuffer::kInvalidTag; void ClearTagHistory(void) { - sTagHistoryHead = sTagHistoryTail; + for (uint8_t priority = 0; priority < kNumPrios; priority++) + { + sTagHistoryHead[priority] = sTagHistoryTail[priority]; + } } -void AddTagToHistory(NcpFrameBuffer::FrameTag aTag) +void AddTagToHistory(NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority) { - sTagHistoryArray[sTagHistoryTail] = aTag; + uint8_t priority = static_cast(aPriority); - if (++sTagHistoryTail == kTagArraySize) + sTagHistoryArray[priority][sTagHistoryTail[priority]] = aTag; + + if (++sTagHistoryTail[priority] == kTagArraySize) { - sTagHistoryTail = 0; + sTagHistoryTail[priority] = 0; } - VerifyOrQuit(sTagHistoryTail != sTagHistoryHead, "Ran out of space in `TagHistoryArray`, increase its size."); + VerifyOrQuit(sTagHistoryTail[priority] != sTagHistoryHead[priority], + "Ran out of space in `TagHistoryArray`, increase its size."); } -void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag) +void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority) { - VerifyOrQuit(sTagHistoryHead != sTagHistoryTail, "Tag history is empty,"); - VerifyOrQuit(aTag == sTagHistoryArray[sTagHistoryHead], "Removed tag does not match the added one"); + uint8_t priority = static_cast(aPriority); - if (++sTagHistoryHead == kTagArraySize) + VerifyOrQuit(sTagHistoryHead[priority] != sTagHistoryTail[priority], "Tag history is empty,"); + VerifyOrQuit(aTag == sTagHistoryArray[priority][sTagHistoryHead[priority]], "Removed tag does not match the added one"); + + if (++sTagHistoryHead[priority] == kTagArraySize) { - sTagHistoryHead = 0; + sTagHistoryHead[priority] = 0; } if (sExpectedRemovedTag != NcpFrameBuffer::kInvalidTag) @@ -105,7 +124,8 @@ void VerifyAndRemoveTagFromHistory(NcpFrameBuffer::FrameTag aTag) } } -void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpBuffer) +void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority, + NcpFrameBuffer *aNcpBuffer) { CallbackContext *callbackContext = reinterpret_cast(aContext); @@ -113,19 +133,22 @@ void FrameAddedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameB VerifyOrQuit(callbackContext != NULL, "Null context in the callback"); VerifyOrQuit(aTag != NcpFrameBuffer::kInvalidTag, "Invalid tag in the callback"); VerifyOrQuit(aTag == aNcpBuffer->InFrameGetLastTag(), "InFrameGetLastTag() does not match the tag from callback"); - AddTagToHistory(aTag); + + AddTagToHistory(aTag, aPriority); callbackContext->mFrameAddedCount++; } -void FrameRemovedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer *aNcpBuffer) +void FrameRemovedCallback(void *aContext, NcpFrameBuffer::FrameTag aTag, NcpFrameBuffer::Priority aPriority, + NcpFrameBuffer *aNcpBuffer) { CallbackContext *callbackContext = reinterpret_cast(aContext); VerifyOrQuit(aNcpBuffer != NULL, "Null NcpFrameBuffer in the callback"); VerifyOrQuit(callbackContext != NULL, "Null context in the callback"); VerifyOrQuit(aTag != NcpFrameBuffer::kInvalidTag, "Invalid tag in the callback"); - VerifyAndRemoveTagFromHistory(aTag); + + VerifyAndRemoveTagFromHistory(aTag, aPriority); callbackContext->mFrameRemovedCount++; } @@ -183,7 +206,7 @@ void ReadAndVerifyContent(NcpFrameBuffer &aNcpBuffer, const uint8_t *aContentBuf } } -void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer) +void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority) { Message *message; CallbackContext oldContext; @@ -194,13 +217,12 @@ void WriteTestFrame1(NcpFrameBuffer &aNcpBuffer) message->Write(0, sizeof(sMottoText), sMottoText); oldContext = sContext; - SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameBegin() failed."); + SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameBegin() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)), "InFrameFeedData() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)), "InFrameFeedData() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message), "InFrameFeedMessage() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)), "InFrameFeedData() failed."); SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed."); - VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed."); VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed."); } @@ -210,27 +232,24 @@ void VerifyAndRemoveFrame1(NcpFrameBuffer &aNcpBuffer) CallbackContext oldContext = sContext; sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag(); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag(), "OutFrameGetTag() value changed unexpectedly."); - - VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMottoText) + sizeof(sMysteryText) + sizeof(sMottoText) - + sizeof(sHelloText), "GetLength() is incorrect."); - + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText)); ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText)); ReadAndVerifyContent(aNcpBuffer, sMottoText, sizeof(sMottoText)); ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText)); - VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected."); VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame."); VerifyOrQuit(sExpectedRemovedTag == aNcpBuffer.OutFrameGetTag(), "OutFrameGetTag() value changed unexpectedly."); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed."); - VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed."); VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed."); } -void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer) +void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority) { Message *message1; Message *message2; @@ -246,7 +265,7 @@ void WriteTestFrame2(NcpFrameBuffer &aNcpBuffer) SuccessOrQuit(message2->SetLength(sizeof(sHelloText)), "Could not set the length of message."); message2->Write(0, sizeof(sHelloText), sHelloText); - SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameFeedBegin() failed."); + SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameFeedBegin() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1), "InFrameFeedMessage() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message2), "InFrameFeedMessage() failed."); @@ -260,25 +279,23 @@ void VerifyAndRemoveFrame2(NcpFrameBuffer &aNcpBuffer) { CallbackContext oldContext = sContext; + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size, "GetLength() is incorrect."); SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); - - VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText) + sizeof(sHelloText) + sizeof(sOpenThreadText), - "GetLength() is incorrect."); - + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size, "GetLength() is incorrect."); ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText)); ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText)); ReadAndVerifyContent(aNcpBuffer, sHelloText, sizeof(sHelloText)); - VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected."); VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame."); sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag(); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == kTestFrame2Size, "GetLength() is incorrect."); SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed."); VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed."); VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed."); } -void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer) +void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority) { Message *message1; CallbackContext oldContext = sContext; @@ -289,7 +306,7 @@ void WriteTestFrame3(NcpFrameBuffer &aNcpBuffer) // An empty message with no content. SuccessOrQuit(message1->SetLength(0), "Could not set the length of message."); - SuccessOrQuit(aNcpBuffer.InFrameBegin(), "InFrameFeedBegin() failed."); + SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameFeedBegin() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedMessage(message1), "InFrameFeedMessage() failed."); SuccessOrQuit(aNcpBuffer.InFrameFeedData(sMysteryText, sizeof(sMysteryText)), "InFrameFeedData() failed."); SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed."); @@ -302,15 +319,44 @@ void VerifyAndRemoveFrame3(NcpFrameBuffer &aNcpBuffer) { CallbackContext oldContext = sContext; - SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); - VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect."); - + SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect."); ReadAndVerifyContent(aNcpBuffer, sMysteryText, sizeof(sMysteryText)); - VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected."); VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame."); sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag(); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sMysteryText), "GetLength() is incorrect."); + SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed."); + + VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed."); + VerifyOrQuit(oldContext.mFrameRemovedCount + 1 == sContext.mFrameRemovedCount, "FrameRemovedCallback failed."); +} + +void WriteTestFrame4(NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority) +{ + CallbackContext oldContext = sContext; + + SuccessOrQuit(aNcpBuffer.InFrameBegin(aPriority), "InFrameFeedBegin() failed."); + SuccessOrQuit(aNcpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed."); + SuccessOrQuit(aNcpBuffer.InFrameEnd(), "InFrameEnd() failed."); + + VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed."); + VerifyOrQuit(oldContext.mFrameRemovedCount == sContext.mFrameRemovedCount, "FrameRemovedCallback failed."); +} + +void VerifyAndRemoveFrame4(NcpFrameBuffer &aNcpBuffer) +{ + CallbackContext oldContext = sContext; + + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText), "GetLength() is incorrect."); + SuccessOrQuit(aNcpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText), "GetLength() is incorrect."); + ReadAndVerifyContent(aNcpBuffer, sOpenThreadText, sizeof(sOpenThreadText)); + VerifyOrQuit(aNcpBuffer.OutFrameHasEnded() == true, "Frame longer than expected."); + VerifyOrQuit(aNcpBuffer.OutFrameReadByte() == 0, "ReadByte() returned non-zero after end of frame."); + sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag(); + VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == sizeof(sOpenThreadText), "GetLength() is incorrect."); SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "Remove() failed."); VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed."); @@ -352,21 +398,47 @@ void TestNcpFrameBuffer(void) VerifyOrQuit(ncpBuffer.OutFrameGetTag() == NcpFrameBuffer::kInvalidTag, "Incorrect OutFrameTag after init."); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - printf("\nTest 2: Write a frame 1 "); + printf("\nTest 2: Write and read a single frame"); - WriteTestFrame1(ncpBuffer); - DumpBuffer("\nBuffer after frame1", buffer, kTestBufferSize); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + DumpBuffer("\nBuffer after frame1 (low priority)", buffer, kTestBufferSize); printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength()); + VerifyAndRemoveFrame1(ncpBuffer); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + DumpBuffer("\nBuffer after frame1 (high priority)", buffer, kTestBufferSize); + printf("\nFrameLen is %u", ncpBuffer.OutFrameGetLength()); VerifyAndRemoveFrame1(ncpBuffer); printf("\nIterations: "); - // Repeat this multiple times. + // Always add as low priority. for (j = 0; j < kTestIterationAttemps; j++) { printf("*"); - WriteTestFrame1(ncpBuffer); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty"); + + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty."); + } + + // Always add as high priority. + for (j = 0; j < kTestIterationAttemps; j++) + { + printf("*"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty"); + + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty."); + } + + // Every 5th add as high priority. + for (j = 0; j < kTestIterationAttemps; j++) + { + printf("*"); + WriteTestFrame1(ncpBuffer, ((j % 5) == 0) ? NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow); VerifyOrQuit(ncpBuffer.IsEmpty() == false, "IsEmpty() is incorrect when buffer is non-empty"); VerifyAndRemoveFrame1(ncpBuffer); @@ -376,12 +448,12 @@ void TestNcpFrameBuffer(void) printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - printf("\nTest 3: Multiple frames write and read "); + printf("\nTest 3: Multiple frames write and read (same priority)"); - WriteTestFrame2(ncpBuffer); - WriteTestFrame3(ncpBuffer); - WriteTestFrame2(ncpBuffer); - WriteTestFrame2(ncpBuffer); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); DumpBuffer("\nBuffer after multiple frames", buffer, kTestBufferSize); @@ -397,15 +469,15 @@ void TestNcpFrameBuffer(void) { printf("*"); - WriteTestFrame2(ncpBuffer); - WriteTestFrame3(ncpBuffer); - WriteTestFrame2(ncpBuffer); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); VerifyAndRemoveFrame2(ncpBuffer); VerifyAndRemoveFrame3(ncpBuffer); - WriteTestFrame2(ncpBuffer); - WriteTestFrame3(ncpBuffer); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow); VerifyAndRemoveFrame2(ncpBuffer); VerifyAndRemoveFrame2(ncpBuffer); @@ -417,14 +489,75 @@ void TestNcpFrameBuffer(void) printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - printf("\nTest 4: Frame discard when buffer full and partial read restart"); + printf("\nTest 4: Multiple frames write and read (mixed priority)"); + + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame3(ncpBuffer); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame3(ncpBuffer); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame2(ncpBuffer); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + + printf(" -- PASS\n"); + + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\nTest 5: Frame discard when buffer full and partial read restart"); + + printf("\nIterations: "); for (j = 0; j < kTestIterationAttemps; j++) { - WriteTestFrame2(ncpBuffer); - WriteTestFrame3(ncpBuffer); + bool frame1IsHighPriority = ((j % 3) == 0); - ncpBuffer.InFrameBegin(); + printf("*"); + + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + + ncpBuffer.InFrameBegin((j % 2) == 0 ? NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow); ncpBuffer.InFrameFeedData(sHelloText, sizeof(sHelloText)); message = sMessagePool->New(Message::kTypeIp6, 0); @@ -434,20 +567,11 @@ void TestNcpFrameBuffer(void) ncpBuffer.InFrameFeedMessage(message); - // Now cause a restart the current frame and test if it's discarded ok. - WriteTestFrame2(ncpBuffer); + // Start writing a new frame in middle of an unfinished frame. Ensure the first one is discarded. + WriteTestFrame1(ncpBuffer, + frame1IsHighPriority ? NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow); - if (j == 0) - { - DumpBuffer("\nAfter frame gets discarded", buffer, kTestBufferSize); - printf("\nIterations: "); - } - else - { - printf("*"); - } - - VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame3(ncpBuffer); // Start reading few bytes from the frame ncpBuffer.OutFrameBegin(); @@ -456,8 +580,17 @@ void TestNcpFrameBuffer(void) ncpBuffer.OutFrameReadByte(); // Now reset the read pointer and read/verify the frame from start. - VerifyAndRemoveFrame3(ncpBuffer); - VerifyAndRemoveFrame2(ncpBuffer); + if (frame1IsHighPriority) + { + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + } + else + { + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + } + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty."); } @@ -465,9 +598,9 @@ void TestNcpFrameBuffer(void) printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - printf("\nTest 5: Clear() and empty buffer method tests"); + printf("\nTest 6: Clear() and empty buffer method tests"); - WriteTestFrame1(ncpBuffer); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); ncpBuffer.Clear(); ClearTagHistory(); @@ -480,7 +613,7 @@ void TestNcpFrameBuffer(void) "Remove() returned incorrect error status when buffer is empty."); VerifyOrQuit(ncpBuffer.OutFrameGetLength() == 0, "OutFrameGetLength() returned non-zero length when buffer is empty."); - WriteTestFrame1(ncpBuffer); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); VerifyAndRemoveFrame1(ncpBuffer); VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() is incorrect when buffer is empty."); @@ -492,9 +625,9 @@ void TestNcpFrameBuffer(void) printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - printf("\nTest 6: OutFrameRead() in parts\n"); + printf("\nTest 7: OutFrameRead() in parts\n"); - ncpBuffer.InFrameBegin(); + ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityLow); ncpBuffer.InFrameFeedData(sMottoText, sizeof(sMottoText)); ncpBuffer.InFrameEnd(); @@ -512,8 +645,159 @@ void TestNcpFrameBuffer(void) } VerifyOrQuit(readOffset == sizeof(sMottoText), "Read len does not match expected length."); + + ncpBuffer.OutFrameRemove(); + printf("\n -- PASS\n"); + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\nTest 8: Remove a frame without reading it first"); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + SuccessOrQuit(ncpBuffer.OutFrameRemove(), "Remove() failed."); + VerifyAndRemoveFrame2(ncpBuffer); + printf(" -- PASS\n"); + + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\nTest 9: Check length when front frame gets changed (a higher priority frame is added)"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame3Size, "GetLength() is incorrect."); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + printf(" -- PASS\n"); + + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\nTest 10: Active out frame remaining unchanged when a higher priority frame is written while reading it"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText)); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText)); + SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText)); + ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText)); + ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText)); + ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText)); + VerifyOrQuit(ncpBuffer.OutFrameHasEnded() == true, "Frame longer than expected."); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + // Repeat test reversing frame priority orders. + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText)); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText)); + SuccessOrQuit(ncpBuffer.OutFrameBegin(), "OutFrameBegin() failed unexpectedly."); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText)); + ReadAndVerifyContent(ncpBuffer, sMysteryText, sizeof(sMysteryText)); + ReadAndVerifyContent(ncpBuffer, sMottoText, sizeof(sMottoText)); + ReadAndVerifyContent(ncpBuffer, sHelloText, sizeof(sHelloText)); + VerifyOrQuit(ncpBuffer.OutFrameHasEnded() == true, "Frame longer than expected."); + WriteTestFrame3(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame4(ncpBuffer, NcpFrameBuffer::kPriorityLow); + VerifyOrQuit(ncpBuffer.OutFrameGetLength() == kTestFrame1Size, "GetLength() is incorrect."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame3(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame4(ncpBuffer); + printf(" -- PASS\n"); + + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\n Test 11: Read and remove in middle of an active input frame write"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameFeedBegin() failed."); + SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed."); + SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed."); + VerifyAndRemoveFrame4(ncpBuffer); + // Repeat the test reversing priorities + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityLow), "InFrameFeedBegin() failed."); + SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed."); + SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed."); + VerifyAndRemoveFrame4(ncpBuffer); + // Repeat the test with same priorities + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameFeedBegin() failed."); + SuccessOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, sizeof(sOpenThreadText)), "InFrameFeedData() failed."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed."); + SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed."); + VerifyAndRemoveFrame4(ncpBuffer); + printf(" -- PASS\n"); + + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\n Test 12: Check returned error status"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameFeedBegin() failed."); + VerifyOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer)) == OT_ERROR_NO_BUFS, "Incorrect error status"); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty() failed."); + + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityLow); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + // Ensure writes with starting `InFrameBegin()` fail + VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 1) == OT_ERROR_INVALID_STATE, "Incorrect error status"); + VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE, "Incorrect error status"); + VerifyOrQuit(ncpBuffer.InFrameFeedData(sOpenThreadText, 0) == OT_ERROR_INVALID_STATE, "Incorrect error status"); + VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE, "Incorrect error status"); + message = sMessagePool->New(Message::kTypeIp6, 0); + VerifyOrQuit(message != NULL, "Null Message"); + SuccessOrQuit(message->SetLength(sizeof(sMysteryText)), "Could not set the length of message."); + message->Write(0, sizeof(sMysteryText), sMysteryText); + VerifyOrQuit(ncpBuffer.InFrameFeedMessage(message) == OT_ERROR_INVALID_STATE, "Incorrect error status"); + SuccessOrQuit(message->Free(), "Failed to free allocated message"); + VerifyOrQuit(ncpBuffer.InFrameEnd() == OT_ERROR_INVALID_STATE, "Incorrect error status"); + VerifyAndRemoveFrame2(ncpBuffer); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() failed"); + VerifyOrQuit(ncpBuffer.OutFrameBegin() == OT_ERROR_NOT_FOUND, "OutFrameBegin() failed on empty queue"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyOrQuit(ncpBuffer.IsEmpty(), "IsEmpty() failed"); + printf(" -- PASS\n"); + + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + printf("\n Test 13: Ensure we can utilize the full buffer size when frames removed during write"); + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityHigh), "InFrameBegin() failed."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4), "InFrameFeedData() failed."); + SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed."); + SuccessOrQuit(ncpBuffer.OutFrameRemove(), "OutFrameRemove() failed."); + // Repeat the test with a low priority buffer write + WriteTestFrame1(ncpBuffer, NcpFrameBuffer::kPriorityHigh); + WriteTestFrame2(ncpBuffer, NcpFrameBuffer::kPriorityLow); + SuccessOrQuit(ncpBuffer.InFrameBegin(NcpFrameBuffer::kPriorityLow), "InFrameBegin() failed."); + VerifyAndRemoveFrame1(ncpBuffer); + VerifyAndRemoveFrame2(ncpBuffer); + SuccessOrQuit(ncpBuffer.InFrameFeedData(buffer, sizeof(buffer) - 4), "InFrameFeedData() failed."); + SuccessOrQuit(ncpBuffer.InFrameEnd(), "InFrameEnd() failed."); + SuccessOrQuit(ncpBuffer.OutFrameRemove(), "OutFrameRemove() failed."); + printf(" -- PASS\n"); + testFreeInstance(sInstance); } @@ -536,11 +820,12 @@ enum kLensArraySize = 500, // Size of "Lengths" array. kMaxFrameLen = 400, // Maximum frame length kReadProbability = 50, // Probability (in percent) to randomly choose to read vs write frame + kHighPriorityProbablity = 20, // Probability (in percent) to write a high priority frame kUseTrueRandomNumberGenerator = 1, // To use true random number generator or not. }; -uint8_t sFrameBuffer[kFuzTestBufferSize]; -uint32_t sFrameBufferTailIndex = 0; +uint8_t sFrameBuffer[kNumPrios][kFuzTestBufferSize]; +uint32_t sFrameBufferTailIndex[kNumPrios] = {0}; uint32_t GetRandom(uint32_t max) { @@ -558,25 +843,26 @@ uint32_t GetRandom(uint32_t max) return value % max; } -otError WriteRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer) +otError WriteRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer, NcpFrameBuffer::Priority aPriority) { otError error; uint8_t byte; + uint8_t priority = static_cast(aPriority); CallbackContext oldContext = sContext; - uint32_t tail = sFrameBufferTailIndex; + uint32_t tail = sFrameBufferTailIndex[priority]; - SuccessOrExit(error = aNcpBuffer.InFrameBegin()); + SuccessOrExit(error = aNcpBuffer.InFrameBegin(aPriority)); while (aLength--) { byte = static_cast(GetRandom(256)); SuccessOrExit(error = aNcpBuffer.InFrameFeedData(&byte, sizeof(byte))); - sFrameBuffer[tail++] = byte; + sFrameBuffer[priority][tail++] = byte; } SuccessOrExit(error = aNcpBuffer.InFrameEnd()); - sFrameBufferTailIndex = tail; + sFrameBufferTailIndex[priority] = tail; // check the callbacks VerifyOrQuit(oldContext.mFrameAddedCount + 1 == sContext.mFrameAddedCount, "FrameAddedCallback failed."); @@ -586,7 +872,7 @@ exit: return error; } -otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer) +otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer, uint8_t priority) { CallbackContext oldContext = sContext; @@ -594,13 +880,13 @@ otError ReadRandomFrame(uint32_t aLength, NcpFrameBuffer &aNcpBuffer) VerifyOrQuit(aNcpBuffer.OutFrameGetLength() == aLength, "OutFrameGetLength() does not match"); // Read and verify that the content is same as sFrameBuffer values... - ReadAndVerifyContent(aNcpBuffer, sFrameBuffer, static_cast(aLength)); + ReadAndVerifyContent(aNcpBuffer, sFrameBuffer[priority], static_cast(aLength)); sExpectedRemovedTag = aNcpBuffer.OutFrameGetTag(); SuccessOrQuit(aNcpBuffer.OutFrameRemove(), "OutFrameRemove failed"); - sFrameBufferTailIndex -= aLength; - memmove(sFrameBuffer, sFrameBuffer + aLength, sFrameBufferTailIndex); + sFrameBufferTailIndex[priority] -= aLength; + memmove(sFrameBuffer[priority], sFrameBuffer[priority] + aLength, sFrameBufferTailIndex[priority]); // If successful check the callbacks VerifyOrQuit(oldContext.mFrameAddedCount == sContext.mFrameAddedCount, "FrameAddedCallback failed."); @@ -616,9 +902,9 @@ void TestFuzzNcpFrameBuffer(void) uint8_t buffer[kFuzTestBufferSize]; NcpFrameBuffer ncpBuffer(buffer, kFuzTestBufferSize); - uint32_t lensArray[kLensArraySize]; // Keeps track of length of written frames so far - uint32_t lensArrayStart; - uint32_t lensArrayCount; + uint32_t lensArray[kNumPrios][kLensArraySize]; // Keeps track of length of written frames so far + uint32_t lensArrayStart[kNumPrios]; + uint32_t lensArrayCount[kNumPrios]; sInstance = testInitInstance(); sMessagePool = &sInstance->mIp6.mMessagePool; @@ -626,8 +912,8 @@ void TestFuzzNcpFrameBuffer(void) memset(buffer, 0, sizeof(buffer)); memset(lensArray, 0, sizeof(lensArray)); - lensArrayStart = 0; - lensArrayCount = 0; + memset(lensArrayStart, 0, sizeof(lensArrayStart)); + memset(lensArrayCount, 0, sizeof(lensArrayCount)); sContext.mFrameAddedCount = 0; sContext.mFrameRemovedCount = 0; @@ -640,11 +926,11 @@ void TestFuzzNcpFrameBuffer(void) { bool shouldRead; - if (lensArrayCount == 0) + if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0) { shouldRead = false; } - else if (lensArrayCount == kLensArraySize - 1) + else if (lensArrayCount[0] == kLensArraySize - 1 || lensArrayCount[1] == kLensArraySize - 1) { shouldRead = true; } @@ -656,25 +942,40 @@ void TestFuzzNcpFrameBuffer(void) if (shouldRead) { - uint32_t len = lensArray[lensArrayStart]; + uint32_t len; + uint8_t priority; - lensArrayStart = (lensArrayStart + 1) % kLensArraySize; - lensArrayCount--; + priority = (lensArrayCount[NcpFrameBuffer::kPriorityHigh] != 0) ? + NcpFrameBuffer::kPriorityHigh : NcpFrameBuffer::kPriorityLow; - printf("R%d ", len); + len = lensArray[priority][lensArrayStart[priority]]; + lensArrayStart[priority] = (lensArrayStart[priority] + 1) % kLensArraySize; + lensArrayCount[priority]--; - SuccessOrQuit(ReadRandomFrame(len, ncpBuffer), "Failed to read random frame."); + printf("R%c%d ", priority == NcpFrameBuffer::kPriorityHigh ? 'H' : 'L', len); + + SuccessOrQuit(ReadRandomFrame(len, ncpBuffer, priority), "Failed to read random frame."); } else { uint32_t len = GetRandom(kMaxFrameLen) + 1; + NcpFrameBuffer::Priority priority; - if (WriteRandomFrame(len, ncpBuffer) == OT_ERROR_NONE) + if (GetRandom(100) < kHighPriorityProbablity) { - lensArray[(lensArrayStart + lensArrayCount) % kLensArraySize] = len; - lensArrayCount++; + priority = NcpFrameBuffer::kPriorityHigh; + } + else + { + priority = NcpFrameBuffer::kPriorityLow; + } - printf("W%d ", len); + if (WriteRandomFrame(len, ncpBuffer, priority) == OT_ERROR_NONE) + { + lensArray[priority][(lensArrayStart[priority] + lensArrayCount[priority]) % kLensArraySize] = len; + lensArrayCount[priority]++; + + printf("W%c%d ", priority == NcpFrameBuffer::kPriorityHigh ? 'H' : 'L', len); } else { @@ -682,7 +983,7 @@ void TestFuzzNcpFrameBuffer(void) } } - if (lensArrayCount == 0) + if (lensArrayCount[0] == 0 && lensArrayCount[1] == 0) { VerifyOrQuit(ncpBuffer.IsEmpty() == true, "IsEmpty failed."); printf("EMPTY "); diff --git a/tests/unit/test_util.h b/tests/unit/test_util.h index 753fb046b..36b17e621 100644 --- a/tests/unit/test_util.h +++ b/tests/unit/test_util.h @@ -43,25 +43,23 @@ extern "C" { // Enable main functions #define ENABLE_TEST_MAIN -#define SuccessOrQuit(ERR, MSG) \ - do { \ - if ((ERR) != OT_ERROR_NONE) \ - { \ - fprintf(stderr, "%s FAILED: ", __FUNCTION__); \ - fputs(MSG, stderr); \ - exit(-1); \ - } \ - } while (0) +#define SuccessOrQuit(ERR, MSG) \ + do { \ + if ((ERR) != OT_ERROR_NONE) \ + { \ + fprintf(stderr, "\nFAILED %s:%d - %s\n", __FUNCTION__, __LINE__, MSG); \ + exit(-1); \ + } \ + } while (false) -#define VerifyOrQuit(TST, MSG) \ - do { \ - if (!(TST)) \ - { \ - fprintf(stderr, "%s FAILED: ", __FUNCTION__); \ - fputs(MSG, stderr); \ - exit(-1); \ - } \ - } while (0) +#define VerifyOrQuit(TST, MSG) \ + do { \ + if (!(TST)) \ + { \ + fprintf(stderr, "\nFAILED %s:%d - %s\n", __FUNCTION__, __LINE__, MSG); \ + exit(-1); \ + } \ + } while (false) //#define CompileTimeAssert(COND, MSG) typedef char __C_ASSERT__[(COND)?1:-1]